構造函數constructor 與析構函數destructor(四)

拷貝構造函數:拷貝構造函數就是在用一個類對象來建立另一個類對象時被調用的構造函數,若是咱們沒有顯示的提供拷貝構造函數,編譯器會隱式的提供一個默認拷貝構造函數。ios

拷貝構造函數的定義是X(const X& ){}函數

1 class Test{
2    int m_i;
3 public:
4     Test(int i):m_i(i){}
5      Test(const Test& vt):m_i(vt.m_i){}//拷貝構造函數
int getI()const {return m_i;}
6 7 };
1 int main(){
2 
3    Test t(12);
4     Test t2(t);//調用拷貝構造函數來初始化t2對象
5     Test t3=t;//等價於t3(t),也是調用拷貝構造函數
6     return 07 }

記住拷貝構造函數的只有一個參數,而且這個參數是類類型的const引用,參數不能是普通的值傳遞,必須是引用。緣由有二測試

一:若是參數是const Test vt,那麼實參在傳遞給形參的時候,仍是會給形參建立對象,分配內存。這時把實參傳遞給形參,仍然是是用類對象初始化類對象,仍是會調用拷貝構造函數,就會造成遞歸調用。spa

二:若是傳遞引用,其實是傳遞的一個地址,不會建立新的對象,而拷貝時也只是拷貝的一個地址大小的空間,會增長程序的效率。code

 

 

 

當函數的形參是類的對象,調用函數時,進行形參與實參結合時使用。這時要在內存新創建一個局部對象,並把實參拷貝到新的對象中。理所固然也調用拷貝構造函數。對象

1 void fun(Test vt){
2 
3     cout<<vt.,getI()<<endl;
4 
5 }

這個函數的參數是類的對象,會調用拷貝構造函數的blog

1 int main(){
2 
3     Test t(12);
4     Test t2 = t;//調用一次拷貝構造函數
5     Test t3(t);//調用一次拷貝構造函數
6     fun(t);//調用拷貝構造函數
7     return 0;
8 }

fun的參數變成對象的引用時,就不會再調用拷貝構造函數了。遞歸

 

返回值爲Const 引用 以及類對象的區別生命週期

當函數的返回值是類對象,函數執行完成,返回到調用者時也會調用拷貝構造函數。理由也是要創建一個臨時對象中,從函數返回的對象來初始化產生的臨時對象。內存

 1 //test.h
 2 #ifndef TEST_H
 3 #define TEST_H
 4 class Test
 5 {
 6     int m_i;
 7 public:
 8     Test(int i=0);
 9     Test(const Test& vt);
10     ~Test();
11     int getI()const;
12 };
13 #endif //TEST_H
14 
15 
16 //test.cpp
17 #include "Test.h"
18 #include<iostream>
19 using std::cout;
20 using std::endl;
21 
22 Test::Test(int i) :m_i(i)
23 {
24     cout << "default constructor" << endl;
25 }
26 
27 Test::Test(const Test& vt):m_i(vt.m_i){
28     cout << "copy constructor" << endl;
29 }
30 
31 Test::~Test()
32 {
33 }
34 
35 int Test::getI()const{
36     return m_i;
37 }
38 
39 //demo.cpp
40 
41 #include<iostream>
42 #include"Test.h"
43 using std::endl;
44 using std::cout;
45 void fun1(Test& vt){
46 
47     cout << "m_i=" << vt.getI() << endl;
48 }
49 void fun2(Test vt){
50     cout << "m_i=" << vt.getI() << endl;
51 }
52 
53 Test fun3(const Test& vt){
54     return vt;
55 }
56 int main(){
57 
58     Test t;
59     fun3(t);//由於fun3函數的參數是類的引用,所以不用產生臨時對象。可是返回值類型爲類對象會產生臨時對象,
60            // 因此要調用一個拷貝構造函數,把從函數返回的值,拷貝到生成的臨時對象中
61     return 0;
62 }

 

 

咱們還能夠在析構函數中添加代碼,來測試臨時對象時何時釋放的。

在析構函數~Test()中添加打印代碼,main函數以下所示:

1 int main(){
2 
3     Test t;
4     fun3(t);//由於fun3函數的參數是類的引用,所以不用產生臨時對象。可是返回值類型爲類對象會產生臨時對象,
5            // 因此要調用一個拷貝構造函數,把從函數返回的值,拷貝到生成的臨時對象中
6     cout << "........." << endl;
7     return 0;
8 }

咱們能夠看到,臨時對象產生後,若是沒有別的對象接受,立刻就銷燬。若是有別的對象接受,再複製完成後銷燬。可是若是mia函數中添加一句以下的代碼

1 int main(){
2 
3     Test t;
4     Test t2=fun3(t);//由於fun3函數的參數是類的引用,所以不用產生臨時對象。可是返回值類型爲類對象會產生臨時對象,
5            // 因此要調用一個拷貝構造函數,把從函數返回的值,拷貝到生成的臨時對象中
6     cout << "........." << endl;
7     return 0;
8 }

 

此時臨時對象被Test t2接受,就沒有立刻釋放,而是當t2的生命週期完時釋放。

 

 

如今增長一個函數fun4,它的返回值類型和傳遞的參數類型都是類的引用

1 const Test& fun4(const Test& vt){
2      return vt;
3 }

而後把main的代碼修改爲以下的;

1 int main(){
2 
3     Test t;//建立t對象,調用默認構造函數
4     const Test& t1=fun4(t);//傳參,和返回值都沒有產生臨時對象。
5     cout << "........." << endl;
6     return 0;
7 }

運行結果

咱們能夠看到,當返回值爲類的引用時,並不會產生臨時對象。這是一個很是好的改變程序效率的方法。

 

 

因此從上面的一系列代碼中,咱們能夠看到,傳參和返回值類型會影響程序的效率,由於傳參類型是值類型的話,會產生臨時對象,臨時對象的產生,釋放,拷貝,都會產生消耗,從而致使程序效率下降。

相關文章
相關標籤/搜索