编译器会自动为每一个空类创建构造函数、拷贝构造函数、赋值运算符以及析构函数
不要使用编译器自动创建的函数,要杜绝这种情况发生,自己编写这些函数
编译器默认提供的函数,不仅不能给编程带来便利,而且会给在某种情况下引入难以定位的bug,因此应该明确拒绝
拒绝的方式就是明确声明该函数为私有且不提供函数体
私有表示对权限的控制,防止外部在意想不到的情况下调用
不提供函数体,是让编译器在链接的时候报错,明确提示你注意这个函数
class Uncopyable{
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
}
//作为基类继承即可
如果一个类中有一个虚函数,那么必须将析构函数也申明成虚函数,主要是防止资源泄露(使得析构从下向上依次执行)
如果涉及的类不用于多态,那么就不要声明虚函数,因为虚函数的调用需要借助虚函数列表,调用的效率没有普通函数高
如果不想一个类被实例化,那么就声明该类的虚函数为纯虚函数,且提供其实现
class AbstractClass
{
public:
virtual ~AbstractClass();
}
AbstractClass::~AbstractClass()
{
//函数实现
//目的是进行资源释放
}
析构函数绝对不要吐出异常,这会导致资源部分释放的问题,析构函数中的异常一定要try…catch处理
如果客户需要对某个操作做出反应,且有可能抛出异常,那么就需要提供一个普通函数执行该操作,这样设计的好处:
提供普通函数给客户使用,且明确会抛出异常,使得用户有机会对异常做出合理的处理
析构函数中检查资源释放的状态,做最后的处理
class DBConn
{
private:
DBConnect dbc;
bool closed;
public:
void close()
{
dbc.close();
closed = true;
}
~DBConn()
{
if(!closed)
{
try
{
dbc.close();
} catch(...) {
//停止或记录
}
}
}
}
//解决close会跑出异常的可能
看清楚描述,是构造函数和析构函数中不能调用虚函数;析构函数本身是可以设计成虚函数的,且在某些情况下,必须设计成虚函数
在构造函数未执行完成之前,类的虚函数表未建立
进入析构函数之前,类的虚函数表已经销毁,无法调用
好处:便于连续赋值,类似下面这种执行方式:
a = b = c = d //连续赋值
class Widget
{
public:
Widget& operator+=(const Widget& rhs)
{
return *this;
}
}
确认任何函数操作一个以上的对象,且对象可能重复,那么需要进行自我赋值的校验
进行自我赋值的判断:地址判断、copy-and-swap
copy-and-swap:交换两个对象的数据,在交换的过程中,任意一句语句发生异常,只会导致交换函数本身交换不成功,不会导致程序发生其他异常,程序依然可以运行,状态回到没有调用交换之前,唯一遗憾的就是程序的业务逻辑错了。这种函数我们叫异常安全函数
Widget& operator=(const Widget& rhs)
{
if(this == &rhs)
return *this;
// …
return *this;
}
Widget& operator=(const Widget& rhs)
{
Widget temp(rhs);
swap(temp);
return *this;
}
Widget& operator=(Widget rhs) //拷贝发生在参数传入的过程中
{
swap(rhs);
return *this;
}
代码静态检查时,有一条规则就是需要初始化每一个成员变量
拷贝构造函数和赋值运算符不能相互调用---记住就好,如果有相同的代码可以提取成一个private函数
拷贝构造的时候一定要拷贝自己的每一个成员,包括基类的每一个成员
手机扫一扫
移动阅读更方便
你可能感兴趣的文章