拷贝构造函数和赋值函数的一些知识

/*******************拷贝构造函数和赋值运算符重载有以下两个不同之处***************************/

 

1.拷贝构造函数生成新的类对象,而赋值运算符不能。

2.由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新对象相同,而复制操作符需要这个操作,另外赋值运算符中如果原来对象中有内存分配,要先把内存释放掉。

下面是String类的一个实现的部分函数,可以看出二者的区别。

 1 class String{
 2 public:
 3     String(const char * str = NULL);
 4     String(const String& other);
 5     ~String();
 6     String& operator=(const String& other);
 7 private:
 8     char *m_data;
 9 };
10 
11 String::String(const char * str){
12     if (str != NULL){
13         int length = strlen(str);
14         m_data = new char[length + 1];
15         strcpy(m_data, str);
16     }
17 }
18 
19 String::~String(){
20     delete m_data;
21 }
22 
23 String::String(const String& other){
24     int length = strlen(other.m_data);
25     m_data = new char[length + 1];
26     assert(m_data != NULL);
27     strcpy(m_data, other.m_data);
28 }
29 
30 String& String::operator=(const String& other){
31     if (this == &other){                 //这里要判断是否自我赋值
32         return *this;
33     }
34     if (m_data != NULL){                 //要检验是否有内存分配
35         delete m_data;
36     }
37     int length = strlen(other.m_data);
38     m_data = new char[length + 1];
39     assert(m_data == NULL);
40     strcpy(m_data, other.m_data);
41     return *this;
42 }

 

/**************一种调用拷贝构造函数和赋值函数的微妙差别*******************/

 

#include<iostream>
#include<assert.h>
using namespace std;

class B{
public:
    B():data(0){
        cout << "default constructor" << endl;
    }
    B(int i):data(i){
        cout << "constructed by parameter " << data << endl;
    }
    B (B &b){
        data = b.data;
        cout << "copyed by parameter " << data << endl;
    }
    B & operator=(const B& b){
        this->data = b.data;
        cout << "=     by parameter " << data << endl;
        return *this;
    }
private:
    int data;
};

void test(){
    B b1;
    B b2 = b1;
    B b3;
    b3 = b1;
}
int main(){

  test();
system("pause"); return 0; }

test()函数和system("pause")是为了不退出main函数,可以看到析构函数执行。运行之后得到以下结果。

default constructor
copyed by parameter 0
default constructor
=     by parameter 0

注意仔细看test函数里的代码段。

b2调用的是拷贝构造函数,而b3调用的是赋值函数。这两者是不同的。

 

/*********************关于拷贝构造函数和赋值函数的临时对象问题******************************/

 

看以下代码,它的输出会是什么。

 1 #include<iostream>
 2 #include<assert.h>
 3 using namespace std;
 4 
 5 class B{
 6 public:
 7     B():data(0){
 8         cout << "default constructor" << endl;
 9     }
10     ~B(){
11         cout << "destructed by parameter " << data << endl;
12     }
13     B(int i):data(i){
14         cout << "constructed by parameter " << data << endl;
15     }
16     B (B &b){
17         data = b.data;
18         cout << "copyed by parameter " << data << endl;
19     }
20     B & operator=(const B& b){
21         this->data = b.data;
22         cout << "=     by parameter " << data << endl;
23         return *this;
24     }
25 private:
26     int data;
27 };
28 
29 B play(B b){
30     return b;
31 }
32 
33 void test(){
34     play(1);
35     B t1 = play(2);
36     B t2;
37     t2 = play(3);
38 }
39 
40 int main(){
41 
42     test();
43     system("pause");
44     return 0;
45 }


这个程序比上一个增加了一个play()函数和析构函数的输出。看到输出结果后,有一些疑惑。以下为输出结果,为方便起见,给它们编号。

(1)constructed by parameter 1                                            
(2)copyed by parameter 1
(3)destructed by parameter 1
(4)destructed by parameter 1
(5)constructed by parameter 2
(6)copyed by parameter 2
(7)destructed by parameter 2
(8)default constructor
(9)constructed by parameter 3
(10)copyed by parameter 3
(11)destructed by parameter 3
(12)=     by parameter 3
(13)destructed by parameter 3
(14)destructed by parameter 3
(15)destructed by parameter 2

如果有疑问,可以先了解下面三点。

 

1.用同一个类的源对象构造一个目标对象是,会调用拷贝构造函数来构造目标对象,如果没有定义拷贝构造函数,将会调用默认的拷贝函数来构造目标对象。

2.当类有一个带有一个参数的构造函数时,可以用这个参数同类型的数据初始化这个对象,默认会调用这个构造函数。

3.当一个函数的返回值为一个类的对象时,如果在调用函数中(注意是调用函数,不是被调用函数),没有定义一个对象来接收这个返回值,会用返回一个临时对象保存返回对象的值。在被调用函数(注意是被调用函数)结束时,这个临时对象被销毁。而当有一个接收对象时,就将返回对象赋值给接收对象,这个返回对象在调用函数(注意是调用函数)结束时调用析构函数。

 

第一点体现在程序35行,上面讲过,这是会调用拷贝构造函数而不是赋值函数。

第二点体现在34行,play(1)的类型是整型,而类B中有一个带int类型的构造函数,当实参1传给形参b,会调用这个构造函数。

第三点体现在34行和37行。play(1)函数被调用,返回一个类对象,而此时没有对象接收(在左边接受赋值),所以会返回一个临时对象,而这个临时对象在被调用函数play结束时调用析构函数销毁,输出(4)destructed by parameter 1;t2 = play(3);语句中play(3)被调用,返回一个对象,而此时有对象(t2)接收,所以调用赋值函数赋值给t2,在调用函数(test)结束时,这个对象才被销毁输出(13)destructed by parameter 3。

 

所以,上面的输出的含义分别是:

constructed by parameter 1                  //用1构造参数b
copyed by parameter 1                        //用b构造一个临时对象
destructed by parameter 1                   //参数b被析构
destructed by parameter 1                   //临时对象被析构
constructed by parameter 2                 //用2构造参数b
copyed by parameter 2                        //用b构造t1
destructed by parameter 2                   //参数b被析构
default constructor                             //构造t2
constructed by parameter 3                 //用3构造参数b
copyed by parameter 3                       //用b拷贝一个临时对象
destructed by parameter 3                  //参数b被析构
=     by parameter 3                           //调用赋值函数=()初始化t2
destructed by parameter 3                 //临时对象被析构
destructed by parameter 3                 //t2被析构
destructed by parameter 2                 //t1被析构

posted @ 2014-08-08 01:27  树上的猪  阅读(875)  评论(0编辑  收藏  举报