C++——this指針和析構函數


this指針和常成員函數

this指針

  1. 類中的構造函數和成員函數都隱藏了一個該類類型的指針參數,名爲this;在構造函數或者成員訪問類中的其它成員時,本質都是通過this指針實現的。
    對於構造函數,this指針指向正在創建的對象;
    對於成員函數,this指針指向該函數的調用對象。
  2. 顯示使用this指針的場景
    • 區分作用域;
    • 從成員函數返回調用對象自身;
    • 從類的內部銷燬對象的自身;
    • 作爲函數的實參,實現對象間的交互。

代碼示例

  • this.cpp
#include <iostream>
using namespace std;

class User{
public:
    /*User(const string& name,int age)
        :m_name(name),m_age(age){
        cout << "構造函數:" << this << endl;
    }*/
    //使用this指針區分成員變量和參數變量
    User(const string& m_name,int m_age){
        this->m_name = m_name;
        this->m_age = m_age;
    }
    void print(void){
        cout << "我叫" << this->m_name << 
          ",今年" << this->m_age << "歲" 
          << endl;
    }/*編譯器編譯以後:
    void print(User* this){
        cout << this->m_name << .. 
            << this->m_age << .. 
    }*/
private:
    string m_name;
    int m_age;
};
int main(void)
{
    User u1("張三",22);
    cout << "&u1=" << &u1 << endl;
    
    User u2("李四",23);
    cout << "&u2=" << &u2 << endl;

    u1.print();//User::print(&u1);
    u2.print();//User::print(&u2);
    return 0;
}
  • 執行結果
    在這裏插入圖片描述* 02this.cpp
#include <iostream>
using namespace std;
class Counter{
public:
    Counter(int count=0):m_count(count){}
    Counter& add(void){
        ++m_count;
        //this指向調用對象
        //*this就是調用對象自身
        return *this;//返回自引用
    }
    void destroy(void){
        //...
        cout << "this=" << this << endl;
        delete this;//對象自銷燬
    }
    int m_count;
};
int main(void)
{
    Counter cn;
    cn.add().add().add();
    cout << cn.m_count << endl;//3

    Counter* pcn = new Counter;
    //...
    cout << "pcn=" << pcn << endl;
    //delete pcn;
    pcn->destroy();

    return 0;
}
  • 執行結果
    在這裏插入圖片描述* 03this.cpp
#include <iostream>
using namespace std;

class Student;//短視聲明
class Teacher{
public:
    //向參數表示的學生對象提問
    void educate(Student* s);
    //獲取學生對象回答的答案
    void reply(const string& answer);
private:
    string m_answer;//保存答案
};
class Student{
public:
    //接收問題,並將答案傳回給教師對象
    void ask(
        const string& question,Teacher* t);
};
void Teacher::educate(Student* s){
    s->ask("什麼是this指針?",this);
    cout << "學生回答:" << m_answer << endl;
}
void Teacher::reply(const string& answer){
    m_answer = answer;
}
void Student::ask(
    const string& question,Teacher* t){
    cout << "問題:" << question << endl;
    t->reply("this指針指向調用對象的地址");
}
int main(void)
{
    Teacher teacher;
    Student student;
    teacher.educate(&student);
    return 0;
}
  • 執行結果
    在這裏插入圖片描述

常成員函數(常函數)

  • 在一個成員函數參數表後面加const修飾,表示這個函數是常成員函數。
    返回類型 函數名(形參表) const{函數體}
  • 常函數中的this指針是一個常指針,不能再常函數中修改成員變量的值。(注:被mutable關鍵字修飾的成員變量,可以在常函數中被修改)
  • 非常對象既可以調用常函數也可以調用非常函數,但是常對象只能調用常函數,不能調用非常函數。(注:常對象也包括常指針或常引用)
  • 函數名和形參相同的成員函數,其常版本和非常版本可以構成重載關係,常對象調用常數版本,非常對象調用非常版本。

代碼示例

  • constfunc.cpp
#include <iostream>
using namespace std;
class A{
public:
    A(int data = 0):m_data(data){}
    void print(void) const {//常函數
        cout << m_data++ << endl;
    }/*
    void print(const A* this){
        cout << this->m_data++ << endl;
    }*/
private:
    mutable int m_data;
};
int main(void)
{
    A a(100);
    a.print();//100
    a.print();//101
    a.print();//102
    return 0;
}
  • 執行結果
    在這裏插入圖片描述
  • 02constfunc.cpp
#include <iostream>
using namespace std;
class A{
public:
    //void func1(const A* this)
    void func1(void) const {
        cout << "常函數" << endl;
    }
    //void func2(A* this)
    void func2(void){
        cout << "非 常函數" << endl;
    }
};
int main(void)
{
    A a;
    a.func1();//A::func1(&a),A*
    a.func2();

    const A a2 = a;
    a2.func1();//A::func1(&a2),const A*
    //a2.func2();//error
    
    const A* pa = &a;//pa常指針
    pa->func1();
    //pa->func2();//error
   
    const A& ra = a;//ra常引用
    ra.func1();
    //ra.func2();//error

    return 0;
}
  • 執行結果
    在這裏插入圖片描述
  • 03constfunc.cpp
#include <iostream>
using namespace std;
class A{
public:
    void func(void)const{//const A* this
        cout << "func常版本" << endl;
    }
    void func(void){//A* this
        cout << "func非常版本" << endl;
    }
};
int main(void)
{
    A a;
    a.func();//非常版本
    const A& ra = a;
    ra.func();//常版本
    return 0;
}
  • 執行結果
    在這裏插入圖片描述

析構函數(Destructor)

  • 語法
  class 類名{
  		~類名(void){
  			負責清理對象生命期內動態分配的資源
  		}
  };
  1)函數名必須是 “~類名”
  2)沒有返回類型,也沒有參數
  3)不能被重載,一個類只能有一個析構函數
  • 當對象被銷燬時,該對象的析構函數自動被調用和執行;
    • 1)棧對象離開作用域時,其析構函數被作用域終止右花括號“}”調用;
    • 2)堆對象的析構函數被delete操作符調用。
  • 如果一個類沒用顯示定義析構函數,那麼編譯器會爲這個類提供一個缺省的析構函數;
    • 1)對基類類型的成員變量,什麼也不做;
    • 2)對類 類型成員變量(成員子對象),會自動調用相應類的析構函數。
  • 對象的創建和銷燬過程
    • 1)對象創建
      –》分配內存
      –》構造成員子對象(按聲明順序)
      –》執行構造函數代碼
    • 2)對象銷燬
      –》執行析構函數代碼
      –》析構成員子對象(按聲明逆序)
      –》釋放內存

代碼示例

  • destructor.cpp
#include <iostream>
using namespace std;
class Integer{
public:
    Integer(int data = 0)
        :m_data(new int(data)){
        //m_data = new int(data);
    }
    void print(void)const{
        cout << *m_data << endl;
    }
    ~Integer(void){
        cout << "Integer的析構函數" << endl;
        delete m_data;
        m_data = NULL;
    }
private:
    int* m_data;
};
int main(void)
{
    if(1){
        Integer i(100);
        i.print();//100
        cout << "test1" << endl;
        Integer* pi = new Integer(200);
        pi->print();//200
        delete pi;//delete-->調用析構
        cout << "test3" << endl;
    }//}-->調用析構函數
    cout << "test2" << endl;
    return 0;
}
  • 執行結果
    在這裏插入圖片描述

  • 02destructor.cpp

#include <iostream>
using namespace std;
class A{
public:
    A(void){
        cout << "A::A(void)" << endl;
    }
    ~A(void){
        cout << "A::~A(void)" << endl;
    }
};
class B{
public:
    B(void){
        cout << "B::B(void)" << endl;
    }
    ~B(void){
        cout << "B::~B(void)" << endl;
    }
    A m_a;//成員子對象
};
int main(void)
{
    B b;
    return 0;
}
  • 執行結果
    在這裏插入圖片描述

拷貝構造和拷貝賦值

淺拷貝和深拷貝

  • 如果一個類中包含指針形式的成員變量,缺省的拷貝構造函數只是複製指針變量本身,而沒用複製該指針所指向的內容,這種拷貝方式稱爲淺拷貝。
  • 淺拷貝將導致不同對象之間的數據共享,如果數據在堆區,析構時還可能發送“double free”,導致進程的終止,這時需要定義一個支持複製指針指向內容的拷貝構造函數,即深拷貝。

拷貝賦值

  • 當兩個對象進行賦值操作時,比如“i3 = i2”,編譯器會將其自動翻譯成 i3.operator=(i2)成員函數的調用形式,其中“operator=”稱爲拷貝賦值操作符函數,通過它實現兩個對象的賦值操作,該函數的返回結果就是表達式結果。
  • 但是編譯器缺省提供的拷貝賦值函數和缺省的拷貝構造函數類似,也就是淺拷貝,只是複製了指針變量本身,沒用複製指針所指向的內容,有“double free”和內存泄漏的風險。
  • 爲了得到深拷貝的效果,避免錯誤,必須自己定義一個支持深拷貝的拷貝賦值函數。
類名& operator=(const 類名& that){
		if(&that != this){//防止自賦值
			釋放舊內存;
			分配新內存;
			拷貝新數據;
		}
		return *this;//返回自引用
	}
	this指向調用對象(左操作數)
	that對應參數對象(右操作數)

代碼示例

  • copy.cpp
#include <iostream>
using namespace std;
class Integer{
public:
    Integer(int data = 0)
        :m_data(new int(data)){
        //m_data = new int(data);
    }
    void print(void)const{
        cout << *m_data << endl;
    }
    ~Integer(void){
        cout << "Integer的析構函數" << endl;
        delete m_data;
        m_data = NULL;
    }
    /*編譯器提供的缺省拷貝構造函數(淺拷貝)*/
    /*Integer(const Integer& that){
        cout << "缺省的拷貝構造" << endl;
        m_data = that.m_data;
    }*/
    /*自定義深拷貝構造函數*/
    Integer(const Integer& that){
        cout << "自定義深拷貝" << endl;
        //m_data = new int;
        //*m_data = *that.m_data;
        m_data = new int(*that.m_data);
    }
private:
    int* m_data;
};
int main(void)
{
    Integer i1(100);
    Integer i2 = i1;//拷貝構造
    i1.print();//100
    i2.print();//100
    return 0;
}
  • 執行結果
    在這裏插入圖片描述
  • 實現string類的基本,string.cpp
#include <iostream>
#include <cstring>
using namespace std;
class String{
public:
    //構造函數
    String(const char* str){
        m_str = new char[strlen(str)+1];
        strcpy(m_str,str);
    }
    //練習:析構函數,拷貝構造
    ~String(void){
        delete[] m_str;
        m_str = NULL;
    }
    String(const String& that){
        m_str = 
            new char[strlen(that.m_str)+1];
        strcpy(m_str,that.m_str);
    }
    const char* c_str(void)const{
        return m_str;
    }
private:
    char* m_str;
};
int main(void)
{
    String s("hello");
    cout << s.c_str() << endl;//hello
    String s2 = s;
    cout << s2.c_str() << endl;//hello

    return 0;
}
  • 執行結果
    在這裏插入圖片描述
  • 拷貝賦值,copy.cpp
#include <iostream>
using namespace std;
class Integer{
public:
    Integer(int data = 0)
        :m_data(new int(data)){
        //m_data = new int(data);
    }
    void print(void)const{
        cout << *m_data << endl;
    }
    ~Integer(void){
        cout << "Integer的析構函數" << endl;
        delete m_data;
        m_data = NULL;
    }
    /*編譯器提供的缺省拷貝構造函數(淺拷貝)*/
    /*Integer(const Integer& that){
        cout << "缺省的拷貝構造" << endl;
        m_data = that.m_data;
    }*/
    /*自定義深拷貝構造函數*/
    Integer(const Integer& that){
        cout << "自定義深拷貝" << endl;
        //m_data = new int;
        //*m_data = *that.m_data;
        m_data = new int(*that.m_data);
    }
    /*編譯器缺省實現的拷貝賦值操作符函數*/
    //i3 = i2;//i3.operator=(i2)
    /*
    Integer& operator=(const Integer& that){
        cout << "缺省拷貝賦值函數" << endl;
        m_data = that.m_data;
        return *this;
    }*/
    //自定義深拷貝賦值操作符函數
    Integer& operator=(const Integer& that){
        cout << "自定義深拷貝賦值" << endl;
        if(&that != this){
            delete m_data;
            m_data = new int;
            *m_data = *that.m_data;
        }
        return *this;
    }
private:
    int* m_data;
};
int main(void)
{
    Integer i1(100);
    Integer i2(i1);//拷貝構造
    i1.print();//100
    i2.print();//100
    
    Integer i3;

    //i3.operator=(i2);
    i3 = i2;//拷貝賦值
    i3.print();//100
    return 0;
}
  • 執行結果
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章