相信大家对C++的特性有了比较多的了解, 这一节我们来对函数参数(实参, 虚参), 返回值机制;类深度拷贝构造,赋值操作符
使用更深一步的了解。如果你对这一节完全了解,恭喜你,你对C++的了解不算太不肤浅。
我们还是使用例子来说明: 定义一个类 CTest, 包含构造函数, 拷贝构造函数,重载赋值操作符函数等。
class CTest
{
public:
CTest(): m_pch(NULL)
{
}
//为什么要使用深度拷贝构造,经典问题, 请各位自己了解
CTest(const CTest&oCTest)
{
if (NULL != oCTest.m_pch)
{
m_pch = new char[20];//为m_pch重新在堆上开辟空间
strncpy(m_pch, oCTest.m_pch, 19);
}
}
//为什么要重载赋值操作符,经典问题, 请各位自己了解
CTest& operator = (const CTest&oCTest)
{
if (this != &oCTest) // 防止自己赋值自己的情况
{
if (NULL != oCTest.m_pch)
{
m_pch = new char[20]; //为m_pch重新在堆上开辟空间
strncpy(m_pch, oCTest.m_pch, 19);
}
}
return *this;
}
//为m_pch开辟堆空间, 赋值操作
void setCh()
{
m_pch = new char[20]; // 为oCTest3.m_pch 在堆上开辟空间 地址为:0x00346f0
strncpy(m_pch, "It is test", 19); // 赋值oCTest3.m_pch 为 "It is test"
}
protected:
private:
char *m_pch; // 这个就是深度拷贝构造 和 重载赋值操作符的原因所在
};
//定义一个全局测试函数
CTest fun(CTest oCTest3)
{
//oCTest3是栈对象oCTest2的副本(形参) 地址: 0x0013fefc
//形参oCTest2的产生, 编译器需要做: CTest oCTest3 = oCTest2;
//调用的是oCTest3 的拷贝构造函数,这是第一次调用拷贝构造函数。但是由于oCTest2.m_pch == NULL,
//所以oCTest3 本次没有为m_pch开辟空间
//在这说明一下: 如果参数是引用就没有重新构造形参的过程了
oCTest3.setCh();
//在返回oCTest3值过程, 编译器又为我们做了一件事情
//为我们创建一个临时CTest 对象作为返回值副本,const CTest tmp_Test = oCTest3;
//在这里又调用拷贝构造函数,这是第二次调用拷贝构造函数。
return oCTest3;
}
int main(int argc, char* argv[])
{
CTest oCTest1; //栈对象 地址: 0x0013ff7c
CTest oCTest2; //栈对象 地址: 0x0013ff78
//oCTest2是实参传给fun函数,在返回的时候,
//将返回值副本对象赋给oCTest1, 调用oCTest1的重载赋值操作符函数
oCTest1 = fun(oCTest2);
return 0;
}
到现在就告一段落, 不知道有没有谁发现上面的例子实际上存在严重问题----- 内存泄露!
由于oCTest3调用setCh 函数后为m_pch 开辟堆空间, 并且oCTest3 又是栈对象,
在退出作用范围后会被系统释放这个时候m_pch 开辟的堆空间泄露了。
怎么补救吗,很简单: 在CTest 析构函数释放它, 让它有始有终吧
~ CTest()
{
delete m_pch;
m_pch = NULL;
}