C++那些细节--中operator=相关问题

转载:http://blog.csdn.net/puppet_master/article/details/46973435


一.简介

operator= 运算符重载,就类似正常我们为变量赋值(如a = b)时的情况,但是我们自己写的类,内部肯定有各种字段,赋值当然不会就像a=b那么简单啦。


如果我们自己写重载操作符=,编译器也会为我们生成一个,但是这个函数功能很弱,只能实现浅赋值。


  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include “stdafx.h”  
  5. #include <iostream>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9.   
  10. class CopyTest  
  11. {  
  12. private:  
  13.     string name;  
  14.     int id;  
  15. public:  
  16.     CopyTest(int i, string n):id(i),name(n)  
  17.     {  
  18.         //cout<<”Construct!”<<endl;  
  19.     }  
  20.   
  21.     ~CopyTest()  
  22.     {  
  23.         //cout<<”No”<<id<<” ”<<”Destruct!”<<endl;  
  24.     }  
  25.   
  26.     void Display()  
  27.     {  
  28.         cout<<name<<” ”<<id<<endl;  
  29.     }  
  30. };  
  31.   
  32. int _tmain(int argc, _TCHAR* argv[])  
  33. {  
  34.     CopyTest test1(1, ”hehe”);  
  35.     CopyTest test2(2, ”haha”);  
  36.       
  37.     cout<<”Before operator = test2 is: ”;  
  38.     test2.Display();  
  39.   
  40.     test2 = test1;  
  41.     cout<<”After operator = test2 is ”;  
  42.     test1.Display();  
  43.   
  44.     system(”pause”);  
  45.     return 0;  
  46. }  
// C++Test.cpp : 定义控制台应用程序的入口点。 
//

include "stdafx.h"

include <iostream>

include <string>

using namespace std;

class CopyTest
{
private:
string name;
int id;
public:
CopyTest(int i, string n):id(i),name(n)
{
//cout<<"Construct!"<<endl;
}

~CopyTest()
{
    //cout&lt;&lt;"No"&lt;&lt;id&lt;&lt;" "&lt;&lt;"Destruct!"&lt;&lt;endl;
}

void Display()
{
    cout&lt;&lt;name&lt;&lt;" "&lt;&lt;id&lt;&lt;endl;
}

};

int _tmain(int argc, _TCHAR* argv[])
{
CopyTest test1(1, “hehe”);
CopyTest test2(2, “haha”);

cout&lt;&lt;"Before operator = test2 is: ";
test2.Display();

test2 = test1;
cout&lt;&lt;"After operator = test2 is ";
test1.Display();

system("pause");
return 0;

}

结果:

Before operator = test2 is: haha 2
After operator = test2 is hehe 1
请按任意键继续…


可见,虽然我们并没有写那个operator=的函数,但是我们仍然实现了对象的赋值。


二.自己DIY一个operator=函数

  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include “stdafx.h”  
  5. #include <iostream>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9.   
  10. class CopyTest  
  11. {  
  12. private:  
  13.     string name;  
  14.     int id;  
  15. public:  
  16.     CopyTest(int i, string n):id(i),name(n)  
  17.     {  
  18.         //cout<<”Construct!”<<endl;  
  19.     }  
  20.   
  21.     ~CopyTest()  
  22.     {  
  23.         //cout<<”No”<<id<<” ”<<”Destruct!”<<endl;  
  24.     }  
  25.   
  26.     CopyTest& operator= (const CopyTest& e)  
  27.     {  
  28.         name = e.name;  
  29.         id = e.id;  
  30.         cout<<”operator= function is called!”<<endl;  
  31.         return *this;  
  32.     }  
  33.   
  34.     void Display()  
  35.     {  
  36.         cout<<name<<” ”<<id<<endl;  
  37.     }  
  38. };  
  39.   
  40. int _tmain(int argc, _TCHAR* argv[])  
  41. {  
  42.     CopyTest test1(1, ”hehe”);  
  43.     CopyTest test2(2, ”haha”);  
  44.       
  45.     cout<<”Before operator = test2 is: ”;  
  46.     test2.Display();  
  47.   
  48.     test2 = test1;  
  49.     cout<<”After operator = test2 is ”;  
  50.     test1.Display();  
  51.   
  52.     system(”pause”);  
  53.     return 0;  
  54. }  
// C++Test.cpp : 定义控制台应用程序的入口点。
//





include "stdafx.h"

include <iostream>

include <string>

using namespace std;

class CopyTest
{
private:
string name;
int id;
public:
CopyTest(int i, string n):id(i),name(n)
{
//cout<<"Construct!"<<endl;
}

~CopyTest()
{
    //cout&lt;&lt;"No"&lt;&lt;id&lt;&lt;" "&lt;&lt;"Destruct!"&lt;&lt;endl;
}

CopyTest&amp; operator= (const CopyTest&amp; e)
{
    name = e.name;
    id = e.id;
    cout&lt;&lt;"operator= function is called!"&lt;&lt;endl;
    return *this;
}

void Display()
{
    cout&lt;&lt;name&lt;&lt;" "&lt;&lt;id&lt;&lt;endl;
}

};

int _tmain(int argc, _TCHAR* argv[])
{
CopyTest test1(1, “hehe”);
CopyTest test2(2, “haha”);

cout&lt;&lt;"Before operator = test2 is: ";
test2.Display();

test2 = test1;
cout&lt;&lt;"After operator = test2 is ";
test1.Display();

system("pause");
return 0;

}


结果:
Before operator = test2 is: haha 2
operator= function is called!
After operator = test2 is hehe 1
请按任意键继续…

这里我们就可以看到,系统调用了我们所写的operator=函数,完成了与系统为我们添加的函数相同的功能。


三.operator=返回一个本对象的引用

这里有个细节,要注意一下,同时这也是《Effictive C++》中的重要一条:

关于operator=函数的返回值,我们的返回值类型是对象本身的引用,return时使用的是this指针所指的位置的内容(即*this的值),为什么要这样呢?

对于=,我们可能会这样使用:
  1. a = b = c;  
  2. //相当于:  
  3. a = (b = c);  
a = b = c;
//相当于:
a = (b = c);
即所谓的连锁赋值,赋值操作符必须返回一个reference指向操作符的左侧实参。

注意,这种操作在赋值操作中都成立。
  1. CopyTest& operator= (const CopyTest& e)  
  2.         {   …  
  3.         return *this;  
  4.     }  
CopyTest& operator= (const CopyTest& e)
        {   ...
        return *this;
    }
  1. </pre><pre name=“code” class=“cpp”>//在+=,-=,*=,/=等情况也都成立  
</pre><pre name="code" class="cpp">//在+=,-=,*=,/=等情况也都成立

 当然,这不是强制的,只是一个协议。不遵守程序并无问题,但是这条协议被所有内置数据类型,string,vector等等C++几乎最常用的类型所遵守,我们还是随大流吧… 

四.深拷贝浅拷贝

与拷贝构造函数中相同,operator=重载中也含有深拷贝与浅拷贝的概念。浅拷贝即只是字面上的拷贝,不涉及指针等动态空间的拷贝,而深拷贝则是会处理动态内存空间的相关信息。关于浅拷贝和深拷贝,在拷贝构造函数中有更详细的介绍: http://blog.csdn.net/puppet_master/article/details/46955965,如果我们不写operator=的话,编译器会自动为我们生成一个,但是这个自动生成的只能实现浅拷贝,遇到动态内存相关的肯定会出错,所以我们还是自己写一个比较好。
  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include “stdafx.h”  
  5. #include <iostream>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9.   
  10. class CopyTest  
  11. {  
  12. private:  
  13.     string name;  
  14.     int id;  
  15.     int* pointer;  
  16. public:  
  17.     CopyTest(int i, string n, int* p):id(i),name(n), pointer(p)  
  18.     {  
  19.         //cout<<”Construct!”<<endl;  
  20.     }  
  21.   
  22.     ~CopyTest()  
  23.     {  
  24.         //cout<<”No”<<id<<” ”<<”Destruct!”<<endl;  
  25.     }  
  26.   
  27.   
  28.     CopyTest& operator= (const CopyTest& e)  
  29.     {  
  30.         name = e.name;  
  31.         id = e.id;  
  32.         delete pointer;  
  33.         pointer = new int(*e.pointer);  
  34.         cout<<”operator= function is called!”<<endl;  
  35.         return *this;  
  36.     }  
  37.   
  38.     void Display()  
  39.     {  
  40.         cout<<”name: ”<<name<<“ ”<<“id: ”<<id<<“ pointer: ”<<*pointer<<endl;  
  41.     }  
  42. };  
  43.   
  44. int _tmain(int argc, _TCHAR* argv[])  
  45. {  
  46.     CopyTest test1(1, ”hehe”new int(1));  
  47.     CopyTest test2(2, ”haha”new int (2));  
  48.       
  49.     cout<<”Before operator = test2 is: ”;  
  50.     test2.Display();  
  51.   
  52.     test2 = test1;  
  53.     cout<<”After operator = test2 is ”;  
  54.     test1.Display();  
  55.   
  56.     system(”pause”);  
  57.     return 0;  
  58. }  
// C++Test.cpp : 定义控制台应用程序的入口点。
//





include "stdafx.h"

include <iostream>

include <string>

using namespace std;

class CopyTest
{
private:
string name;
int id;
int* pointer;
public:
CopyTest(int i, string n, int* p):id(i),name(n), pointer(p)
{
//cout<<"Construct!"<<endl;
}

~CopyTest()
{
    //cout&lt;&lt;"No"&lt;&lt;id&lt;&lt;" "&lt;&lt;"Destruct!"&lt;&lt;endl;
}


CopyTest&amp; operator= (const CopyTest&amp; e)
{
    name = e.name;
    id = e.id;
    delete pointer;
    pointer = new int(*e.pointer);
    cout&lt;&lt;"operator= function is called!"&lt;&lt;endl;
    return *this;
}

void Display()
{
    cout&lt;&lt;"name: "&lt;&lt;name&lt;&lt;" "&lt;&lt;"id: "&lt;&lt;id&lt;&lt;" pointer: "&lt;&lt;*pointer&lt;&lt;endl;
}

};

int _tmain(int argc, _TCHAR* argv[])
{
CopyTest test1(1, “hehe”, new int(1));
CopyTest test2(2, “haha”, new int (2));

cout&lt;&lt;"Before operator = test2 is: ";
test2.Display();

test2 = test1;
cout&lt;&lt;"After operator = test2 is ";
test1.Display();

system("pause");
return 0;

}


结果:
Before operator = test2 is: name: haha id: 2 pointer: 2
operator= function is called!
After operator = test2 is name: hehe id: 1 pointer: 1
请按任意键继续…

上面的就是一个最简单的深拷贝,普通的变量还是正常赋值,但是遇到指针时,先把原来指针所指向的内容delete掉,释放内存,然后重新申请一块内存,并将这块内存的值用=右边的指针所指向的内容赋值,用本对象中的指针指向。

五.防止对象自我赋值

上面的例子虽然很好的实现了深拷贝,但是有一个问题,如果我们用一个普通的变量自己给自己赋值,正常是没有问题的,但是深拷贝涉及到指针,和内存释放的问题,试想一下,如果我们自己给自己赋值,先delete掉了pointer所指向的内容,然后我们又new出了一块内存,要给他赋值的时候,发现这个pointer已经被释放了,所以肯定会出问题…
例如,上面的程序我们这样赋值:
  1. test2 = test2;  
test2 = test2;
那么结果:

Before operator = test2 is: name: haha id: 2 pointer: 2
operator= function is called!
After operator = test2 is name: haha id: 2 pointer: -17891602
请按任意键继续…

pointer的值变成了一个很奇怪的数。

也许有人会问,我们不会傻到自己给自己赋值吧?
还真不好说,上面的情况比较极端,如果我们用一个数组循环的时候,说不好i,j重合了,那么就有可能发生这种情况。或者代码比较复杂时,很有可能就会出现自我赋值的情况。
既然我们知道有这个隐患,那么就要消除这个隐患,其实这个很简单,我们只需要在赋值之前判断一下, 这两个对象是不是同一个对象即可,如果是同一个对象,那么就直接返回,不进行操作。

  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include “stdafx.h”  
  5. #include <iostream>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9.   
  10. class CopyTest  
  11. {  
  12. private:  
  13.     string name;  
  14.     int id;  
  15.     int* pointer;  
  16. public:  
  17.     CopyTest(int i, string n, int* p):id(i),name(n), pointer(p)  
  18.     {  
  19.         //cout<<”Construct!”<<endl;  
  20.     }  
  21.   
  22.     ~CopyTest()  
  23.     {  
  24.         //cout<<”No”<<id<<” ”<<”Destruct!”<<endl;  
  25.     }  
  26.   
  27.   
  28.     CopyTest& operator= (const CopyTest& e)  
  29.     {  
  30.   
  31.         if (this == &e)  
  32.             return *this;  
  33.         name = e.name;  
  34.         id = e.id;  
  35.         delete pointer;  
  36.         pointer = new int(*e.pointer);  
  37.         cout<<”operator= function is called!”<<endl;  
  38.         return *this;  
  39.     }  
  40.   
  41.     void Display()  
  42.     {  
  43.         cout<<”name: ”<<name<<“ ”<<“id: ”<<id<<“ pointer: ”<<*pointer<<endl;  
  44.     }  
  45. };  
  46.   
  47. int _tmain(int argc, _TCHAR* argv[])  
  48. {  
  49.     CopyTest test1(1, ”hehe”new int(1));  
  50.     CopyTest test2(2, ”haha”new int (2));  
  51.       
  52.     cout<<”Before operator = test2 is: ”;  
  53.     test2.Display();  
  54.   
  55.     test2 = test2;  
  56.     cout<<”After operator = test2 is ”;  
  57.     test2.Display();  
  58.   
  59.     system(”pause”);  
  60.     return 0;  
  61. }  
// C++Test.cpp : 定义控制台应用程序的入口点。
//





include "stdafx.h"

include <iostream>

include <string>

using namespace std;

class CopyTest
{
private:
string name;
int id;
int* pointer;
public:
CopyTest(int i, string n, int* p):id(i),name(n), pointer(p)
{
//cout<<"Construct!"<<endl;
}

~CopyTest()
{
    //cout&lt;&lt;"No"&lt;&lt;id&lt;&lt;" "&lt;&lt;"Destruct!"&lt;&lt;endl;
}


CopyTest&amp; operator= (const CopyTest&amp; e)
{

    if (this == &amp;e)
        return *this;
    name = e.name;
    id = e.id;
    delete pointer;
    pointer = new int(*e.pointer);
    cout&lt;&lt;"operator= function is called!"&lt;&lt;endl;
    return *this;
}

void Display()
{
    cout&lt;&lt;"name: "&lt;&lt;name&lt;&lt;" "&lt;&lt;"id: "&lt;&lt;id&lt;&lt;" pointer: "&lt;&lt;*pointer&lt;&lt;endl;
}

};

int _tmain(int argc, _TCHAR* argv[])
{
CopyTest test1(1, “hehe”, new int(1));
CopyTest test2(2, “haha”, new int (2));

cout&lt;&lt;"Before operator = test2 is: ";
test2.Display();

test2 = test2;
cout&lt;&lt;"After operator = test2 is ";
test2.Display();

system("pause");
return 0;

}

结果:

Before operator = test2 is: name: haha id: 2 pointer: 2
After operator = test2 is name: haha id: 2 pointer: 2
请按任意键继续…

这样,我们就防止了自我赋值时发生的意外情况。这是《Effictive C++》中的一条。


六.防止拷贝过程中内存申请失败导致原来内容被破坏

解决了上面自我赋值的隐患,我们还有一个隐患,如果赋值过程中new失败了,内存没有申请成功,那么原来的内容已经被删除了,就会出现我们不想看到的情况。要解决这种情况只需要简单的加几句话:
  1. CopyTest& operator= (const CopyTest& e)  
  2.     {  
  3.         if (this == &e)  
  4.             return *this;  
  5.         name = e.name;  
  6.         id = e.id;  
  7.         //先用一个临时指针变量存储  
  8.         int* tempPointer = new int (*e.pointer);  
  9.         //如果上一步执行成功,才删除原有内容  
  10.         delete pointer;  
  11.         //赋值  
  12.         pointer = tempPointer;  
  13.         cout<<”operator= function is called!”<<endl;  
  14.         return *this;  
  15.     }  
CopyTest& operator= (const CopyTest& e)
    {
        if (this == &e)
            return *this;
        name = e.name;
        id = e.id;
        //先用一个临时指针变量存储
        int* tempPointer = new int (*e.pointer);
        //如果上一步执行成功,才删除原有内容
        delete pointer;
        //赋值
        pointer = tempPointer;
        cout<<"operator= function is called!"<<endl;
        return *this;
    }

结果:

Before operator = test2 is: name: haha id: 2 pointer: 2
operator= function is called!
After operator = test2 is name: hehe id: 1 pointer: 1
请按任意键继续…


七.去除operator= 和拷贝构造函数中的冗余代码

通过c++的一个函数,swap,我们可以实现值的交换
  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include “stdafx.h”  
  5. #include <iostream>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9.   
  10. class CopyTest  
  11. {  
  12. private:  
  13.     string name;  
  14.     int id;  
  15.     int* pointer;  
  16. public:  
  17.     CopyTest(int i, string n, int* p):id(i),name(n), pointer(p)  
  18.     {  
  19.         //cout<<”Construct!”<<endl;  
  20.     }  
  21.   
  22.     ~CopyTest()  
  23.     {  
  24.         //cout<<”No”<<id<<” ”<<”Destruct!”<<endl;  
  25.     }  
  26.   
  27.     //拷贝构造函数  
  28.     CopyTest(const CopyTest& e)  
  29.     {  
  30.         name = e.name;  
  31.         id = e.id;  
  32.         pointer = new int ();  
  33.         *pointer = *e.pointer;  
  34.         cout<<”Copy Construct is called!”<<endl;  
  35.     }  
  36.   
  37.     CopyTest& operator= (const CopyTest& e)  
  38.     {  
  39.         CopyTest temp(e);  
  40.         std::swap(*this, temp);  
  41.         cout<<”operator= function is called!”<<endl;  
  42.         return *this;  
  43.     }  
  44.   
  45.     void Display()  
  46.     {  
  47.         cout<<”name: ”<<name<<“ ”<<“id: ”<<id<<“ pointer: ”<<*pointer<<endl;  
  48.     }  
  49. };  
  50.   
  51. int _tmain(int argc, _TCHAR* argv[])  
  52. {  
  53.     CopyTest test1(1, ”hehe”new int(1));  
  54.     CopyTest test2(2, ”haha”new int (2));  
  55.       
  56.     cout<<”Before operator = test2 is: ”;  
  57.     test2.Display();  
  58.   
  59.     test2 = test1;  
  60.     cout<<”After operator = test2 is ”;  
  61.     test2.Display();  
  62.   
  63.     system(”pause”);  
  64.     return 0;  
  65. }  
// C++Test.cpp : 定义控制台应用程序的入口点。
//





include "stdafx.h"

include <iostream>

include <string>

using namespace std;

class CopyTest
{
private:
string name;
int id;
int* pointer;
public:
CopyTest(int i, string n, int* p):id(i),name(n), pointer(p)
{
//cout<<"Construct!"<<endl;
}

~CopyTest()
{
    //cout&lt;&lt;"No"&lt;&lt;id&lt;&lt;" "&lt;&lt;"Destruct!"&lt;&lt;endl;
}

//拷贝构造函数
CopyTest(const CopyTest&amp; e)
{
    name = e.name;
    id = e.id;
    pointer = new int ();
    *pointer = *e.pointer;
    cout&lt;&lt;"Copy Construct is called!"&lt;&lt;endl;
}

CopyTest&amp; operator= (const CopyTest&amp; e)
{
    CopyTest temp(e);
    std::swap(*this, temp);
    cout&lt;&lt;"operator= function is called!"&lt;&lt;endl;
    return *this;
}

void Display()
{
    cout&lt;&lt;"name: "&lt;&lt;name&lt;&lt;" "&lt;&lt;"id: "&lt;&lt;id&lt;&lt;" pointer: "&lt;&lt;*pointer&lt;&lt;endl;
}

};

int _tmain(int argc, _TCHAR* argv[])
{
CopyTest test1(1, “hehe”, new int(1));
CopyTest test2(2, “haha”, new int (2));

cout&lt;&lt;"Before operator = test2 is: ";
test2.Display();

test2 = test1;
cout&lt;&lt;"After operator = test2 is ";
test2.Display();

system("pause");
return 0;

}
不过,这里我的程序会不停的调用swap函数,直到stack overflow…查了半天也没差出来,以后来填坑。



更方便的办法:
  1. CopyTest& operator= (CopyTest& e)  
  2.     {  
  3.         std::swap(*this, e);  
  4.         cout<<”operator= function is called!”<<endl;  
  5.         return *this;  
  6.     }  
CopyTest& operator= (CopyTest& e)
    {
        std::swap(*this, e);
        cout<<"operator= function is called!"<<endl;
        return *this;
    }
直接使用函数的实参,来作为swap的参数,但是注意要把const去掉。


简单总结一下:
1.确保当对象自我赋值时operator=有良好的行为,其中技术包括比较“来源对象”和“目标对象”的地址,改变顺序达到防止内存分配失败而使原内容被删除,以及copy-and-swap。
2.确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为依然正确。


八.复制对象时勿忘其每一个成分(拷贝构造函数和operator=都要注意)

  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include “stdafx.h”  
  5. #include <iostream>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9.   
  10. class CopyTest  
  11. {  
  12. private:  
  13.     string name;  
  14.     int id;  
  15. public:  
  16.     CopyTest(int i, string n):id(i),name(n)  
  17.     {  
  18.         //cout<<”Construct!”<<endl;  
  19.     }  
  20.   
  21.     ~CopyTest()  
  22.     {  
  23.         //cout<<”No”<<id<<” ”<<”Destruct!”<<endl;  
  24.     }  
  25.   
  26.     //拷贝构造函数  
  27.     CopyTest(const CopyTest& e)  
  28.     {  
  29.         name = e.name;  
  30.         id = e.id;  
  31.         cout<<”Copy Construct is called!”<<endl;  
  32.     }  
  33.   
  34.     CopyTest& operator= (CopyTest& e)  
  35.     {  
  36.         if (this == &e)  
  37.             return *this;  
  38.         name = e.name;  
  39.         //id = e.id;如果我们忘记写这一句,编译器并不会报错  
  40.         cout<<”operator= function is called!”<<endl;  
  41.         return *this;  
  42.     }  
  43.   
  44.     void Display()  
  45.     {  
  46.         cout<<”name: ”<<name<<“ ”<<“id: ”<<id<<endl;  
  47.     }  
  48. };  
  49.   
  50. int _tmain(int argc, _TCHAR* argv[])  
  51. {  
  52.     CopyTest test1(1, ”hehe”);  
  53.     CopyTest test2(2, ”haha”);  
  54.       
  55.     cout<<”Before operator = test2 is: ”;  
  56.     test2.Display();  
  57.   
  58.     test2 = test1;  
  59.     cout<<”After operator = test2 is ”;  
  60.     test2.Display();  
  61.   
  62.     system(”pause”);  
  63.     return 0;  
  64. }  
// C++Test.cpp : 定义控制台应用程序的入口点。
//





include "stdafx.h"

include <iostream>

include <string>

using namespace std;

class CopyTest
{
private:
string name;
int id;
public:
CopyTest(int i, string n):id(i),name(n)
{
//cout<<"Construct!"<<endl;
}

~CopyTest()
{
    //cout&lt;&lt;"No"&lt;&lt;id&lt;&lt;" "&lt;&lt;"Destruct!"&lt;&lt;endl;
}

//拷贝构造函数
CopyTest(const CopyTest&amp; e)
{
    name = e.name;
    id = e.id;
    cout&lt;&lt;"Copy Construct is called!"&lt;&lt;endl;
}

CopyTest&amp; operator= (CopyTest&amp; e)
{
    if (this == &amp;e)
        return *this;
    name = e.name;
    //id = e.id;如果我们忘记写这一句,编译器并不会报错
    cout&lt;&lt;"operator= function is called!"&lt;&lt;endl;
    return *this;
}

void Display()
{
    cout&lt;&lt;"name: "&lt;&lt;name&lt;&lt;" "&lt;&lt;"id: "&lt;&lt;id&lt;&lt;endl;
}

};

int _tmain(int argc, _TCHAR* argv[])
{
CopyTest test1(1, “hehe”);
CopyTest test2(2, “haha”);

cout&lt;&lt;"Before operator = test2 is: ";
test2.Display();

test2 = test1;
cout&lt;&lt;"After operator = test2 is ";
test2.Display();

system("pause");
return 0;

}
结果:


Before operator = test2 is: name: haha id: 2
operator= function is called!
After operator = test2 is name: hehe id: 2
请按任意键继续…

可见,这里,我们忘记写了一句id的拷贝,于是编译器就老老实实的按照我们的方法执行了。“叫你不用我默认的方法,这下你出错了我也不告诉你…”
这是很难发现的一个错误,所以我们在复制值时,一定要记住所有的变量都要写全,不要漏写。

另外一种情况更加危险,更不容易被我们发现,一定要注意!!!
当我们的这个类又派生出其他的类时,拷贝时一定要把基类的内容也一并拷贝过去!!!


九.派生类中的拷贝要考虑基类部分的拷贝(拷贝构造函数和operator=都要注意)

  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include “stdafx.h”  
  5. #include <iostream>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9.   
  10. class CopyTest  
  11. {  
  12. private:  
  13.     string name;  
  14.     int id;  
  15. public:  
  16.     CopyTest(int i, string n):id(i),name(n)  
  17.     {  
  18.         //cout<<”Construct!”<<endl;  
  19.     }  
  20.   
  21.     ~CopyTest()  
  22.     {  
  23.         //cout<<”No”<<id<<” ”<<”Destruct!”<<endl;  
  24.     }  
  25.   
  26.     //拷贝构造函数  
  27.     CopyTest(const CopyTest& e)  
  28.     {  
  29.         name = e.name;  
  30.         id = e.id;  
  31.         cout<<”CopyTest CopyConstructor is called!”<<endl;  
  32.     }  
  33.   
  34.     CopyTest& operator= (const CopyTest& e)  
  35.     {  
  36.         if (this == &e)  
  37.             return *this;  
  38.         name = e.name;  
  39.         id = e.id;  
  40.         cout<<”CopyTest operator= function is called!”<<endl;  
  41.         return *this;  
  42.     }  
  43.   
  44.     virtual void Display()  
  45.     {  
  46.         cout<<”name: ”<<name<<“ ”<<“id: ”<<id<<endl;  
  47.     }  
  48. };  
  49.   
  50. class CopyTestChild : public CopyTest  
  51. {  
  52. private:  
  53.     int childId;  
  54. public:  
  55.     CopyTestChild(int i, string n, int ci): CopyTest(i, n), childId(ci)  
  56.     {  
  57.         //cout<<”CopyTestCilid is Constructed!”<<endl;  
  58.     }  
  59.   
  60.     ~CopyTestChild()  
  61.     {  
  62.   
  63.     }  
  64.   
  65.     //子类拷贝构造函数调用基类拷贝构造函数  
  66.     CopyTestChild(const CopyTestChild& e) : CopyTest(e)  
  67.     {  
  68.         childId = e.childId;  
  69.         cout<<”CopyTestChild CopyConstructor is called!”<<endl;  
  70.     }  
  71.   
  72.     CopyTestChild& operator= (const CopyTestChild& e)   
  73.     {  
  74.         if (this == &e)  
  75.             return *this;  
  76.         //调用基类operator=函数  
  77.         CopyTest::operator=(e);  
  78.         childId = e.childId;  
  79.         cout<<”CopyTestChild operator= function is called!”<<endl;  
  80.         return *this;  
  81.     }  
  82.   
  83.     virtual void Display()  
  84.     {  
  85.         CopyTest::Display();  
  86.         cout<<”ChildId is ”<<childId<<endl;  
  87.     }  
  88.   
  89.       
  90. };  
  91.   
  92. int _tmain(int argc, _TCHAR* argv[])  
  93. {  
  94.     CopyTestChild test1(1, ”hehe”, 1);  
  95.     CopyTestChild test2(2, ”haha”, 2);  
  96.       
  97.     cout<<”Before operator = test2 is: ”;  
  98.     test2.Display();  
  99.   
  100.     test2 = test1;  
  101.     cout<<”After operator = test2 is ”;  
  102.     test2.Display();  
  103.   
  104.     system(”pause”);  
  105.     return 0;  
  106. }  
// C++Test.cpp : 定义控制台应用程序的入口点。
//





include "stdafx.h"

include <iostream>

include <string>

using namespace std;

class CopyTest
{
private:
string name;
int id;
public:
CopyTest(int i, string n):id(i),name(n)
{
//cout<<"Construct!"<<endl;
}

~CopyTest()
{
    //cout&lt;&lt;"No"&lt;&lt;id&lt;&lt;" "&lt;&lt;"Destruct!"&lt;&lt;endl;
}

//拷贝构造函数
CopyTest(const CopyTest&amp; e)
{
    name = e.name;
    id = e.id;
    cout&lt;&lt;"CopyTest CopyConstructor is called!"&lt;&lt;endl;
}

CopyTest&amp; operator= (const CopyTest&amp; e)
{
    if (this == &amp;e)
        return *this;
    name = e.name;
    id = e.id;
    cout&lt;&lt;"CopyTest operator= function is called!"&lt;&lt;endl;
    return *this;
}

virtual void Display()
{
    cout&lt;&lt;"name: "&lt;&lt;name&lt;&lt;" "&lt;&lt;"id: "&lt;&lt;id&lt;&lt;endl;
}

};

class CopyTestChild : public CopyTest
{
private:
int childId;
public:
CopyTestChild(int i, string n, int ci): CopyTest(i, n), childId(ci)
{
//cout<<”CopyTestCilid is Constructed!”<<endl;
}

~CopyTestChild()
{

}

//子类拷贝构造函数调用基类拷贝构造函数
CopyTestChild(const CopyTestChild&amp; e) : CopyTest(e)
{
    childId = e.childId;
    cout&lt;&lt;"CopyTestChild CopyConstructor is called!"&lt;&lt;endl;
}

CopyTestChild&amp; operator= (const CopyTestChild&amp; e) 
{
    if (this == &amp;e)
        return *this;
    //调用基类operator=函数
    CopyTest::operator=(e);
    childId = e.childId;
    cout&lt;&lt;"CopyTestChild operator= function is called!"&lt;&lt;endl;
    return *this;
}

virtual void Display()
{
    CopyTest::Display();
    cout&lt;&lt;"ChildId is "&lt;&lt;childId&lt;&lt;endl;
}

};

int _tmain(int argc, _TCHAR* argv[])
{
CopyTestChild test1(1, “hehe”, 1);
CopyTestChild test2(2, “haha”, 2);

cout&lt;&lt;"Before operator = test2 is: ";
test2.Display();

test2 = test1;
cout&lt;&lt;"After operator = test2 is ";
test2.Display();

system("pause");
return 0;

}
结果:


Before operator = test2 is: name: haha id: 2
ChildId is 2
CopyTest operator= function is called!
CopyTestChild operator= function is called!
After operator = test2 is name: hehe id: 1
ChildId is 1
请按任意键继续…

拷贝构造函数的情况:
  1. int _tmain(int argc, _TCHAR* argv[])  
  2. {  
  3.     CopyTestChild test1(1, ”hehe”, 1);  
  4.       
  5.     CopyTestChild test2(test1);  
  6.     test2.Display();  
  7.   
  8.     system(”pause”);  
  9.     return 0;  
  10. }  
int _tmain(int argc, _TCHAR* argv[])
{
    CopyTestChild test1(1, "hehe", 1);

    CopyTestChild test2(test1);
    test2.Display();

    system("pause");
    return 0;
}

结果:
CopyTest CopyConstructor is called!
CopyTestChild CopyConstructor is called!
name: hehe id: 1
ChildId is 1
请按任意键继续…

这个真的很容易忽略,我们很有可能只记住了子类要拷贝的东东,而忘记了父类。


十.关于拷贝构造函数和operator=函数中相同的部分怎么处理

看到拷贝构造函数和operator=函数中有很多重复的东东,确实有种强迫症想要使一个函数调用另一个函数,但是这样做并不妥当,两者没什么太大的关系,一个是构造一个不存在的对象,另一个是给已经存在的对象赋值。要说两者的相同之处只是有一部分赋值的代码相同,我们如果确实想要优化就把这一部分代码提取出来,放到一个Init函数中,供两者调用即可。














            </div>
                </div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值