1. 程式人生 > >Day40、this指標和常函式、解構函式、物件的建立和銷燬過程、拷貝構造和拷貝賦值(深拷貝、淺拷貝!)

Day40、this指標和常函式、解構函式、物件的建立和銷燬過程、拷貝構造和拷貝賦值(深拷貝、淺拷貝!)

一、            this和常函式

1、 this 指標

1)     類中的建構函式和成員函式都隱藏一個該類型別的指標引數,引數名為this。

2)     對於普通的成員函式,this指標就是指向呼叫該函式的物件

3)     對於建構函式,this指標指向正在被構造的物件

舉例:

  1#include<iostream>

  2using namespace std;

  3class User{

  4public:

 5     User(const string&name,int age):

 6     m_name(name),m_age(age){

 7        cout<<"A::A()"<<this<<endl;

 8     }

 9     //void print(User* this){

 10    void print(void){

 11        cout<<m_name<<','<<m_age<<endl;

 12        cout<<this->m_name<<','<<this->m_age<<endl;

 13        //this指標指向呼叫物件

 14        cout<<"this="<<this<<endl;

 15     }

 16private:

 17    string m_name;

 18    int m_age;

 19};

 20int main(void){

 21    User user1("張飛",25);

 22    cout<<"&user1:"<<&user1<<endl;

 23    user1.print();//print(&user1)

24     return 0;

 25 }

2、 必須使用this指標的地方

1) 區分作用域

  1#include<iostream>

  2using namespace std;

  3class User{

  4public:

 5     //構造形參和成員變數一樣的名字,通過this區分

 6     User(const string&name,int age){

 7         this->name=name;

 8         this->age=age;

 9     }

 10    void print(void){

 11        cout<<name<<","<<age<<endl;

 12     }

 13private:

 14    string name;

 15    int age;

 16};

 17int main(){

 18    User user("tangzihao",24);

 19    user.print();

 20    return 0;

 21 }

2)  從成員函式中返回物件自身 

1 #include<iostream>

  2using namespace std;

  3class counter{

  4public:

 5     counter(intdata=0):m_data(data){}

 6     counter&add(void/*counter* this*/){

 7         ++m_data;

 8         return *this;//返回自引用

 9     }

13     int m_data;

 14};

 15int main(){

 16    counter cn;

 17    cn.add().add().add();//add(&cn)

 18    cout<<cn.m_data<<endl;//3

21     return 0;

 22 }

3) 從類的內部銷燬該物件自身-----物件自毀

  1#include<iostream>

  2using namespace std;

  3class counter{

  4public:

 5     counter(intdata=0):m_data(data){}

 6     counter&add(void/*counter* this*/){

 7         ++m_data;

 8         return *this;//返回自引用

 9     }

 10    void destory(void){

 11        delete this;

 12     }

 13    int m_data;

 14};

 15int main(){

 16    counter cn;

 17    cn.add().add().add();//add(&cn)

 18    cout<<cn.m_data<<endl;//3

 19    counter* pcn=new counter;

 20    pcn->destory();//destory(pcn)

 21    return 0;

 22 }

4)作為函式的實參,實現物件間資料互動(瞭解)

  1#include<iostream>

  2using namespace std;

  3class student;//短視宣告

  4class teacher{

  5public:

 6     void educate(student*student);

 7     void reply(const string&answer){

 8         m_answer=answer;

 9     }

 10private:

 11    string m_answer;//儲存答案

 12};

 13class student{

 14public:

 15    void ask(const string& question,teacher* t){

 16        cout<<"問題:"<<question<<endl;

 17        t->reply("this指標指向呼叫物件的地址");

 18     }

 19};

 20void teacher::educate(student* student){

 21    student->ask("什麼是this指標",this);

 22    cout<<"學生回答"<<m_answer<<endl;

 23}  

 24int main(void){

 25    teacher tch;

 26    student sdt;

 27    tch.educate(&sdt);

 28    return 0;

 29 }

3、常函式

1)在一個普通的成員函式引數表後面加上const,這個成員函式稱為常函式

返回型別 函式名(形參表)const {函式體}

2)常函式中的this指標是一個常指標,不能在常函式中修改成員變數的值

  1#include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     A(int data=0):m_data(data){}

 6     /*如果定義一個成員函式,裡面不需要修改成員變數,只是訪問,那麼應該宣告為    常函式*/

 7     void show(void)const{//constA* this

 8        cout<<m_data/*++*/<<endl;// 不能修改成員變數,如果修改會報錯

 9     }

 10private:

 11    int m_data;

 12};

 13int main(void){

 14     Aa(123456);

 15    a.show();

16     a.show();

 17     return 0;

 18 }

123456

123456

3)mutable關鍵字:修飾的成員變數可以在常函式中修改

  1#include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     A(int data=0):m_data(data){}

 6     void show(void)const{//constA* this

 7        /* cout<<

 8            const_cast<A*>(this)->m_data++<<endl;*/

  9/*mutable修飾的成員變數在常函式中使用時會自動做常量轉換,和上句意思一樣*/

 10        cout<<m_data++<<endl;

 11     }

 12private:

 13    mutable int m_data;

 14};

 15int main(void){

 16     Aa(123456);

 17    a.show();

 18    a.show();

 19    return 0;

 20 }

[email protected]:~/day40$./a.out

123456

123457

4) 非常物件既可以呼叫非常函式,也可以呼叫常函式,但是常物件只能呼叫常函式,不能呼叫非常函式

  1#include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     void func1(void)const{//this-->const A*

 6         cout<<"常函式"<<endl;

 7     }

 8     void func2(void){

 9         cout<<"非常函式"<<endl;

 10     }

 11};

 12int main(void){

 13     Aa;

 14    a.func1();//&a-->A

 15    a.func2();

 16    const A a2=a;

 17    //a2是個常物件,常物件只能呼叫常函式,不能呼叫非常函式

 18    a2.func1();//&a2-->const A*

 19    //a2.func2();error  只讀呼叫可讀可寫,擴大範圍,不行

 20    const A* pa=&a;//pa常指標

 21    pa->func1();

 22    //pa->func2();

 23    const A& ra=a;//ra常引用

24     ra.func1();

 25    //ra.fun2();

 26    return 0;

 27 }

[email protected]:~/day40$./a.out

常函式

非常函式

常函式

常函式

常函式

5) 函式名形參表相同的成員函式,其常版本和非常版本可以構成有效的過載關係,常物件呼叫常版本,非常物件呼叫非常版本

   1 #include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     void func(void)const{//this-->const A*

 6         cout<<"常版本"<<endl;

 7     }

 8     void func(void){//this-->A*     //可以構成過載函式

 9         cout<<"非常版本"<<endl;

 10     }

 11};

 12int main(void){

 13     Aa;

 14    a.func();//呼叫非常版本

 15    const A a2=a;

 16    a2.func();

 17    return 0;

 18 }

[email protected]:~/day40$./a.out

非常版本

常版本

二、            解構函式(Destructor)

建立物件的時候呼叫建構函式

1、 解構函式和建構函式形式類似,是類中特殊的成員函式

class 類名{

       ~類名(void){….}

}

1)函式名必須是:~類名

2)沒有返回型別,也沒有引數,所以不能被過載,一個類只能有一個解構函式。

3)主要負責清理物件生命週期中產生的動態資源

(如果用銷燬new的成員變數比較麻煩,萬一忘記,會造成記憶體洩露,解構函式自動釋放new出來的)

  1#include<iostream>

  2using namespace std;

  3class integer{

  4public:

 5     integer(intdata=0):m_data(new int(data)){}

 6     ~integer(void){

 7         cout<<"integer::~integer()"<<endl;

 8         delete m_data;

 9         m_data=NULL;

 10     }

 11    int get(void)const{

 12        return *m_data;

 13     }

 14private:

 15        int* m_data;

 16};

 17int main(void){

 18    integer i(100);

 19     cout<<i.get()<<endl;//100

 20    return 0;

 21 }

2、 當物件被銷燬時,該物件的解構函式將自動被執行

(建構函式:當物件被建立時,該物件的建構函式將自動被呼叫)

1) 棧物件(區域性變數)當其離開作用域時,其解構函式被作用域終止運算子“}”呼叫

2) 堆物件(new出來的)的解構函式被delete運算子呼叫

  1#include<iostream>

  2using namespace std;

  3class A{

 4     public:

 5         ~A(void){

 6            cout<<"A::A~()"<<endl;

 7         }

  8};

  9int main(void){

 10     {

 11        A a;

 12        cout<<"test1"<<endl;

 13        A* pa=new A;

 14        delete pa;// delete -->A::~A()

 15        cout<<"test3"<<endl;

 16     }// } -->A::~A()  物件的作用域結束時呼叫解構函式

 17    cout<<"test2"<<endl;

 18    return 0;

 19 }

test1

A::A~()

test3

A::A~()

test2

3、 如果一個類沒有顯式定義解構函式,那麼系統會為該類提供一個預設解構函式

1)對基本型別的成員變數,什麼都不做

2)對類型別的成員變數,呼叫相應型別的解構函式,析構成員子物件

  1#include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     A(void){

 6        cout<<"A::A()"<<endl;

 7     }

 8     ~A(void){

 9        cout<<"A::~A()"<<endl;

 10     }

 11};

 12class B{

 13public:

 14    B(void){

 15        cout<<"B::B()"<<endl;

 16     }

 17     ~B(void){

 18        cout<<"B::~B()"<<endl;

 19     }

 20     Am_a;

 21}; 

 22int main(void){

 23     Bb;

24     return 0;

 25 }

[email protected]:~/day40$./a.out

A::A()

B::B()

B::~B()

A::~A()

4、 物件的建立和銷燬過程

1) 物件的建立

-à為物件分配記憶體空間

-à依次呼叫類型別成員變數的建構函式,構造成員子物件

à執行建構函式體程式碼

2) 物件的銷燬

-à執行解構函式的程式碼、

-à呼叫成員子物件的解構函式

-à釋放物件所佔的記憶體

(以上程式碼)

三、            拷貝構造和拷貝賦值

A a1;

A a2(a1); // 拷貝構造

A  a3;

a3=a1;// 拷貝賦值

1、淺拷貝和深拷貝

如果一個包含指標形式的成員變數,預設的拷貝建構函式只是賦值指標成員變數本身(拷貝地址),而沒有賦值該指標所指向的內容,這種拷貝方式稱為淺拷貝

淺拷貝將導致不同物件間資料共享(多個物件指向同一塊記憶體中的值),如果資料是在堆區,會在析構時引發“doublefree”異常,因此就必須自己定義一個支援複製指標所指向內容的拷貝建構函式,即深拷貝

  1#include<iostream>

  2using namespace std;

  3class Integer{

  4public:

 5     Integer(intdata=0):m_data(new int(data)){} //預設建構函式

 6    /* 預設拷貝建構函式,淺拷貝 error//(拷地址)

 7     Integer(const Integer&that):

 8         m_data(that.m_data){}*/

 9     Integer(const Integer&that):

 10        m_data(new int(*that.m_data)){}//深拷貝:(分配地址拷資料)

 11    ~Integer(void){

 12        delete m_data;

 13        m_data=NULL;

 14     }

 15    int get(void)const{

 16        return *m_data;

 17     }

 18private:

 19    int* m_data;

 20};

 21int main(void){

 22    Integer i1(100);

 23    Integer i2(i1);//拷貝構造

24    cout<<i1.get()<<endl;//100

 25    cout<<i2.get()<<endl;//100

 26    return 0;

 27 }

[email protected]:~/day40$./a.out

100

100

2、 類的預設拷貝賦值和預設拷貝構造一樣,都是淺拷貝,為了得到深拷貝的效果,必須自己定義一個支援深拷貝的拷貝賦值運算子函式。

1) 防止自賦值

2) 釋放舊資源

3) 分配新資源

4) 賦值新內容

5) 返回自引用

拷貝構造和拷貝賦值程式碼:

#include<iostream>

using namespace std;

class Integer{

public:

   Integer(int data=0):m_data(new int(data)){}

   /*預設拷貝建構函式,淺拷貝 error//(拷地址)

   Integer(const Integer& that):

       m_data(that.m_data){} */

   Integer(const Integer& that):

       m_data(new int(*that.m_data)){}//深拷貝:(分配地址拷資料)

   /*編譯器會自動新增一個拷貝賦值函式*/

   /*預設拷貝賦值一樣也是淺拷貝*/

   /*Integer& operator=(const Integer& that){

       cout<<"預設拷貝賦值運算子函式"<<endl;

       m_data=that.m_data;

       return *this;

   }*/

   /*預設的淺拷貝會引發doublefree,所以自己定義深拷貝賦值*/

   Integer& operator=(const Integer& that){//深拷貝

       if(&that!=this){//地址比較 防止自賦值

           delete m_data;//釋放舊資源

           //分配新資源

           //拷貝新資料

           m_data=new int(*that.m_data);

       }

       return *this;//返回自引用

    }

   ~Integer(void){

       delete m_data;

       m_data=NULL;

    }

   int get(void)const{

       return *m_data;

    }

private:

   int* m_data;

};

int main(void){

   Integer i1(100);

   Integer i2(i1);//拷貝構造

   cout<<i1.get()<<endl; //100

   cout<<i2.get()<<endl; //100

   Integer i3;

   i3=i2;//拷貝賦值:i3.operator=(i2);operator是左值

   cout<<i3.get()<<endl; //100

   return 0;

}

練習:實現String(實現庫裡的string類)

class String{

public:

       //建構函式

       //解構函式

       //拷貝構造

       //拷貝賦值

private:

       char*m_str;

}

程式碼:重要!!!手寫

#include<iostream>

#include<cstring>

using namespace std;

class String{

public:

   //建構函式

   String(const char* str=""):

       m_str(strcpy(new char[strlen(str)+1],str)){}

   //解構函式

   ~String(void){

       delete[] m_str;

       m_str=NULL;

    }

   //深拷貝構造

   String(const String& that):

       m_str(strcpy(new char[strlen(that.m_str)+1],that.m_str)){}

   //深拷貝賦值

   String& operator=(const String& that){

       if(&that!=this){//防止自賦值

   /*    小鳥版 */

           delete[] m_str;//釋放舊資源

           m_str=new char[strlen(that.m_str)+1];//分配新資源

           strcpy(m_str,that.m_str);//賦值新內容*/

           //上面方法如果new失敗不好處理         

   /*大鳥版    */

   /*      char* str=newchar[strlen(that.m_str)+1];//分配新資源

           delete[] m_str; //釋放舊資源

           m_str=strcpy(str,that.m_str); //賦值新內容   */  

   /*老鳥版:*/

   /*      String temp(that);

           swap(m_str,temp.m_str);    */

           }                               

           return *this;   

    }

   const char* c_str(void)const{

       return m_str;

    }

private:

   char* m_str;

};

int main(void){

   String s1("hello world!");

   cout<<s1.c_str()<<endl;//hello world!

   String s2=s1;//拷貝構造

   cout<<s2.c_str()<<endl;//hello world!

   String s3("hello C++!");

   s2=s3;//拷貝賦值

   cout<<s2.c_str()<<endl;//hello C++!

   return 0;

}

[email protected]:~/day40$./a.out

hello world!

hello world!

hello C++!


相關推薦

Day40this指標函式函式物件建立銷燬過程拷貝構造拷貝拷貝拷貝

一、            this和常函式 1、 this 指標 1)     類中的建構函式和成員函式都隱藏一個該類型別的指標引數,引數名為this。 2)     對於普通的成員函式,this指標就是指向呼叫該函式的物件 3)     對於建構函式,this指標指向正

函式為什麼不能宣告為虛擬函式函式為什麼要宣告為虛擬函式

多型中的虛擬函式表是在執行時建立的還是編譯時建立的? 答:虛擬函式表在編譯的時候就確定了,而類物件的虛擬函式指標vptr是在執行階段確定的,這是實現多型的關鍵 (類的函式的呼叫並不是在編譯時就確定的,而是在執行時才確定的,由於編寫程式碼的時候並不能確定被呼叫的是基類的函式還是哪個派生類的函式,所以宣告為虛

C++ 虛函式函式的區別

先看下面程式碼: class Parent { public: Parent(); ~Parent(); }; Parent::Parent() { cout << "基類構造...." << endl << endl; } Parent::

建構函式拷貝建構函式函式函式

例一: class CGoods {     //行為,成員方法 public:     CGoods(char *name = NULL, int amount = 0, double price = 0.0) //建構函式 &nbs

C++建構函式函式

C++(建構函式與解構函式) 1. 建構函式 用於對類的物件的初始化,建構函式名與類名相同。 可在類內直接定義,也可在類內宣告類外定義(定義時在函式名前加類名::)。 建構函式無返回值型別。 class C { public: C(int a,int b);//類

Java的建構函式函式筆記1

已Mydate為例: package text1; public class MyDate { int year; int month; int day; //1.建構函式 public MyDate(int y,int m,int d) { year = y; mon

建構函式函式

建構函式及解構函式是一組特殊的成員函式,用來對物件進行初始化以及回收,這兩個都是與例項物件掛鉤的,當該類被例項物件化時,建構函式就會自動執行,當該類結束時,就會自動進行解構函式,可以說例項物件是以建構函式開始,以解構函式為結束。 建構函式的定義規範是具有與該類相同的名稱,例如程式: na

《C++反彙編與逆向分析技術揭祕》讀書總結——建構函式函式

建構函式的必要條件: 這個函式的呼叫,是這個物件在作用域內的第一次成員函式呼叫,看this指標即可以區分物件,是哪個物件的this指標就是哪個物件的成員函式。 使用thiscall呼叫方式,使用ecx傳遞this指標; 返回值為this指標。 解構函式的必要條件: 這

private建構函式 & 函式

https://www.cnblogs.com/kangyoung/articles/2375211.html   很多情況下要求當前的程式中只有一個object。例如一個程式只有一個和資料庫的連線,只有一個滑鼠的object。通常我們都將建構函式的宣告置於public區段,假如我們將其放入private區

C++知識積累:運算子過載時建構函式函式呼叫次數不一致的問題

在學習運算子過載的時候自己寫了這樣一段程式: class Stu { public: Stu() { std::cout<<"Stu No parameter constructor called!"<<

單繼承派生類建構函式函式順序

派生類建構函式形式: 派生類建構函式 (引數表):基類建構函式(引數表) 類物件成員1(引數表)... 類物件成員n(引數表)//只能用表示式的方式對類物件成員進行初始化 {...派生類自定義的資料成員初始化} 在派生類中,首先呼叫基類的建構函式,其次呼叫

記憶體的分配VS回收&建構函式VS函式

之前有一個問題一直困擾著我,就是一個變量出了作用域,我以為這個變數的記憶體就被回收了,其實不是這樣的,昨天問了一個高手,才豁然開朗,自己在看相關程式碼的反彙編程式碼,才知道原來真是這樣就。這個問題,我想簡單的說一下記憶體的分配VS回收&建構函式VS解構函式之間的關係。 我的疑問:為什麼p出了作用域,

C++:建構函式&函式

建構函式:           C++類的目標之一是讓使用類物件就像使用標準型別一樣,事實上它並不能像初始化int型別那樣初始化類物件,也就是說,常規的初始化語法不適用於類物件。為此,C++提供了一個特殊的成員函式——類建構函式。它是一種特殊的類成員

建構函式函式的呼叫時間

    建構函式與解構函式是被編譯器隱式呼叫的。這些函式的呼叫時間取決於程式執行進入和離開例項化物件的作用域的順序。通常,解構函式的呼叫順序和對應建構函式的呼叫順序相反。但是,物件的儲存類別可以改變解構函式的呼叫順序。    在全域性作用域內定義的建構函式在該檔案中任何其他函

建構函式函式

類的建構函式是在建立類的物件時有系統自動執行的函式,一般把類的初始化工作放在這裡,客戶無法呼叫該函式。  類解構函式是類的物件在銷燬是執行的函式,一些收尾工作放在這裡,比如釋放記憶體等,客戶無法呼叫  

建構函式函式的示例

class Student { public: Student(); Student(int); private: double score, count; };     建構函式是一個很神奇

C/C++面試題:建構函式函式

建構函式與解構函式的一道小題 下面程式的輸出是什麼? #include<iostream> using namespace std; class TestClass{ cha

解析c++建構函式函式

目錄 建構函式的定義 定義:建構函式是一種特殊的成員函式,對物件進行初始化的函式。 建構函式的特點 建構函式名字必須與類名相同。 建構函式不具有任何型別,沒有返回值。 在建立類的物件的時候,系統會自動呼叫建構函式。 如果使用者沒有定義建構函式,系

c++實現String類建構函式函式,複製建構函式,各類運算子過載函式的編寫

編寫類 String 的建構函式,解構函式,複製建構函式  需要過載下面的運算子: 1、<、>、==和!=比較運算子 2、+=連線運算子和賦值運算子 3、<<輸出運算子和>>輸入運算子 String.h #ifndef _STRING

建構函式函式的比較

建構函式的作用:建立並初始化物件,即為物件成員變數賦初始值。 特點: 1、建構函式名與類名相同。 2、建構函式沒有返回值。 3、建構函式不可以被直接呼叫。 4、定義了類沒有定義建構函式時C++