1.inline函数
当函数在类内部定义时,默认为inline,即可不使用inline关键字说明。
函数声明或者定义时,只要一处使用inline关键字说明,即为inline函数,也就是说没必要声明和定义时都使用inline关键字。
inline 函数(包括成员函数和非成员函数)的定义必须在调用该函数的每个源文件中是可见的。inline 成员函数如果不定义在类定义体内 ,则其定义通常应该和类定义放在同一头文件中。这点比较重要,感觉自己好像之前没有将inline成员函数的定义放在头文件里呀。。。
2.类的前向声明(forward declaration)
前向声明是声明一个类而不定义它。在声明之后,定义之前,类是一个不 完全类型(incomlete type),即已知类型,但不知道包含哪些成员。
不完全类型(incomplete type)只能以有限方式使用。虽然不能定义该类型的对象。但是可以定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。在创建类的对象之前,必须完整地定义该类,而不只是声明类,这样,编译器才会给类的对象预定相应的存储空间。同样地,在使用引用或指针访问类的成员之前,必须已经定义类。
类不能具有自身类型的数据成员,但是可以拥有一类特殊的数据成员,即指向自身类型的指针或引用,因为只要类名一出现就可以认为该类已声明。
3.类的定义为什么必须以分号结束
因为类定义之后可以接一个对象定义列表,定义必须以分号结束。例如:
class Sales_item { /* ... */ } accum, trans;
不过将类定义和对象定义组合不是一种好的编码风格。
4.const重载
同名的非成员函数,使用const和非const形参能形成 重载,但要求形参必须为指针或者引用,如:
void fun(const int a); 和 void fun(int a); //不能形成重载,并且编译报错。
void fun(const int& a); 和 void fun(int& a); //重载OK。调用时,const形参调用const函数,反之亦然。
void fun(const int* a); 和 void fun(int* a); //重载OK。调用时,const形参调用const函数,反之亦然。
同名的成员函数,const函数和非const函数也能形成重载,如:
void fun(int a); 和 void fun(int a) const; //重载OK。调用时,const对象调用const成员函数,非 const 对象可以使用任一成员,但非 const 版本是一个更好的匹配。
5.构造函数的初始化列表
从概念上讲,构造函数分两个阶段执行:(1)初始化阶段;(2)普通的计算阶段。计算阶段由构造函数函数体中的所有语句组成。
初始化阶段,会对每个数据成员进行初始化。如果数据成员出现在初始化列表中,则按列表中的方式进行初始化。否则,使用与初始化变量相同的方式进行初始化。这分为两种情况,内置或复合类型的数据成员和类类型的数据成员。前者初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为 0。后者运行该类型的默认构造函数。
在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为 0。这句话的意思见下例:
class A
{
public:
int a;
char b[10];
};
A obj;
int main()
{
A obj1;
cout << obj.a << endl; //OK
cout << obj.b[0] << endl; //OK
cout << obj1.a << endl; //ERROR
cout << obj1.b[0] << endl; //ERROR
}
类类型的数据成员,如果在初始化列表中没有指定初始化方式,则编译器会使用默认构造函数进行初始化。如果那个类没有默认构造函数则会报错。这种情况下,为了初始化数据成员,必须显示的在初始化列表中提供使用其它构造函数的方式。
const或者引用类型的数据成员,只能在初始化列表中进行初始化,不能在普通计算阶段初始化。
成员按照声明的次序进行初始化,如下例:
class X {
int i;
int j;
public:
// run-time error: i is initialized before j
X(int val): j(val), i(j) { }
};
在这种情况下,构造函数初始化列表看起来似乎是用val 初始化 j,然后再用 j 来初始化 i。然而,由于i 首先被初始化,当使用尚未初始化的 j 值来初始化 i时就有问题了!在VS2010下,编译不报错,运行后,j的值正常,但i为未初始化的值。