C++讲解---什么是静态成员函数
静态成员函数与静态数据成员一样,不依赖于类的实例,不属于各个对象。我们可以通过之前的日期类的闰年判断功能来看一下具体的使用。
1. 日期类复习
日期类的成员
成员类型 | 成员名称 | 作用说明 |
---|---|---|
私有数据成员 | int y | 存储公历年份 |
私有数据成员 | int m | 存储月份(1-12) |
私有数据成员 | int d | 存储日数(1-31) |
构造函数 | Date() | 默认构造函数 |
构造函数 | Date(int, int, int) | 带参构造函数(支持默认参数) |
成员函数 | year() | 返回当前年份 |
成员函数 | month() | 返回当前月份 |
成员函数 | day() | 返回当前日数 |
成员函数 | preceding_day() | 返回前一天的日期对象 |
成员函数 | to_string() | 返回日期字符串表示 |
友元函数 | operator<< | 输出流重载(支持cout << date_obj 格式) |
友元函数 | operator>> | 输入流重载(支持cin >> date_obj 格式) |
日期类的讲解及其代码如下:
创建日期类
代码:
//头文件
#include <string>
#include <iostream>class Date {
private:int y; // 公历年int m; // 月int d; // 日public:Date();Date(int yy, int mm = 1, int dd = 1);int year() const { return y; }int month() const { return m; }int day() const { return d; }Date preceding_day() const;std::string to_string() const;std::ostream& operator<<(std::ostream& s, const Date& x);std::istream& operator>>(std::istream& s, Date& x);
};
//源文件
#include <ctime> // 获取当前日期
#include <sstream>// 字符串流操作
#include <iostream> //输入输出操作
#include "Date.h"using namespace std;//--- Date 的默认构造函数(设置为当前日期)---//
Date::Date() {time_t current = time(NULL);struct tm* local = localtime(¤t);y = local->tm_year + 1900;m = local->tm_mon + 1;d = local->tm_mday;
}//--- Date 的构造函数(设置为指定的年、月、日)---//
Date::Date(int yy, int mm, int dd) {y = yy;m = mm;d = dd;
}//--- 返回前一天的日期(不支持闰年的处理)---//
Date Date::preceding_day() const {int dmax[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};Date temp = *this;if (temp.d > 1)temp.d--;else {if (--temp.m < 1) {if (temp.m == 0) {temp.y--;temp.m = 12;}temp.d = dmax[temp.m - 1];}}return temp;
}//--- 返回字符串表示 ---//
string Date::to_string() const {ostringstream s;s << y << "年" << m << "月" << d << "日";return s.str();
}
2. 完善功能后的日期类函数
完善功能后的日期类
成员类型 | 成员名称 | 作用说明 |
---|---|---|
私有数据成员 | int y | 存储公历年份 |
私有数据成员 | int m | 存储月份(1-12) |
私有数据成员 | int d | 存储日数(1-31) |
静态数据成员 | static int dmax[] | 存储平年各月的天数数组 |
静态成员函数 | days_of_month | 计算指定年份(y)和月份(m)的天数 |
静态成员函数 | is_leap(int year) | 判断指定年份是否为闰年(静态版本) |
构造函数 | Date() | 默认构造函数 |
构造函数 | Date(int, int, int) | 带参构造函数(支持默认参数) |
成员函数 | year() | 返回当前年份 |
成员函数 | month() | 返回当前月份 |
成员函数 | day() | 返回当前日数 |
成员函数 | is_leap() const | 判断当前年份是否为闰年(调用静态版本) |
成员函数 | preceding_day() | 返回前一天的日期对象 |
成员函数 | following_day() | 返回后一天的日期对象 |
成员函数 | day_of_year() | 返回当年内的累计天数 |
成员函数 | day_of_week() | 返回星期几(0=周日,1=周一…) |
成员函数 | to_string() | 返回日期字符串表示 |
友元函数 | operator<< | 输出流重载(支持cout << date_obj 格式) |
友元函数 | operator>> | 输入流重载(支持cin >> date_obj 格式) |
这里有两个注意的点:
1. days_of_month,is_leap(int year)是静态成员函数,这两个函数是纯粹的日期计算工具,不依赖或修改任何特定 Date对象的状态(y, m, d),仅通过参数进行运算。所以设计为静态成员函数更加灵活。
2. 这里有两个版本的is_leap()函数,一个为静态成员函数,一个是成员函数,只是调用了静态成员函数is_leap()。那么静态成员函数不需要依赖对象,只需要知道y值即可,而成员函数需要一个具体的对象。具体差别请参考使用示例
代码如下:
// 日期类 Date (第4版: 头文件)
#ifndef ___Class_Date
#define ___Class_Date
#include <string>
#include <iostream>class Date {int y; // 私有:公历年(1900-9999)int m; // 私有:月(1-12)int d; // 私有:日(1-31,根据月份和闰年变化)static int dmax[]; // 静态数组:存储各月最大天数(考虑闰年)static int days_of_month(int y, int m); // 静态方法:计算y年m月的天数public:// 构造函数Date(); // 默认构造(当前日期)Date(int yy, int mm = 1, int dd = 1); // 带参构造(年月日)// 静态方法:闰年判断static bool is_leap(int year) {return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;}// 访问器(const成员函数)int year() const { return y; } // 返回年int month() const { return m; } // 返回月 int day() const { return d; } // 返回日// 功能方法bool is_leap() const { return is_leap(y); } // 当前年份是否闰年Date preceding_day() const; // 返回前一天的日期Date following_day() const; // 返回后一天的日期int day_of_year() const; // 返回年内经过天数int day_of_week() const; // 返回星期(0=周日,1=周一...)std::string to_string() const; // 返回字符串表示(如"2023-12-25")
};// 流操作符重载(非成员函数)
std::ostream& operator<<(std::ostream& s, const Date& x); // 输出日期(格式:yyyy-mm-dd)
std::istream& operator>>(std::istream& s, Date& x); // 输入日期(格式:yyyy mm dd)#endif
// 日期类 Date(第4版:源文件)
#include <ctime>
#include <sstream>
#include <iostream>
#include "Date.h"
using namespace std;// 静态数据成员初始化(平年各月天数)
int Date::dmax[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};// 静态成员函数:计算指定年月天数(考虑闰年)
int Date::days_of_month(int y, int m) {return dmax[m - 1] + (is_leap(y) && m == 2); // 闰年2月加1天
}// 默认构造函数(设置为当前系统日期)
Date::Date() {time_t current = time(NULL); // 获取当前时间戳struct tm* local = localtime(¤t); // 转换为本地时间结构y = local->tm_year + 1900; // 年份转换(tm_year从1900起)m = local->tm_mon + 1; // 月份转换(0~11 → 1~12)d = local->tm_mday; // 直接获取日期(1~31)
}// 带参构造函数(年月日)
Date::Date(int yy, int mm, int dd) {y = yy;m = mm;d = dd;
}// 返回年内经过天数(从1月1日起)
int Date::day_of_year() const {int days = d; // 当前月已过天数for (int i = 1; i < m; i++) // 累加前m-1个月的天数days += days_of_month(y, i);return days;
}// 返回前一天的日期
Date Date::preceding_day() const {Date temp = *this; // 复制当前日期if (temp.d > 1) // 简单情况:非月初temp.d--;else if (--temp.m < 1) { // 跨年到上一年temp.y--;temp.m = 12;temp.d = 31; // 12月31日} else { // 跨月到上一月temp.d = days_of_month(temp.y, temp.m); // 上一月的最后一天}return temp;
}// 返回后一天的日期
Date Date::following_day() const {Date temp = *this; // 复制当前日期if (temp.d < days_of_month(temp.y, temp.m)) // 简单情况:非月末temp.d++;else if (++temp.m > 12) { // 跨年到下一年temp.y++;temp.m = 1;temp.d = 1; // 1月1日} else { // 跨月到下一月temp.d = 1; // 下一月的第一天}return temp;
}// 返回字符串表示(格式:yyyy年mm月dd日)
string Date::to_string() const {ostringstream s;s << y << "年" << m << "月" << d << "日";return s.str();
}// 计算星期(Zeller公式:0=周日,1=周一...6=周六)
int Date::day_of_week() const {int yy = y;int mm = m;if (mm == 1 || mm == 2) { // 1月2月当作前一年的13月14月yy--;mm += 12;}return (yy + yy/4 - yy/100 + yy/400 + (13*mm+8)/5 + d) % 7;
}// 输出流重载(使用to_string格式)
ostream& operator<<(ostream& s, const Date& x) {return s << x.to_string();
}// 输入流重载(格式:yyyy/mm/dd 或 yyyy-mm-dd)
istream& operator>>(istream& s, Date& x) {int yy, mm, dd;char ch; // 用于读取分隔符s >> yy >> ch >> mm >> ch >> dd; // 支持多种分隔符x = Date(yy, mm, dd); // 构造新日期对象return s;
}
// DateTest.cpp - 日期类Date(第4版)使用示例
#include <iostream>
#include "Date.h"
using namespace std;int main()
{Date today; // 创建今天日期对象(自动获取系统日期)cout << "今天的日期:" << today << '\n'; // 输出格式:yyyy年mm月dd日cout << "昨天的日期:" << today.preceding_day() << '\n';cout << "前天的日期:" << today.preceding_day().preceding_day() << '\n';cout << "明天的日期:" << today.following_day() << '\n';cout << "后天的日期:" << today.following_day().following_day() << '\n';cout << "从元旦开始经过了" << today.day_of_year() << "天。\n";cout << "今年" << (today.is_leap() ? "是闰年。" : "不是闰年。") << '\n';int y;cout << "公历年:"; cin >> y; // 用户输入任意年份cout << "该年" << (Date::is_leap(y) ? "是闰年。" : "不是闰年。") << '\n';
}