Effective C++ 55 Ways 读书笔记
第一章: 适应c++
1. 相比较#define, 更倾向于用const, enum, inline。
#define 在预处理阶段处理,在编译后的文件中找不到任何的symbol, 不利于debug. 而const, enum 则有。
相对于#define, inline 函数既也能减少函数调用的开销,也能做更多的类型检查,不易出错。
2. 尽可能多的使用const.
原因1:
X operator*(const X& x1, const X& x2); X a, b, c; if ((a*b)=c) // 合法,但通常不是我们想要的 const X operator*(const X& x1, const X& x2); X a, b, c if ((a*b)=c) // 不合法, 正是我们想要的
原因2:编译器能对reference-to-const 做更多的优化, 而const 对象只能调用const 成员方法。 所以某个成员方法如果是const 的, 就标记他为const 的。
3 确保在使用某个对象时,这个对象已经被初始化了。
对于global 的指针变量,可能的实现方式是:
X* px; void f() { px->f(); }
更好的实现方式是:
X& getX(){ static X x; // init here, only once return x; } void f() { x.f(); }
第二章:类的构造函数,析构函数,赋值函数
构造函数
4. 编译器会产生默认的构造函数,所以如果不希望类被拷贝构造或者赋值构造,就明确的禁用掉。
class X { private: X(const X& x); X& operator=(const X& x); };
5. 不要在构造函数中调用virtual 成员函数。
class Transaction { public: Transaction() { logTransaction(); } virtual logTransaction() {} }; class ATransaction: public Transaction {};
// 这里的A 调用的还是Transaction 的logTransaction() 函数。 因为A 的初始化是先从基类开始的。
解决方法:
class Transaction { public: Transaction(const std::string& loginfo) { logTransaction(loginfo);} logTransaction(const std::string loginfo) {} // 不再是virtual 函数 }; class ATransaction : public Transaction { public: ATransaction(params): Transaction(createLogString(params)); private: static std::string createLogString(params); // 这里设置成static, 也能确保createLogString 不能访问任何的成员变量。 };
类的析构函数
6. 多态编程,基类的析构函数都是virtual 的。 如果成员函数有virtual 的, 析构函数必须是virtual 的
class Base { public: ~Base(){} }; class D1 : public Base {}; class D2 : public Base {}; Base* pb = getBase(); // 可能是D1, 也可能是D2 delete pb; // 糟糕, 调用的Base::~Base();
正确的写法是:
class Base { public: virtual ~Base(){} };
7. 基类的析构函数不一定必须是virtual 的。 如果不必须,又设置成virtual, 反而会有损失。
class Point { int x; int y; };
本身这个类只有64bit, 可以完整的放到寄存器里面。 如果设置了virtual 的析构函数,因为多类vtable, 反而不能放在寄存器了。
8 将析构函数可能抛出exception的代码通过public 成员函数暴露给用户.
class X { ~X { f(); // 可能抛出异常 }; }; void f() { vector<X> vx; // vx 里面的成员会被逐个析构 ...; };
如果某个X::~X 抛出异常, 后面的类的析构函数就不能轻易调用。
可能的解决方式:
class X { ~X() { try { f(); } catch .. { // log // std::abort(); } } };
但这里的的问题是无论是否abort, 用户都不知道有这个异常。 更好的做法是:
class X { public: void release() { close(); closed = true; } ~X { if(!closed) { try { close(); } catch .. { // log here } } } private: bool closed; };
这样用户就有机会去catch closed 的exception
{ X x; try { x.close(); } except .. { } }
赋值构造函数
9 让赋值构造函数返回reference *this
X& operator(const X& x) { //...; return *this;} x1 = x2 = x3 = X(params); // 合法的。
10 赋值构造函数要确保传入的reference 不是reference 的自己。
有bug 的版本
class X { public: X& operator=(const X& x) { delete p; // 如果this 和 x 是同一个对象, delete p 时也delete 了x.p this.p = x.p; return *this; } P* p; };
解决方法:
X& X::operator=(const X& x) { if(this == &x) return *this; delete p; p = x.p; return *this; };
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。