Effective C++ 条款总结
程序员文章站
2022-07-12 17:52:04
...
Effective C++ 条款总结
自己在看这本书的时候,回去翻看目录的时候,有些规则会被遗忘,因此做个简单的小总结供自己和其他人参考,没读过的还是要先去读一遍的
一、让自己习惯C++
1 视C++为一个语言联邦
C++是一种包含许多特性的语言,因而不要把它视为一个单一语言。理解C++至少需要学习一下4个部分:
- C语言。C++仍以C为基础
- objected-oriented C++。面向对象编程,类、封装、继承、多态
- template C++。C++泛型编程、模板元编程的基础
- STL。容器、迭代器、算法
2 尽量使用const等替换#define
- 对于单纯常量,最好以const或者enums替换#define
- 对于形似函数的宏(macros),最好改用inline函数替换
3 尽可能使用const
- const可以作用于:变量、指针、函数参数类型、类中的常函数。const可以防止变量被意外的修改,有助于编译器检测这些意外改变。
- 当non-const和const实现相同逻辑时,non-const对象可以调用const成员函数,这样可以缩减代码量。另外注意const对象不能调用non-const成员函数,编译报错:discards qualifiers。
- const类型 迭代器,指针不能变;const_iterator类型,指针可以变,所指向的值不变:
const vector<int>::iterator it;
it++;错误
*it=10;正确
vector<int>::const_iterator it;
it++;正确
*it=10;错误
4 确定对象使用前被初始化
在构造函数的初始化列表中的才算是初始化,而构造函数的内容是在初始化列表之后执行的,已经不算是初始化操作。这里就存在效率问题。
假设你的类成员是个其他类的对象,比如std::string name,你在初始化列表中进行初始化,调用的是string的拷贝构造函数,而在构造函数中进行赋值的话,调用的是:默认构造函数+赋值函数,调用默认构造的原因是,调用构造函数之前会先对成员进行初始化(这也就是为什么在构造函数中进行的操作不能称之为初始化操作),而对于大多数类,默认构造函数+赋值函数的效率是小于只调用拷贝构造函数的。
- 内置类型要手动初始化,不同的平台不能保证对内置类型进行初始化
- 因此最好是在初始化列表中进行初始化操作。
- 为避免“夸编译单元初始化次序”,以local static对象替换non-local static对象。
二、构造/析构/赋值运算
5 C++默认编写并调用的函数
- 编译器默认实现的函数:默认构造、析构、拷贝构造、赋值函数。这里注意深拷贝和浅拷贝问题。①对类内引用型数据对象进行赋值操作,必须定义拷贝赋值函数;②某个基类将拷贝赋值函数声明为private,则编译器对派生类不能生成该函数。
6 不想使用默认生成的函数,可以明确拒绝
- 默认的构造可以被其他构造替换,拷贝构造和赋值函数如果不想被外面调用可以将其声明为private。(member函数与friend函数仍然可以调用private函数,但是会获得连接错误)
- 不想被复制,也可以设计基类,其中基类的复制构造函数和赋值函数均为private类型,则派生类的对象,可以避免被复制。(但可能会导致多重继承)
7 多态基类声明virtual析构函数
- 当一个父类指针指向子类对象时(即多态的用法),在释放对象时,如果父类的析构函数不是virtual的,那么编译器会将这个指针视为父类类型的,只会释放掉这个对象的一部分空间。如果声明为virtual的,那么在释放的时候,编译器就知道这是一个子类类型,会将对象都释放掉,即防止内存泄漏问题。
- 此处涉及虚表指针和虚函数表
- 当类内含有至少一个virtual函数时,声明为virtual析构函数
- 纯虚析构函数,必须进行一份定义。子类继承父类,析构函数的运行方式是,最深处的子类的析构被调用,然后是每个基类的析构函数被调用
- 并非所有基类的设计都是为了多态,如6中,设计带有private的基类为了避免子类被复制。
8 别让异常逃离析构函数
- 绝不要让析构函数抛出异常,应该让用户自己处理可能发生异常的操作
解决办法①:调用absort()
解决办法②:析构中吞下因调用函数而发生的异常(try…catch)
或者说,重新设计函接口,对可能出现的问题做出反应。
9 不要在构造和析构函数调用virtual函数
10 令operator=返回reference to *this 并 11处理自我赋值
①这么写,没毛病,返回引用比临时变量要少几次构造析构,效率高
②第二条是因为,赋值的时候要先释放自己的资源然后赋予新的资源(资源假设为一个指针,这样便于理解),如果你自己给自己赋值,按照这个先释放再赋值的逻辑,自己直接就没了。
- 所以发现是自己赋值自己的时候(this = &object)直接返回*this即可。
Widget& operator=(const Widget& rhs)
{
...
return* this;
}
11 在operator=中处理“自我赋值”
- 确保当对象自我赋值时,operator=的行为良好,技术包括“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap
- 确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为依然正确。
12 复制对象时勿忘其每一个成分
9/12、深浅拷贝
下一篇: MySQL必知必会