一、static成员变量和static成员函数
1.普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享
2.普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。
3.因此静态成员不需要通过对象就能访问
4.静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。静态成员函数本质上是全局函数。
5.设置静态成员这种机制的目的是将和某些类紧密相关的全局变量和函数写到类里面,看上去像一个整体,易于维护和理解。
注意:
1.必须在定义类的文件中对静态成员变量进行一次说明或者初始化,否则编译能通过,但链接不能通过。
2.在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。
二、this指针和静态成员函数
1.静态成员函数中不能使用this指针
因为静态成员函数并不具体作用于某个对象。因此,静态成员函数的真实的参数的个数,就是程序中写出的参数个数。
三、关于const的全部用法
1.常量对象
如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加const关键字。
2.常量成员函数
在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数。
常量成员函数执行期间不应修改其所作用的对象。因此在常量成员函数中不能修改成员变量的值(静态成员变量除外),也不能调用同类的非常量成员函数(静态成员函数除外)。
注意:
两个成员函数,名字和参数表都一样,但是一个是const,一个不是,算重载。
3.常引用
引用前面可以加const关键字,成为常引用。不能通过常引用,修改其引用的变量。
四、非引用的函数返回值不可以作为左值使用。
五、public继承的赋值兼容规则
1.派生类的对象可以赋值给基类对象。
2.派生类的对象可以初始化基类的引用。
3.派生类的对象的地址可以赋值给基类指针。
六、C++如下运算符不能重载:
“.” ".*" "::" "?:" sizeof
七、函数模板调用顺序:
1.先找参数完全匹配的普通函数(非自由模板实例化而得的函数)
2.再找参数完全匹配的的模板函数
3.再找实参经过自动类型转换后能够匹配的普通函数
4.上面的都找不到,则报错
备注:
赋值兼容规则引起函数模板中类型参数的二义性
1 template <class T> 2 3 T func(T a, T b) { 4 5 cout << "a = " << a << endl; 6 7 return a; 8 9 } 10 11 func(5, 7); // ok: replace T with int 12 13 func(5.8, 8.4); // ok: replace T with double 14 15 func(5, 8.4); // error: replace T with int or double ??
针对上面问题的解决方案如下:
可以在函数模板中使用多个类型参数,可以避免二义性
1 template <class T1, class T2> 2 3 T1 func(T1 a, T2 b) { 4 5 cout << "a = " << a << endl; 6 7 return a; 8 9 } 10 11 func(5, 7); // ok: replace T1 and T2 with int 12 13 func(5.8, 8.4); // ok: replace T1 and T2 with double 14 15 func(5, 8.4); // ok: replace T1 with int, T2 with double
八、构造函数
1.复制构造函数(copy constructor)
1)复制构造函数只有一个参数,即对同类对象的引用。
2)形如 X::X( X & )或者X::X( const X & ),二选一,后者能以常量对象作为参数
3)如果没有定义复制构造函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能。
备注:如果定义了自己的复制构造函数,则默认的复制构造函数不再存在。
不允许有形如X::X ( X )的构造函数
class CSample{ CSample( CSample cs) { } // 错,不允许这样的构造函数 };
2.复制(拷贝)构造函数起作用的三种情况
1) 当用一个对象去初始化同类的另一个对象时。
1 Complex c2(c1); 2 Complex c2 = c1; // 初始化语句,非赋值语句
2)如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用。
1 class A { 2 public: 3 A(){}; 4 A( A &a) { 5 cout << "Copy constructor called" << endl; 6 } 7 }; 8 9 void Func(A a1) { } 10 11 int main(void) 12 { 13 A a2; 14 Func(a2); 15 return 0; 16 }
程序输出结果:
Copy constructor called
3)如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数将被调用:
1 class A { 2 public: 3 int v; 4 A(int n) { v = n; } 5 A( const A &a ) { 6 v = a.v; 7 cout << "Copy constructor called" << endl; 8 } 9 }; 10 11 A Func() { 12 A b(4); 13 return b; 14 } 15 16 int main(void) { 17 cout << Func().v << endl; 18 return 0; 19 } 20 21 // 输出结果: 22 Copy constructor called 23 4
3.类型转换构造函数
类型转换构造函数只有一个参数,它不同于复制构造函数。
编译系统会自动调用转换构造函数,建立一个临时对象/临时变量
1 class Complex{ 2 public: 3 double real, imag; 4 5 Complex(int i) { // 类型转换构造函数 6 cout << "IntConstructor called" << endl; 7 real = i; 8 imag = 0; 9 } 10 Complex(double r, double i) { 11 real = r; 12 imag = i; 13 } 14 }; 15 16 int main(void) { 17 18 Complex c1(7, 8); 19 Complex c2 = 12; 20 c1 = 9; 21 cout << c1.real << "," << c1.imag << endl; 22 return 0; 23 } 24 25 // 输出: 26 IntConstructor called 27 IntConstructor called 28 9,0
Complex(int)这个构造函数就是类型转换构造函数。可以看出,该构造函数一共被调用了两次。第一次来自于对c2的初始化,第二次来自于第20行的赋值语句。
这条赋值语句的等号两边类型是不匹配的,之所以不会报错,就是因为Complex(int)这个类型转换构造函数能够接受一个整型参数。因此,编译器在处理这条赋值语句的时候,
会在等号右边自动生成一个临时的Complex对象,该临时对象以9为实参,用Complex(int)这个构造函数初始化,然后再将这个临时对象的值赋给c1,也可以说是9被自动转换成一个
Complex对象然后再赋值给c1。要注意,第19行是初始化语句,而不是赋值语句。所以,不会将12转换成一个临时对象,而是直接以12作为参数调用Complex(int)构造函数来初始化c2。
九、私有成员的访问权限:
类的成员函数内部,可以访问:
1)当前对象的全部属性,函数
2)同类其他对象的全部属性,函数
类的成员函数以外的地方:
1)只能够访问该类对象的公有成员
十、静态成员变量static
普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享。sizeof运算符不会计算静态成员变量。
普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。因此,静态成员不需要通过对象就能访问。
静态成员的访问:
1) 类名::成员名
CRectangle::PrintTotal();
2) 对象名.成员名
CRectangle r; r.PrintTotal();
3) 指针->成员名
CRectangle *p = &r; p->PrintTotal();
4) 引用.成员名
CRectangle &ref = r; int n = ref.nTotalNumber;
静态成员变量,本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。
静态成员函数本质上是全局函数。
设置静态成员这种机制的目的是将和某些类紧密相关的全局变量和函数写到类里面,看上去像一个整体,易于维护和理解。
1 class CRectangle 2 { 3 private: 4 int w, h; 5 static int nTotalArea; 6 static int nTotalNumber; 7 public: 8 CRectangle(int w_,int h_); 9 ~CRectangle(); 10 static void PrintTotal(); 11 }; 12 13 14 CRectangle::CRectangle(int w_,int h_) { 15 w = w_; 16 h = h_; 17 nTotalNumber ++; 18 nTotalArea += w * h; 19 } 20 21 CRectangle::~CRectangle() { 22 nTotalNumber --; 23 nTotalArea -= w * h; 24 } 25 26 void CRectangle::PrintTotal() { 27 cout << nTotalNumber << "," << nTotalArea << endl; 28 } 29 30 31 int CRectangle::nTotalNumber = 0; 32 int CRectangle::nTotalArea = 0; 33 // 必须在定义类的文件中对静态成员变量进行一次说明 34 //或初始化。否则编译能通过,链接不能通过。 35 int main() 36 { 37 CRectangle r1(3,3), r2(2,2); 38 //cout << CRectangle::nTotalNumber; // Wrong , 私有 39 CRectangle::PrintTotal(); 40 r1.PrintTotal(); 41 return 0; 42 } 43 44 // 输出: 45 2,13 46 2,13
注意事项:
在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。
1 void CRectangle::PrintTotal() 2 { 3 cout << w << "," << nTotalNumber << "," << nTotalArea << endl; //wrong 4 } 5 6 CRetangle::PrintTotal(); //解释不通,w 到底是属于那个对象的?
在CRectangle类的写法中,有何缺陷?
1 CRectangle::CRectangle(int w_,int h_) { 2 w = w_; 3 h = h_; 4 nTotalNumber ++; 5 nTotalArea += w * h; 6 } 7 8 CRectangle::~CRectangle() { 9 nTotalNumber --; 10 nTotalArea -= w * h; 11 } 12 13 void CRectangle::PrintTotal() { 14 cout << nTotalNumber << "," << nTotalArea << endl; 15 }
在使用CRectangle类时,有时会调用复制构造函数 生成临时的隐藏的CRectangle对象
调用一个以CRectangle类对象作为参数的函数时,
调用一个以CRectangle类对象作为返回值的函数时
临时对象在消亡时会调用析构函数,减少nTotalNumber 和 nTotalArea的值,可是这些临时对象在生成时却没有增加 nTotalNumber 和 nTotalArea的值。
解决办法:为CRectangle类写一个复制构造函数。
1 CRectangle :: CRectangle(CRectangle & r ) { 2 w = r.w; 3 h = r.h; 4 nTotalNumber ++; 5 nTotalArea += w * h; 6 }
参考:
www.coursera.org上的pku公开课C++程序设计
《新标准C++程序设计教程》,郭炜