Effective C++ 一些记录和思考

Iter 3 - 尽可能使用 const

一个反逻辑的 bitwise const

class Text { ... char& operator[](std::size_t pos) const { return text[pos]; } private: char *text; };

. 在 clang 3.8 上编译失败,编译器已经修复这个反逻辑的问题。const 成员函数只能返回 const char& 类型的变量,这就保证了对象不能被修改。

当存在 const 成员函数和 non-const 成员函数的时候,可先实现 const 成员函数,non-const 成员函数通过调用 const 函数来实现,具体做法为先将对象转换为const类型(static_cast<const T&>()),调用const成员函数,再去除const属性(const_cast<T&>())

// const const char& operator[](std::size_t pos) const { return text[pos]; } // non-const char& operator[](std::size_t pos) { return const_cast<char&>(static_cast<const char&>(*this)[pos]); }

. 这样做的好处是 const 成员函数保证了数据的不变,减少代码量,缺点是转换的性能缺失。

Item 4 - 确定对象使用前已先被初始化

勿混淆赋值和初始化

对象的成员变量的初始化动作发生在进入构造函数 {/* body /*} 之前,使用 member initialization list 初始化对象是一种比较好的做法

内置type(int char)默认为 0

初始化顺序为其声明次序

non-local static 编译器对不同编译单元的 non-local static 的编译顺序是随机的

解决的办法是不直接访问 non-local static 对象,而是使用一个函数包装在其内部声明为 local static 对象并且返回改对象引用

static 为声明在其作用域内的全局变量,比如在函数内,只要该函数栈没有被回收该变量便一直存在。在调用该函数时,该 local static 对象会在首次访问时被初始化

Item 5 6 - C++ 隐式实现和调用的函数

编译器在没有 默认构造函数,copy构造函数,赋值重载操作符 时会自动生成,且为 public

阻止调用 copy构造函数,赋值重载操作符函数

C++11 之前,实现一个基类然后继承

class UnCopyable { public: UnCopyable() {} ~UnCopyable() {} private: UnCopyable(const UnCopyable&); UnCopyable& operator=(const UnCopyable&); }; class Impl : private UnCopyable {};

C++11 使用 delete 关键字

class Impl { public: Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete; };

Item 7 - 为多态基类声明 virtual 析构函数

C++ 11 中子类的可以用 override 来覆盖基类的 virtual 函数(析构函数可不用)

由于virtual table 和virtual table pointer的存在,会使的class 的大小膨胀。

尽量不继承non-virtual 析构函数的类,C++11后可以用final关键字修饰类而禁止被继承

构造函数和析构函数执行方式相反,构造函数是从最顶层的基类开始执行

Item 8 - 别让异常逃离析构函数

Item 9 - 不在构造和析构函数内调用 virtual 函数

在多态基类中构造函数调用虚函数,子类对象构造时执行的基类构造函数调用的基类的对象,而非子类对象,析构函数相同,这样虚函数就变成普通的函数了

Item 10 - 令 operator= 返回一个 reference to *this

Item 11 - 在 operator= 中处理“自我赋值”

只看正确性,一般的做法是先保留一个副本,再进行赋值,最后删除副本,这样在赋值操作失败的时候,不会丢失原来的数据

Item 12 - 复制对象时勿忘其每一个部分

一个较容易忽略的地方,子类在copy构造的时候易忽略对基类对象变量进行copy,而这时默认调用了基类的default构造函数

不要在一个构造函数内调用另外一个构造函数,可行的做法是将共同的机能放进一个普通函数中,在两个构造函数内调用

Item 13 - 以对象管理资源 Resource Acquistion Is Initialization, RAII

auto_ptr 在标准中已经废除,用 unique_ptr 替代

shared_ptr 互相引用的问题,可以用 weak_ptr 解决

Item 14 - 在资源管理类中注意copying行为

Item 15 - 在资源管理类中提供对原始资源的访问

在只能指针中提供原始指针即可

Item 16 - 成对使用new和delete

尽量使用只能指针替代 new

数组可用 std::vector 或者 C++11 中的 std::array 代替

Item 17 - 以独立语句将 new对象置入指针指针

分离创建和使用的过程

Item 18 - 让接口易于使用,不易被误用

类的设计通常应与内置类型的逻辑保持一致

函数的有资源相关的操作时可以考虑使用智能指针来处理

Item 19 - 设计 class 如同 type

Item 20 - 宁以 pass-by-reference-to-const 替换 pass-by-value

涉及到底层的处理,编译器对待指针和自定义类型(class)的处理可能会不一样

对于内置类型、STL的迭代器和函数对象,pass-by-value 更合适

Item 21 - 必须返回对象时,勿返回reference

clang 3.8 可以发现这个问题,并发出警告
warning: reference to stack memory associated with local variable 'a' returned [-Wreturn-stack-address]

Item 22 - 将成员变量声明为 private(能够访问 private的函数只有 friend函数和成员函数)

将成员变量隐藏在函数接口后,可以为实现提供弹性

Item 23 - 宁以 non-member、non-friend 替换 member 函数

将 non-member 函数与当前 class 声明在同一个命名空间内

Item 24 - 若所有参数皆需类型转换,请采用 non-member 函数

只有当参数位于参数列内,这个参数才是隐式类型转换的合格者

Item 25 - 考虑写出一个不抛出异常的 swap 函数

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjxwp.html