第7章 类
1. 引入const成员函数(C++ Primer P231⑵32)
C++类的常量对象是没法调用非const成员函数的,如果想让常量对象调用某个成员函数,必须声明成const:
2. 1个类的尾后const成员函数如果返回*this,那末其返回类型必定是const 类名 & 前面这个const是不能少的.否则没法通过编译.
3. 默许构造函数P235⑵36
编译器只有在发现类中不包括任何构造函数的情况下,才会为我们合成1个默许的构造函数,且该函数对类中的成员履行默许初始化(如果类成员具有类内初始值就不履行默许初始化了).
对内置类型成员,局部对象默许初始化赋予随机值,全局对象默许初始化赋予0.
对类类型成员,默许初始化调用该类的默许构造函数.
编译器有可能不能给没有构造函数的类合成1个的.假设A类没有默许构造函数,但是A类中包括1个B类的对象成员b,且B类没有默许构造函数,此时编译器将没法用合成的构造函数初始化A类对象中的成员b,所以此时A类将不存在合成的默许构造函数:
如果1个类的构造函数只给部份成员赋予了初值,那末剩下的成员将取得1个默许值(如果有类内初始值,则直接初始化,否则履行默许初始化).
4. 拷贝与赋值的区分 P239
对象在几种情况下会被拷贝:初始化变量,以值的方式传递或返回1个对象.
我们使用了赋值运算符时,会产生对象的赋值操作.
5. 友元P241
类A定义了两个友元,分别是函数print() 和类B,所以在函数print()内部或在类B的内部可以访问类A的私有成员x.
注意:如果你想直接cout<<b.a.x<<endl;的话 还是错的,由于x是私有的,就算类A是B的友元,你也不能直接访问a的私有成员. 友元的含义不过是指在类或函数的内部你可以访问其他类的私有成员.
友元再探:
假定有A类和B类,现在想在B类中把A类的1个函数print()定义成友元,应当怎样定义前后顺序?
应遵守上面的顺序:先声明B类,然后再定义A类,但是A类的print函数只能声明,不能定义.
可以这样理解:
A类定义先于B类定义(但此时print()仅声明不定义): 既然B类要把1个A类的成员函数print()作为友元,那末明显A类的定义要先于B类,由于这样你才能在定义B类的时候能援用A类的print()成员函数.
B类声明先于A类定义: B类其次由于print()成员函数有B类的形参,所以你需要在定义A类之前,把class B声明1下,才行.
B类定义先于print()函数定义: 由于print()函数的定义中用到了B类的成员,所以在你定义print()函数时,B类必须已完全定义了.
友元函数声明:
假定类A用friend声明了友元函数print(). 此时print()可以先不声明,但是任何类A的代码也不可以调用print(). 只有等print()函数在全局声明以后,类A才能调用print()函数使用. 也就是说print()函数什么时候能被使用,只与它是不是被正常声明有关.
6. 可变数据成员(mutable关键字)P245 就算是const对象,mutable成员也能够被改变.
a是1个常量对象, a只能调用const成员函数.
7. 假定class A有1个set方法和1个print()方法,我们甚么情况下能以下使用: a.set(10).set(100).print(); 当set()返回 *this的援用时!
由于点运算符是左结合的,所以左侧先计算. 此时set返回*this的援用,所以可以继续调用其他成员函数.
注意:如果set()函数是const的,那末它返回的*this 就是1个const对象,那末a.set()就不能调用非const的print()函数. 关于这点可见P247⑵48页.
8. 不完全类型:1个类只有声明,但是却还没有定义.P250
可以定义指向该类型的指针或援用,也能够声明把以不完全类型为参数或返回值的函数. 但是不可以定义不完全类型的对象.所以:
1个类可以包括指向它本身类型的指针或援用
但是不能包括本身类型的对象.
9. 类型名作用域:P255
类中定义的类型名可以覆盖类外定义的类型名,条件是当前定义语句之前没有使用过类外的类型名:
由于定义val时,已用了money定义,所以接着定义新money类型是错的. 如果money新类型放在第4行的话,那末就是正确的.
10. 某些类的构造函数必须初始化: const成员, 援用, 或某些未提供默许构造函数的类类型.由于如果不初始化,那末这些成员就没成心义.
上面的类A构造函数没有对v2和v3履行初始化,所以是错的.A的构造函数应当对v2和v3履行初始值列表初始化.而不应当在构造函数中直接赋值(赋值不是初始化).
11. 构造函数中成员的初始化顺序是依照它们在类中定义的前后顺序的,而不是依照初始值列表中的成员顺序:
v1先初始化,所以v1(v2)后,v1将是1个随机值.v2后初始化,所以v2将是500.
12. 拜托构造函数用法(P261,经测试 非C++11编译器也能用)
注意:假定构造函数1拜托了构造函数2构造对象,那末构造函数1就算定义在构造函数2之前,1函数照样能调用2函数,
13. 类对象被默许初始化或值初始化时自动履行默许构造函数,所以尽可能给每一个类都写1个默许构造函数,否则容易出现毛病.
14. 转换构造函数->隐式的类类型转换(P295)
如果构造函数只接受1个参数,那末它实际上定义了转换为此类类型的隐式转换机制. 即在需要该类对象的地方,我们可使用那个参数替换.
上面A类中,分别用100和cin替换了A类的对象.
只允许1步类类型转换! 但是内置类型的转换支持多步.即上面的语句可以写成 a1=a1+100.3; 先将100.3转成int(内置转换),然后将int转成类A对象(类类型转换).
上面代码想将100转成B的对象,然再转成A的对象将出错.
抑制转换构造函数的隐式转换:explicit关键字
用了explicit关键字的单参数构造函数不会自动履行了(explicit对多参数构造函数无任何影响).且初始化的时候,explicit的构造函数只能直接初始化,不能赋值初始化了.
explicit关键字只能出现在类内部构造函数声明处.
15. 类的静态成员变量与静态成员函数
类的静态成员变量必须在类的外部定义和初始化1次,否则没法正确使用.