Effective C++ 笔记

自己习惯C++

1、C++为一个语言联邦

STL,Template,智能指针,C++11

2、尽量以const,enum,inline替换#define

1)预处理在符号表中不存在,出现bug不好定位;目标码中也不会有多份Pia

eg:

#define Pia 3.14 (instead of--->) const double Pia = 3.14

2)inline代替#define函数

eg:

#define MAX(a,b) f(

         if((a) > (b))

                   return (a)

         else

                   return (b)

         )

template<class T>

inline T Max(const T& a, const T& b)

{...}

3、尽量使用const

1)区别不多说(const出现在星号左边,表示被指物是常量;出现在右边,表示指针本身是常量;出现在两边,被指物和指针都是常量)

char str[] = "hello";

char* p = str;

const char* p = str;

char* const p = str;

const char* const p = str;

2)const std::vector<int>::iterator iter = vec.begin();//like T* const;指针不能变,被指物可以变

*iter = 10;//right

iter++;//error

遍历的时候,不能这样声明,可以使用下面这种

如果想声明被指物不可变,使用const_iterator

std::vector<int>::const_iterator citer = vec.begin();

*citer = 10;//error

citer++;//right

3)函数参数const,不让内部改变参数属性;

返回值const,一些可能的bug可以避免,如(a*b)=c的错误

4)const成员函数:允许const属性的重载

4、确认对象被使用前已经被初始化

构造,析构,赋值

5、了解C++默默编写并调用哪些函数

构造函数(T()),析构(~T()),拷贝构造(T(const T& rhs)),复制函数(T &operator=(const T& rhs))

6、若不想使用编译器自动生成的函数,就该明确拒绝

拷贝构造,复制函数声明为private且不定义

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

防止资源泄漏(保证子类析构函数被调用)

8、别让异常逃离析构函数

异常时终止或者吞下

将可能抛出异常的代码提供给用户管理

9、绝不在构造和析构过程中调用virtual函数

构造和析构期间不要调用virtual函数,因为这类调用不会下降到子类的虚函数

原因:

子类构造前,先构造父类;父类调用虚函数,只能调用父类自己的函数,因为子类还没有构造;

同理,子类析构,先析构自己,然后析构父类,当父类析构函数调用虚函数,不可能指向子类的虚函数,因为子类已经析构不存在了。

PS:

一个virtual函数,就有一个虚表;虚表是在编译期创建的。

在执行期,根据虚表,程序找到虚函数实现体,实现多态

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

eg:

template <class T>

T& operator=(const T& rhs)

{

         ...

         return *this;

}

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

eg1:

template <class T>

T& operator=(const T& rhs)

{

         if(this == &rhs)return *this;

         ...

         return *this;

}

eg2:

copy-and-swap技术

template <class T>

T& operator=(const T& rhs)

{

         if(this == &rhs)return *this;

         //copy-and-swap

         bitmap* pOrig = pb;

         pb = new bitmap(*rhs.pb);

         delete pOrig;

         //

         return *this;

}

12、复制对象时勿忘期每一个成分

如果是子类赋值函数或者是复制构造函数,除了复制子类所有成员变量;

需要适当调用父类的对应函数,父类不满足;父类也需要重新这些函数

eg见代码effective12

资源管理

13、以对象管理资源

构造函数获得资源,析构函数释放资源;

使用智能指针封装:tr1::shared_ptr和auto_ptr。

关于智能指针,见另外一片blog

14、在资源管理类中小心copying行为

15、在资源管理类中提够对原始资源的访问

原始资源获取;

显式转换安全

隐式转换——对客户方便,不安全

16、成对使用new和delete时采用相同形式

new, delete

new [], delete []

17、以独立语句将newed对象置入智能指针

eg:

std::tr1::shared_ptr<T> pw(new T);

processT(pw,test())

代替

processT(std::tr1::shared_ptr<T>(new T),test())

防止test()出现异常,new T之后没有调用tr1::shared_ptr出现资源泄漏

设计与声明

18、让接口容易被正确使用,不易被误用

类型一致性;

shared_ptr防范跨DLL错误。

19、设计class犹如设计type

20、宁以pass-by-refenrence-to-const替换pass-by-value

1)引用传递代替值传递(这个在Java,python中都已经默认使用。当然简单内置的类型还是value传递,其他都是引用传递了)

2)高效,没有拷贝构造函数,析构函数执行

3)STL使用pass-by-value比较适合,估计也是其中成员,而不是STL容器本身

21、必须返回对象时,别妄想返回其reference

1)绝对不要返回pointer或reference指向一个local stack对象,或者返回一个reference指向一个heap-allocated对象,

或者返回pointer或reference指向一个local static 对象

2)栈、堆、静态对象都不要作为引用返回

22、将成员变量声明为private

set***函数便于更改private变量,不易疏漏

23、宁以non-member、non-friend替代member函数

24、若所有参数且需类型转换,请为此采用non-member函数

operator*(+,-等等)有隐式转换的左值,只能是全局函数,不能是成员函数

25、考虑写一个不抛异常的swap函数

实现

26、尽可能延后变量定义式的出现时间

不要提前定义,使用前定义

27、尽量少做转型动作

const_cast<T>:常量转换

dynamic_cast<T>:继承关系转换

reinterpret_cast<T>:eg point to int 转型为 int

static_cast<T>:强迫隐式转换(无法const转为non-const,const_case<T>可以)

详细见另外一个blog

28、

原文地址:https://www.cnblogs.com/2012harry/p/3968321.html