程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 拷貝構造函數和賦值函數的一些知識,拷貝構造函數賦值

拷貝構造函數和賦值函數的一些知識,拷貝構造函數賦值

編輯:C++入門知識

拷貝構造函數和賦值函數的一些知識,拷貝構造函數賦值


/*******************拷貝構造函數和賦值運算符重載有以下兩個不同之處***************************/

 

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被析構


拷貝構造函數、構造函數與賦值語句的不同

拷貝構造函數和賦值函數的區別 blog.csdn.net/...804908
拷貝構造函數、構造函數的區別 blogold.chinaunix.net/u3/94667/showart_2244350.html
 

C++中的賦值函數是為何?為何構造函數可以後多個?

呵呵,一般C++的書上都會介紹的基礎知識。
構造函數有這麼幾種:我以類名為A的類來說說吧
1. 默認構造函數,新建A的對象時默認調用(如果沒有其他符合的構造函數的話)
2. 拷貝構造函數,用A的一個對象來對另一個A類對象進行初始化
4. 賦值函數,用於A對象間的賦值操作
5. 其他構造函數,自己定義。

以上1~3的3個構造函數是每個C++類默認內置的,即使你不聲明也會存在的。
下面舉個例子
class A{
public:
A(){} // 默認構造函數,什麼都不做
A(int i) {m_i=i;} // 自定義構造函數,用int型變量i來初始化A類對象成員m_i
A(A& a) {m_i=a.m_i;} // 拷貝構造函數,一般不需要特別重載,功能是將形參對象的內部成員全部拷貝到新對象中。
A& operator=(const A& a); // 賦值函數,其實不算是構造函數,只是運算符重載而已。用形參對象來對當前對象進行賦值
A& operator=(const int i); // 另一個重載的賦值函數,用i來對A類對象賦值
~A(){}
public:
int m_i;
};

A& A::operator=(const A& a)
{
this.m_i=a.m_i;
return *this;
}

A& A::operator=(const int i)
{
this.m_i=i;
return *this;
}

下面是應用的例子
A a; // 調用默認構造函數
a.m_i=100; // 外部對a的成員進行賦值
A b(a); // 調用拷貝構造函數,用a對象來對b對象初始化,此時b對象的m_i也變成了100
A c;
c=b; // 調用賦值函數,用對象b來對c進行賦值。默認的賦值函數功能類似與拷貝構造函數
c=200; // 調用自定義賦值函數,用200來對c對象的m_i進行賦值
A d(150); // 調用了自定義的構造函數,用150對d對象的m_i進行賦值

以上是本人臨時亂寫的代碼,水平有限,不當之處請各位大俠批評指正。
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved