effective C++
0.1.守则01:把C++看做一个语言的集合,而不是单一的语言
早期的C++只是叫"C with classes",但发展到今天已经成为一个多重泛型编程语言(Multi-paradigm programming language),它具有4种“子语言”:C 面向对象的C++ 模板C++ STL
0.2.守则02:尽量使用const, enum, inline, 减少宏变量#define的使用
④对于#define的宏函数,尽量使用inline修饰的函数来代替#define
#define 的使用确实用例出错,需要更费经历编写和改错误。编译出错后不能直接定位到出错位置。
但下面应用却最好用#define
#define PRINT_ERR(lpszFormat, ...) \
do\
{\
char szTmpBuf[Max_Log_Len + 1] = {0};\
snprintf(szTmpBuf, Max_Log_Len, lpszFormat, ##__VA_ARGS__);\
std::cerr << szTmpBuf<<std::endl;\
}while(0)
因为这样用之后编译器可以帮你做snprintf参数的检查,这个可以避免很多代码错误。
0.2.守则03: 尽可能使用const关键字
我的理解是提前规划好变量或者函数用途,是作为参照还是要进行记录。尽可能限制到最小权限,降低错误风险。
迭代器的const const_iterator
⑥在定义类的常量与非常量成员函数时,避免代码重复 二者可以复用一套代码。
0.3.守则07: 在多态的基类中,把析构函数声明为虚函数
某些类不是被用来当做基类的,比如std::string和STL,否则可能继承后导致析构函数不被调用,引发子类中的内存泄漏。
0.4.守则09: 不要在构造函数和析构函数中调用虚函数
在C++中,当子类开始构造时,它所包含的父类的部分要先完成构造,所以率先调用的构造函数是它的父类"Transaction"的构造函数。现在问题来了,因为父类的构造函数调用了一个纯虚函数,这就会导致即使你创建的是它的子类对象,这个虚函数也不会绑定到子类的版本上,而是使用的父类版本。
0.5.守则10: 赋值操作符要返回一个指向*this的引用
守则11: 赋值操作符要处理自赋值
0.6.什么是RAII?
RAII是Resource Acquisition Is Initialization(wiki上面翻译成 “资源获取就是初始化”)的简称,是C++语言的一种管理资源、避免泄漏的惯用法。利用的就是C++构造的对象最终会被销毁的原则。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。
0.7.守则19: 把类的设计看作类型的设计
0.8.守则20: 多用常量引用传递,少用值传递.
C++编译器会把引用作为指针来实现,所以引用传递本质上其实就是指针传递。因此,对于例如int的原始类型,直接用值传递还是比引用传递要高效,对于STL的迭代器和STL函数对象(function object / functor),即提供至少一个operator()的实现的某类对象,值传递同样也比引用传递高效。
多用常量引用传递,少用值传递。引用传递通常更高效,也能避免对象切割问题。
但是作为惯例,对于原始类型,STL迭代器,STL函数对象,值传递还是更加高效,这是仅有的例外。
1.不要对数组使用多态
数组空间连续,靠偏移访问,多态转换后因为其类型变化,类型所占空间变化。此时偏移访问的方式便不能正确去得数组各元素准确其实位置。
2.创建模板时,应避免无用的缺省构造函数。
通过仔细设计模板可以杜绝对缺省构造函数的需求。例如标准的vector模板(生成一个类似于可扩展数组的类)对它的类型参数没有必须有缺省构造函数的要求。很多模板类没有以仔细的态度去设计,没有缺省构造函数的类就不能与许多模板兼容。
无用缺省构造函数会在没有足够的数据时初始化一个对象,而此时该对象中许多属性处于无效状态。倘若我们不判断这些无效状态,则代码很有可能运行不正常;倘若判断无效状态,则会影响运行的效率,这种对象的使用者也要为此付出更多时间和代码。
而使用没有缺省构造函数的类的确有一些限制,但它给你提供了一种保证:这个类被正确地建立和高效地实现。
避免方法是直接分配空间,而后在该空间以如下方式调用拷贝构造函数和析构函数。这样子只提出了要求模板参数类有拷贝构造即可
::new(__p) _Tp(__val)
__p->~_Tp()
3.智能指针
template<class T> // 模板类,指向T的
class SmartPtr { // 灵巧指针
public:
SmartPtr(T* realPtr = 0);
T* operator->() const;
T& operator*() const;
template<class newType> // 模板成员函数
operator SmartPtr<newType>() // 为了实现隐式类型转换. 执行类似:
{
return SmartPtr<newType>(pointee);
}
...
};
实现模板类的不同类型实例间的隐式转换。需要用到成员函数模板,即成员函数拥有一个未知类型模板参数。
SmartPtr<childclass> c;
SmartPtr<parentclass> p = c;