构造/析构/赋值运算
Constructors,Destructors,and Assignment Operators
5.了解C++默认成员函数
Know what functions C++ silently writes and calls.
- C++默认成员函数有哪些?
- 默认析构函数是个non-virtual,除非这个class的bass class自身声明有virtual析构函数。
- 如果默认拷贝构造需要拷贝的成员是内置类型,那么会以“拷贝源对象的每一个bits”来完成初始化。如果拷贝的对象不是内置类型(如string类型),那么拷贝会调用string的拷贝构造来完成。
- 默认copy assignment会在不违反C++语言特性的基础上为程序员完成对象拷贝。
class Object{
public:
Object(string& s,const T& value);
string& s;
const T object_value;
};
......
std::string str1("Hello");
srd::string str2("Gun");
Object<std::string,int> o1(str1,666);
Object<std::string,int> o2(str2,438);
o1=o2;//调用默认的赋值运算符是不会得逞的。
o1的成员string&是reference,不能改指向不同对象,o1的成员const T具有const属性,也不能被改变。
除构造函数外,其余成员函数调用过程都需要使用隐含的this指针去完成调用。
关于C++中默认的成员函数相关知识,推荐阅读:http://c.zhizuobiao.com/c-19012100036/
6.如果不想使用编译器默认生成的函数,就该明确拒绝
Explicitly disallow the use of compiler-generated functions you do not want.
- 将默认函数声明私有化(private类型函数),并且不定义(防止friend函数调用,直接不实现)。
#include<iostream>
class No_copy {
private:
No_copy(No_copy&);
No_copy& operator=(const No_copy&);
};
拷贝构造和重载赋值操作符对象自身可见,友元函数可见,但是我的函数并未实现,也不能调用。
2.
class No_copy{
protected:
No_copy (){}
~No_copy (){}
private:
No_copy (const No_copy &);
No_copy& operator=(const No_copy &);
};
class No_copy_son:private No_copy
{
.......
};
派生类无法调用基类的私有成员函数,无法实现拷贝或者赋值。
3. C++11太人性化,没天理。
#include<iostream>
class No_copy {
private:
No_copy(No_copy&)=delete;
No_copy operator=(const No_copy&)=delete;
};
7.为多态基类声明virtual析构函数
Declare destructors virtual in polymorphic base classes.
- 析构函数最好是虚函数。 在多态场景下,如果析构函数不是virtual的,通过base class指针删除derived class,会出现结果未定义,通常发生的是derived class未被销毁。而销毁掉了base class对象,形成资源泄漏等问题。
#include<iostream>
class Father {
public:
Father()
{
father_ptr = new char[10];
std::cout << "Father()" << std::endl;
}
~Father()
{
delete[] father_ptr;
std::cout << "~Father()" << std::endl;
}
private:
char* father_ptr;
};
class Son :public Father
{
public:
Son()
{
son_ptr = new char[5];
std::cout << "Son()" << std::endl;
}
~Son()
{
delete[] son_ptr;
std::cout << "~Son()" << std::endl;
}
private:
char* son_ptr;
};
int main()
{
Father* pp = new Son;
delete pp;
}
Father()
Son()
~Father()
F:\代码\Project1\Debug\Project1.exe (进程 10520)已退出,返回代码为: 0。
若要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口...
析构函数没有定义成虚函数,造成了内存泄漏。
- 只有当class内含有至少一个virtual函数,才为它声明virtual析构函数。classes的设计目的不是作为base classes使用,或不是为了具备多态性,就不该声明virtual析构函数(增加vptr会增加对象大小)。
- STL的string、vector、list等容器均是non virtual析构函数,不要企图让这些容器作为base class。
- 有时候令class带有纯虚析构函数,可能颇为便利。最好对纯虚析构函数进行定义,因为derived class析构函数会调用base的析构函数。
8.别让异常逃离析构函数
Prevent exception from destructors.
- 如果析构函数抛出异常会过早结束或者出现不明确行为。C++很不喜欢析构函数抛出异常。
- 如果析构函数必须执行一个动作,而该动作可能会在失败时抛出异常。那么最好赋予客户一个处理异常的机会。而如果在客户处理“因该操作发生的异常”之后,仍然无法避免异常,那么最终只有“强迫结束程序”或者“吞下异常”。
class DBConn{
public:
void close()
{
db.close();
closed=true;
}
~DBConn()
{
if(!closed){
try{
db.close();
}
catch(...){
//abort()强迫结束程序,可抢先制止不明确行为
//或者记录异常行为吞下异常
}
}
}
private:
DBConnection db;
bool closed;
};
- 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。