【翠字营原创】: 函数参数(实参, 虚参)、返回值,类深度拷贝构造、赋值操作符深一步的了解

来源:互联网 发布:js table tr td 编辑:程序博客网 时间:2024/06/08 20:57

相信大家对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;
 }