您的位置:首页 > 编程语言 > C语言/C++

EffectC++(二):构造/析构/赋值运算

2016-01-23 17:33 309 查看
条款5:了解C++默默为你编写的三个函数
注意点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 构造函数。因为那样的结果是不确定的,除非你自己很确定。

后记:三个默认函数就像三剑客一般,一般你需要重新定义其中一个,你就需要另外两个。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: