EffectC++(二):构造/析构/赋值运算
2016-01-23 17:33
309 查看
条款5:了解C++默默为你编写的三个函数
注意点1:三个函数都是public且inline 且non-virtual.
注意点2:Copy 构造函数 和 CopyAssignment 操作符 会将源对象的每一个非non-static 拷贝的目标对象。
例子:
结果:
条款6:如果不想使用系统默认的这三个默认函数,需要明确的拒绝。
拒绝的方法:将需要拒绝的函数放置到private区域。
如:
条款7:为多态基类声明virtual析构函数
注意点1:如果该类不会作为基类(也就是被其他继承)请不要使用virtual析构函数。另外,请不要继承使用non-virtual析构。
比如:classCTestA:publicstd::string
如果你使用的一个指针指向它,在析构的时候将会步入未定义行为的恶地。
技巧点:使用pure析构函数 (纯虚函数)
例:注意到即使使用pure析构函数同样可以再定义。
条款8:严禁析构出现异常
注:此处不是谈论异常的处理,而是谈论如何在析构的时候避开异常提示。
此处列举的例子具有具体性,假设一个数据库类,在析构的时候需要断开此连接
例子:
1. 一般不会采用的做法:
2. 比较好的做法:
条款9:绝不在构造函数和析构函数中调用virtual函数
注意点:逻辑本身不建议这么做。
例如:
执行结果:
如此运行了两次,如果非必要的话,请谨慎使用。
条款10:令operator= 返回一个reference to *this
注:此处的operator=泛指+=/ -= / *= / /= ..等等
原因:连续赋值。
条款11:小心在operator=中对自己赋值
处理办法1:对自我赋值进行检测
CTest& operator=(const CTest& other)
{
if(this == other)
return *this;
this->SetValue(other.GetValue());
return *this;
}
处理办法2(增加了对出现异常的处理):
注:两种书写方式本质上是一样的,swap不必再区别2个对象是否还会存在不同。
条款12:复制对象时请不要忘记任何一个成分
注意点:
这是个有意思的注意点:不要在派生类中的copyassignment 调用 copy 构造函数。因为那样的结果是不确定的,除非你自己很确定。
后记:三个默认函数就像三剑客一般,一般你需要重新定义其中一个,你就需要另外两个。
注意点1:三个函数都是public且inline 且non-virtual.
注意点2:Copy 构造函数 和 CopyAssignment 操作符 会将源对象的每一个非non-static 拷贝的目标对象。
例子:
#include<iostream> class CTestA { public: void SetValue(int a,int *b) { i = a; j = b; } void Show() { std::cout<<"i = "<<i<<std::endl; std::cout<<"j = "<<j<<std::endl; } private: int i; int *j; static int k; }; int main() { CTestA a,b;//采用默认构造函数 std::cout<<"defalut a:"<<std::endl; a.Show(); std::cout<<"defalut b:"<<std::endl; b.Show(); int i = 0; int *j = &i; a.SetValue(i,j); b = a; std::cout<<"SetValue a:"<<std::endl; a.Show(); std::cout<<"Copy assignmentb:"<<std::endl; b.Show(); CTestA c(a); std::cout<<"Copy c:"<<std::endl; c.Show(); return 0; }
结果:
条款6:如果不想使用系统默认的这三个默认函数,需要明确的拒绝。
拒绝的方法:将需要拒绝的函数放置到private区域。
如:
#include<iostream> class CTestA { public: void SetValue(int a,int *b) { i = a; j = b; } void Show() { std::cout<<"i ="<<i<<std::endl; std::cout<<"j ="<<j<<std::endl; } private:// 这里 CTestA(const CTestA &other); CTestA & operator=(const CTestA&other); private: int i; int *j; static int k; };
条款7:为多态基类声明virtual析构函数
注意点1:如果该类不会作为基类(也就是被其他继承)请不要使用virtual析构函数。另外,请不要继承使用non-virtual析构。
比如:classCTestA:publicstd::string
如果你使用的一个指针指向它,在析构的时候将会步入未定义行为的恶地。
技巧点:使用pure析构函数 (纯虚函数)
例:注意到即使使用pure析构函数同样可以再定义。
#include<iostream> #include<string> classCTestBase { public: virtual ~CTestBase() = 0; void SetValue(int a,int *b) { i = a; j = b; } void Show() { std::cout<<"i ="<<i<<std::endl; std::cout<<"j ="<<j<<std::endl; } private: int i; int *j; }; CTestBase::~CTestBase() { std::cout<<"this is~Base"<<std::endl; } classCTestDerived:public CTestBase { public: virtual ~CTestDerived(); }; CTestDerived::~CTestDerived() { std::cout<<"this is~Derived"<<std::endl; } int main() { CTestDerived a; return 0; }
条款8:严禁析构出现异常
注:此处不是谈论异常的处理,而是谈论如何在析构的时候避开异常提示。
此处列举的例子具有具体性,假设一个数据库类,在析构的时候需要断开此连接
例子:
1. 一般不会采用的做法:
classCTestDataBase { public: CTestDataBase() { std::cout<<"ConnectDB!"<<std::endl; } ~CTestDataBase() { std::cout<<"CloseDB!"<<std::endl; } };
2. 比较好的做法:
#include<stdexcept> classCTestDataBase { public: CTestDataBase() { ConnectDB(); } ~CTestDataBase() { if(!m_bClosed)// 双保险2 { try { CloseDB(); } catch(...) { std::cout<<"CloseDBerror"<<std::endl; } } } void ConnectDB() { std::cout<<"ConnectDB!"<<std::endl; m_bClosed = false; } void CloseDB()//双保险1 { std::cout<<"CloseDB!"<<std::endl; m_bClosed = true; } private: bool m_bClosed; }; int main() { CTestDataBase a; return 0; }
条款9:绝不在构造函数和析构函数中调用virtual函数
注意点:逻辑本身不建议这么做。
例如:
#include <iostream> #include <string> #include <stdexcept> class CBase { public: CBase() { Log(); } virtual void Log() { std::cout<<"Base Log"<<std::endl; } }; class CDerived:public CBase { public: CDerived():CBase() { Log(); } virtual void Log() { std::cout<<"Derived Log"<<std::endl; } }; int main() { CDerived a; return 0; }
执行结果:
如此运行了两次,如果非必要的话,请谨慎使用。
条款10:令operator= 返回一个reference to *this
注:此处的operator=泛指+=/ -= / *= / /= ..等等
原因:连续赋值。
#include <iostream> #include <string> #include <stdexcept> class CTest { public: void SetValue(int v) { val = v; } int GetValue() const { return val; } void ShowValue() { std::cout<<"val = "<<val<<std::endl; } CTest& operator=(const /*小心这里的const 只能调用const函数*/ CTest& other) { this->SetValue(other.GetValue()); return *this; } private: int val; }; int main() { CTest a,b,c; a.SetValue(1); b = c = a; std::cout<<"a:"; a.ShowValue(); std::cout<<"b:"; b.ShowValue(); std::cout<<"c:"; c.ShowValue(); return 0; }
条款11:小心在operator=中对自己赋值
处理办法1:对自我赋值进行检测
CTest& operator=(const CTest& other)
{
if(this == other)
return *this;
this->SetValue(other.GetValue());
return *this;
}
处理办法2(增加了对出现异常的处理):
注:两种书写方式本质上是一样的,swap不必再区别2个对象是否还会存在不同。
//书写方式1 CTest& operator=(const CTest& other) { CTest other2(other); // 多构建一个副本 swap(other2); return *this; } //书写方式2 CTest& operator=(CTest other) { swap(other); return *this; }
条款12:复制对象时请不要忘记任何一个成分
注意点:
这是个有意思的注意点:不要在派生类中的copyassignment 调用 copy 构造函数。因为那样的结果是不确定的,除非你自己很确定。
后记:三个默认函数就像三剑客一般,一般你需要重新定义其中一个,你就需要另外两个。
相关文章推荐
- C++实现黄金分割数列(斐波纳奇数列)(非递归)
- LeetCode 1: Number of 1 Bits (C++)
- C语言 百炼成钢12
- 后缀数组
- C语言 hdoj1040
- C语言 hdoj1062
- C语言 hdoj1020
- libjpeg:实现jpeg内存解压缩塈转换色彩空间/压缩分辨率
- C++使用priority_queue方法
- c++
- 机器学习:概念学习FIND-S算法C++实现
- C++ inline 内联函数
- Visual C++在虚继承中使用协变报错
- Text reverse文字倒转
- effective c++笔记
- C++ vector 容器使用
- c++快速排序
- C语言 生成随机数
- c/c++中的sort()与qsort()用法
- 学习笔记--内核中C语言和汇编的使用