自己习惯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、