类
P200 struct
考虑用一个struct实现date的概念:
struct Date { int d, m, y; }; void init_date(Date& d, int, int, int); //初始化d void add_year(Date& d, int n); //d加n年 void add_month(Date& d, int n); //d加n月 void add_day(Date& d, int n); //d加n天
在日期类型与这些函数之间并没有任何显示的联系。可以通过将这些函数声明为成员,从而建立起这种联系:
struct Date { int d, m, y; void init(int dd, int mm, int yy); //初始化 void add_year(int n); //加n年 void add_month(int n); //加n月 void add_day(int n); //加n天 };
由于在不同的结构里,可能存在相同名字的成员函数,我们在定义成员函数时就必须给出有关结构的名字:
void Date::init(int dd, int mm, int yy) { d = dd; m = mm; y = yy; }
在成员函数里面,结构内部各个成员的名字都可以直接调用,不必显式地去引用某个对象。在这种情况下,这些名字所引用的就是该函数调用时所针对的那个对象的成员。
例如,如果today调用Date::init(), 那么m=mm将给today.m赋值。另一方面,如果为my_birthday调用Date::init(),那么m=mm就会给my_birthday.m赋值。
类的成员函数总知道它是为了哪个对象而调用的。
Date my_birthday; void f() { Date today; today.init(16, 10, 1996); my_birthday.init(30, 12, 1950); Date tomorrow = today; //tomorrow.add_day(1); }
P201
上述Date声明提供了一组操作Date的函数,然而,它并没有清楚地说明这些函数就是直接依赖于Date的表示的全部函数,而且也是仅有的能够直接访问Date类的对象的函数。(struct声明中没有私有成员的概念,非成员函数是禁止访问私有成员的)
class Date { int d, m, y; public: void init(int dd, int mm, int yy); //初始化 void add_year(int n); //加n年 void add_month(int n); //加n月 void add_day(int n); //加n天 }; inline void Date::add_year(int n) { //正确:类的成员函数 y += n; } void timewarp(Date& d) { //错误:不是类的成员函数,Date::y为私有成员 d.y -= 200; }
P202 class
init()函数提供对类对象的初始化,可以用构造函数代替:
class Date { int d, m, y; public: Date(int, int, int); //构造函数 void add_year(int n); //加n年 void add_month(int n); //加n月 void add_day(int n); //加n天 };
如果构造函数要求参数,那么必须提供这些参数:
Date today = Date(23, 6, 1983); //ok Date xmax(25, 12, 1990); //上句的简写形式 Date my_birthday; //错误:缺少初始式 Date release1_0(10, 12); //错误:缺少第3个参数
允许提供多个构造函数,服从与其他函数完全一样的重载规则。
另外,可以采用默认参数的方法:
class Date { int d, m, y; public: Date(int dd =0, int mm =0, int yy =0); //构造函数 void add_year(int n); //加n年 void add_month(int n); //加n月 void add_day(int n); //加n天 }; Date today; Date::Date(int dd, int mm, int yy) { d = dd ? dd : today.d; m = mm ? mm : today.m; y = yy ? yy : today.y; }
P203 静态成员
上述Date类依靠一个全局变量today。使这个类在其他环境基本上不能用,形成限制。
如果一个变量是类的一部份,但却不是该类的各个对象的一部分,它就被称为是一个static静态成员。
一个static成员只有唯一的一份副本,而不像常规的非static成员那样在每个对象里各有一份副本。
与此类似,一个需要访问类成员,却不需要针对特定对象去调用的函数,被称为一个static成员函数。
对静态成员(包括静态数据成员和静态函数成员)的引用不必提到任何对象,但需要给成员的名字加上作为限定词的类名字。。
静态成员,必须在某个地方另行定义。
class Date { int d, m, y; static Date default_date; public: Date(int dd =0, int mm =0, int yy =0); //构造函数 //... static void set_default(int, int, int); }; Date::Date(int dd, int mm, int yy) { d = dd ? dd : default_date.d; m = mm ? mm : default_date.m; y = yy ? yy : default_date.y; } Date Date::default_date(16, 12, 1770); void Date::set_default(int d, int m, int y) { Date::default_date = Date(d, m, y); } void f() { Date::set_default(4, 5, 1945); }
P205 常量成员函数
到目前位置,所定义的Date只提供了一些给定Date的值或者改变其值的函数。
接下来提供一些函数来检查一个Date的值。
class Date { int d, m, y; public: int day() const { return d; } int month() const { return m; } int year() const { return y; } //... };
注意:在这些函数声明的空参数表后面出现的const,它指明这些函数不会修改Date的状态。
inline int Date::year() const { //错误:在const函数里企图修改成员值 return y++; } inline int Date::year() const { //ok return y; } inline int Date::year() { //错误:函数不匹配Date中的任何一个,在成员函数类型中缺少const return y; }
对于const或者非const对象都可以调用const成员函数,而非const成员函数则只能对非const对象调用:
void f(Date& d, const Date& cd) { int i = d.year(); //ok d.add_year(1); //ok int j = cd.year(); //ok cd.add_year(1); //错误:不能修改const cd的值 }
P205 自引用
状态更新函数add_year()、add_month()和add_day、都被定义为不返回值的函数。对于这样的函数,可以让它们返回一个到被更新对象的引用,以使对于对象的操作可以串接起来。这种技术很有用。。。
表达式*this引用的就是这个函数的这次调用所针对的那个对象。。
class Date { int d, m, y; public: Date& add_day(int n); Date& add_month(int n); Date& add_year(int n); //... }; void f(Date& d) { d.add_day(1).add_month(1).add_year(1); } Date& Date::add_year(int n) { if(d==29 && m == 2 && !leapyear(y+n)) { d = 1; m = 3; } y += n; return *this; }