条款21:必须返回对象时,别妄想返回其reference
可能会犯的错误:传递reference,指向其实并不存在的对象
任何时候看到一个reference,都要问一问,它的本源在哪里?它指向的对象(或内置类型)在哪里?
不要返回局部对象的reference或指针!
static对象,注意多线程
不要返回局部对象的reference或pointer,也不要返回堆区new出来的对象,因为很可能会造成内存泄露
条款22:将成员变量声明为private
将成员变量隐藏在函数接口背后,可以为“所有可能的实现”提供弹性。
封装的重要性
不封装意味着不可改变
protected不比public更有封装性
条款23:宁以非member、非friend函数替换成员函数
封装性越大,在修改的时候,影响越小
可以增加弹性、可扩展性
条款24:若所有参数都需类型转换,采用非成员函数
如果不能成为class的成员函数,应优先考虑是否是非成员函数,而不是friend函数
条款25:考虑写出一个不抛出异常的swap函数
没理解
条款26:尽量延后变量定义式的出现时间
有时候定义了变量,却未真正使用它,因此变量的构造和析构被白白执行了
不止延后变量的定义,尽量延后到能给它赋初值为止
对于循环,如果对象只在循环内部用,是定义在循环外呢,还是定义于循环内?
Widget w; For (int I = 0; I < n; ++i) { W = } | For (int I = 0; I < n; ++i) { Widget w; } |
一次构造,n次拷贝赋值,1次析构 n次构造,n次析构
一般而言如果(赋值成本<构造+析构),A做法效率高,但是由于w的作用范围更大,有时候对程序的理解和以维护性造成冲击。
除非:赋值成本<构造+析构,并且对效率极其敏感,否则还是采用B做法,即在循环内部定义对象,然后析构
条款27:少做类型转换
c风格转型语法:
(T)exp T(exp)
c++提供的4种新:
static_cast<T>:强迫隐式转换。将非const转换为const;将int转换为double;将void*转换为具体type的指针,将指向父类的指针转换为指向子类的指针。但是无法完成从const转换为非const!!!
const_cast<T>:消除对象的常量性质(使const的对象变为非const对象)
dynamic_cast<T>:执行安全的向下转型(从父类的指针类型转型为子类(真正子类)),运算成本大
reinterpret_cast<T>:依赖于编译器,不可移植。底层转换,例如将(Int*)转换为int
新式转换在代码中容易被识别
转换的目标越窄化,编译器越容易识别出错误的运用
尽量使用新式转换的语法,而避免使用C的或隐式转换
对象的布局方式和地址计算方式随编译器不同而不同。对于“知道对象布局”而设计的转型在一个编译器上行得通,在另一个上就可能不行!
如果在virtual子类中要调用父类的函数:
Class SpecialWindow:public Window{ Public: Virtual void OnResize(){ Static_cast<Window>(*this).onResize(); }将转型的副本的this指针传入 | Class SpecialWindow:public Window{ Public: Virtual void OnResize(){ Window::onResize(); } 将子类的this指针传入 |
dynamic_cast:通常认定一个子类对象,但是只有指向父类的指针或引用。
避免使用dynamic_cast:
1:使用容器,将具体类型放入,而不是使用父类指针
2:使用virtual,使函数在不同子类之间实现不同的功能
绝对必须避免一连串的dynamic_cast
if(dynamic_cast<A1*>(p))
{} else if(dynamic_cast<A2*>(p))
{} else {}
这样又大又慢,不稳定,每次继承体系一改变,就需要重写
1:尽量避免转型,特别是dynamic_cast
2:如果不可避免,试着将它隐藏在某个函数背后
3:尽量使用c++的新式转型