1.在类内部,声明成员函数时必需 的,而定义成员函数则是可选的。在类内部定义的函数默认为inline。
2.const成员函数不能改变其所操作的对象的数据成员。const必须同时出现在声明和定义中,若只出现其中一处,就会出现一个编译时错误。
3.显示指定inline成员函数
在类内部定义的成员函数默认是inline。显示指定inline成员函数的办法有3种:
(1)类定义体内部指定inline,作为其声明的一部分。
(2)类定义体外部的函数定义上inline
(3)类定义体内和外均指定为inline
注意:inline成员函数的定义必须在调用该函数的每个源文件中时可见的。不在类定义体内定义的inline成员函数,其定义通常用放在有类定义的同一头文件中。
4.在一个给定的源文件中,一个类只能被定义一次。如果在多个文件中定义一个类,那么每个文件中的定义必须是完全相同的。
5.在声明之后、定义之前的类是一个不完全的类型。不完全类型只能以有限的方式使用。不能定义该类型的对象。不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型的作为形参类型或者返回类型的函数。
6.定义对象时,将为其分配存储空间,但(一般而言)定义类型时不进行存储分配。
7.在普通的非const成员函数中,this的类型是一个指向类类型的const指针,可以改变this所指向的值,但不能改变this所保存的地址。在const成员函数中,this的类型时一个指向const类类型对象的const指针。既不能改变this所指向的对象,也不能改变this所保存的地址。
8.不能从const成员函数返回指向类对象的普通引用。const成员函数只能返回*this作为一个const引用。
9.如果函数在类定义体之外定义,则用于返回类型的名字在类作用域之外。如果返回类型使用由类定义的类型,则必须使用完全限定名。
例如:
class Screen { public: typedefstd::string::size_type index; index get_cursor() const; }; inline Screen::index Screen::get_cursor() const { return cursor; }
该函数的返回类型时index,这是在Screen类内部定义的一个类型名。
10.当成员定义在类定义的外部时,要考虑在成员函数定义之前出现的全局作用域声明。
class Screen { public: // .... void setHeight(index); private: indexheight; }; Screen::index verify(Screen::index); void Screen::setHeight(index var) { height =verify(var); }
注意:全局函数verify的声明在Screen类定义之前是不可见的。然而名字查找可以考虑那些出现在成员函数定义之前的外围作用域声明,并找到全局函数verify的声明。
11.从概念上讲,可以认为构造函数分两个阶段执行:(1)初始化阶段;(2)普通的计算阶段。计算阶段由构造函数函数体中的语句组成。
不管成员是否在构造函数初始化列表中显示初始化,类类型的数据成员总是在初始化阶段初始化。初始化发生在计算阶段开始之前。
在构造函数初始化列表中没有显示提及的每个成员,使用与初始化变量相同的规则来进行初始化。运行该类型的默认构造函数,来初始化类类型的数据成员。内置或复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中他们被初始化为0。
例如:
Sales_item::Sales_item(const string &book) { isbn = book; units_sold = 0; revenue = 0.0; }
这个构造函数给类Sales_item的成员赋值,但没有进行显示初始化。不管是否有显示的初始化式,在执行构造函数之前,要初始化isbn成员。这个构造函数隐式使用默认的string构造函数初始化isbn。执行构造函数的函数体时,isbn成员已经有值了。该值被构造函数函数体中的赋值所覆盖。
12.有些成员必须在构造函数初始化列表中进行初始化。对于这样的成员,在构造函数函数体中对它们赋值不起作用。
(1)没有构造函数的类类型的成员。
(2)const 成员。
(3)引用类型的成员。
13.只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。
14.合成的默认构造函数使用与变量初始化相同的规则来初始化成员。具有类类型的成员通过运行各自的默认构造函数来进行初始化。内置和复合类型的成员,如指针和数组,只对定义在全局作用域中的对象才初始化。当对象定义在局部作用域中时,内置或复合类型的成员不进行初始化。
如果类包含内置或复合类型的成员,则该类不应该依赖于合成的默认构造函数。它应该定义自己的构造函数来初始化这些成员。
15.explicit关键字只能用于类内部的构造函数声明上。在类的定义体外部所做的定义上不再重复它。
通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为explicit。将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以显示的构造对象。
16.友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。友元的声明以关键字friend开始。它只能出现在类定义的内部。
17.友元可以是普通的非成员函数,或前面定义的其他类的成员函数,或整个类。将一个类设为友元,友元类的所有成员函数都可以访问授予友元关系的那个类的非公有成员。当我们将成员函数声明为友元时,函数名必须用该函数所属的类名字加以限定。
注意:必须先定义包含成员函数的类,才能将成员函数设为友元。
18.非static数据成员存在于类类型的每个对象中。不像普通的数据成员,static数据成员独立于该类的任意对象而存在;每个static数据成员是与类关联的对象,并不与该类的对象关联。static成员函数没有this形参,它可以直接访问所属类的static成员,但不能直接使用非static成员。
19.可以通过作用域操作符从类直接调用static成员,或者通过对象、引用或指向该类类型对象的指针间接调用。
20.当我们在类的外部定义static成员时,无须重复指定static保留字,该保留字只出现在类定义体内部的声明处。
21.因为static成员不是任何对象的组成部分,所以static成员函数不能被声明为const,毕竟,将成员函数声明为const就是承诺不会修改该函数所属的对象。static成员函数也不能声明为虚函数。
22.static数据成员必须在类定义体的外部定义。不像普通数据成员,static成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。(保证对象正好定义一次的最好办法,就是将static数据成员的定义放在包含类的非内联成员函数定义的文件中。
定义static数据成员的方式与定义其他类成员和变量的方式相同:先指定类型名,接着是成员的完全限定名。
doubleAccount::interestRate = initRate();
注意,尽管initRate是私有的,我们仍然可以使用该函数来初始化interestRate。像任意的其他成员定义一样,interestRate的定义是在类的作用域中,因此可以访问该类的私有成员。
像使用任意的类成员一样,在类定义体外部引用类的static成员时,必须指定成员是在哪个类中定义的。然而,static关键字只能用于类定义体内部的声明中,定义不能标示为static。
23.特殊的整型const static 成员
一般而言,类的static成员,像普通数据成员一样,不能在类的定义体中初始化。相反,static数据成员通常在定义时才初始化。这个规则的一个例外是,只要初始化是一个常量表达式,整型const static数据成员就可以在类的定义体中进行初始化:
classAccount { public: static double rate() { return interestRate; } static void rate(double); private: static const int period = 30; double daily_tbl[period]; };
注意:const static 数据成员在类的定义体中初始化时,该数据成员仍必须在类的定义体之外进行定义。在类内部提供初始化式时,成员的定义不必再指定初始值:
const int Account::period;
24.static成员不是类对象的组成部分
static成员独立于任何对象而存在,不是类类型对象的组成部分。
(1)static 数据成员的类型可以是该成员所属的类类型。非static成员被限定声明为其自身类对象的指针或引用:
classBar { public: //.... private: static Bar mem1; Bar* mem2; Bar mem3; };
(2)static数据成员可用作默认实参:
classScreen { public: Screen& clear(char = bkground); private: static const char bkground = '#'; };
非static数据成员不能用作默认实参,因为它的值不能独立于所属的对象而使用。使用非static数据成员作默认实参,将无法提供对象以获取该成员的值,因而是错误的。