將一個類作為另一個類的成員: 類對象和類指針


1.類對象和類指針的區別

1.1 類對象和類指針的定義

class Data
{
public:
    Data(){cout << "Data::Data()" << endl;}
    ~Data(){}
    void setData(int data) { cout << "Data::setData" << endl; m_iData = data; }
    int getData() { return m_iData; }
private:
    int m_iData;
};


Data a; //實例化一個類對象a
a.setData(1);
 
// Data *b = new Data();
Data *b; //實例化一個類指針b
b =  new Data();
b->setData(2);

1.2 區別

(1)內存:
類對象: Data a;在定義之后就已經為a這個對象分配了內存,且為內存棧,是個局部的臨時變量;
類指針: Data *b = new Data();在定義*b的時候並沒有分配內存,只有執行new后才會分配內存,且為內存堆,是個永久變量,除非你釋放它。

(2)引用成員: 
類對象: 用"."操作符
類指針: 用"->"操作符

(3)多態:
類指針: 是間接訪問,但可實現多態(通過父類指針可調用子類對象),並且沒有調用構造函數。
類對象: 可直接訪問,但不能實現多態,聲明即調用了構造函數(已分配了內存)。
/*------------------------------------------------------------------------------*/
class Data
{
public:
    Data(){ cout << "Data::Data()" << endl; }
    ~Data(){ cout << "Data::~Data()" << endl; }
    void setData(int data) { cout << "Data::setData" << endl; m_iData = data; }
    int getData() { return m_iData; }
private:
    int m_iData;
};

class CData : public Data
{
public:
    CData(){ cout << "CData::CData()" << endl; }
    ~CData(){ cout << "CData::~CData()" << endl; }
    void setData(int data) { cout << "CData::setData" << endl; m_iData = data; }
    int getData() { return m_iData; }
private:
    int m_iData;
};

int main()
{
    /*CData d1;
    調用了構造函數
        Data::Data()
        CData::CData()
    調用了析構函數
        CData::~CData()
        Data::~Data()
    */
    CData d1;
    Data * d2 = &d1; // 通過父類指針可調用子類對象,沒有調用構造函數和析構函數
    return 0;
}
/*------------------------------------------------------------------------------*/
類指針可以指向多個不同的對象,這就是多態

1.3 什么情況使用類對象與類指針?

作用基本一樣 都是為了調用類的成員變量和成員函數;
當你希望明確使用這個類的時候,最好使用對象;
如果你希望使用C++中的動態綁定,則最好使用指針或者引用,指針和引用用起來更靈活,容易實現多態等。

1.4 類對象和類指針聯系

在類的聲明尚未完成的情況下,可以聲明指向該類的指針,但是不可聲明該類的對象;
父類的指針可以指向子類的對象。

1.5 指針與虛函數

要發揮虛函數的強大作用,必須使用指針來訪問對象。

當類是有虛函數的基類,Func是它的一個虛函數,則調用Func時:
        類對象:調用的是它自己的Func;
        類指針:調用的是分配給它空間時那個類的Func。

1.6 指針好處

1. 實現多態。
2. 在函數調用,傳指針參數。不管你的對象或結構參數多么龐大,你用指針,傳過去的就是4個字節。如果用對象,參數傳遞占用的資源就太大了。

2.類對象和類指針作為另一個類的成員

類對象:
1. 首先,要將A類的對象作為B類的成員,你必須在B類聲明前聲明A類。這樣,在B類聲明中,就知道A類是一個類,不然編譯器是不知道A這個字母代表什么,只有在B類聲明前聲明了A類,
B類中就知道A代表的是一個類,是一種自定義類型。要達到這個效果,需要在B類聲明前,包含A類的頭文件,A類的頭文件中就是A類的聲明,有A類的數據成員,也有成員函數等。
2. 其次,就是像聲明一個成員變量一樣,在B類中添加一個成員,如A m_a; 這句表示使用A類聲明了一個B類的成員變量m_a。但是在此,只是對m_a成員變量聲明而已,並沒有分配內存空間,
具體的聲明定義和初始化等,請閱讀《C/C++聲明定義初始化和賦值獨家剖析深刻理解》。
3. 最后,初始化m_a。因為這個成員是一個類對象,因此需要調用構造函數才能初始化。因為這個對象m_a在B類中,m_a對象的構造必然是在B類對象構造之前進行,因此不能在B類構造函數中進行,
又因為m_a不是全局對象,也不能在函數外部。如何解決這個問題呢?這就是C++提出的成員初始化列表,用來解決這類問題的。如果A類提供了默認構造函數,不需要參數的,那就不需要顯式初始化,
讓編譯器自動完成即可,但是有時候需要在構造時就需要傳值,此時就只能通過成員初始化列表。
/*-------------------------------------初始化例子------------------------------------------*/
class Data
{
public:
    Data(int data): m_iData(data){ cout << "Data::Data()" << endl; }
    ~Data(){ cout << "Data::~Data()" << endl; }
    void setData(int data) { cout << "Data::setData" << endl; m_iData = data; }
    int getData() { return m_iData; }
private:
    int m_iData;
};

class TData
{
public:
    TData(): m_cData(5){ cout << "TData::TData()" << endl; }
    ~TData(){ cout << "TData::~TData()" << endl; }
    void printData() { cout << "data = " << m_cData.getData() << endl; }
private:
    Data m_cData;
};


int main()
{
    TData d1;
    d1.printData();
    return 0;
}
結果:
Data::Data()
TData::TData()
data = 5
TData::~TData()
Data::~Data()
注意:
如果Data類的構造函數是無參的,那么在TData類的構造函數初始化列表中無需初始化(不需要顯式初始化)。
/*-------------------------------------初始化例子------------------------------------------*/

類指針:
這種方法比較便捷,省去了初始化的麻煩,且可以動態的創建對象,但是也會帶來內存的使用問題。初學者推薦使用第一種方法。指針的使用,需要一定功底,如果使用不當,
會出現內存訪問違規或者內存泄露問題。指針的深入理解,請參考《指針的深入理解,深刻掌握指針》。

1. 首先,在B類聲明前,包含A類頭文件,這個第一個方法的是一樣的解釋,即使是聲明指針,也要包含,因為要使用A代表一個類,就必須先包含A類頭文件告訴編譯器A是一個類的代名詞而不是簡單的字符。
2. 其次,聲明A類指針作為成員,如A * pA;這樣就可以聲明了。很簡單,這個指針和普通指針一樣大,並不占用很多內存。在很多要動態創建很多對象時特別方便。用完就釋放,需要就創建。
3. 然后,就是初始化指針。在B類構造函數中,初始化時將這個指針設為NULL。這是規范的寫法,之后創建對象后便指向了對象,此時指針就不是NULL了,刪除指針指向的對象后,一定要將指針值設置成NULL。
這樣,要判斷指針是否指向一個對象,就只要判斷指針是否為NULL即可。
4. 最后,在使用時,需要A類對象時,new一個A類對象,然后賦值給pA,這樣pA就指向了new出來的對象,然后都用指針來操作A。用完后,使用delete pA 。

特別說明:為了防止內存泄露,new了對象后,一定要delete這個對象。最容易出現內存泄露的就是頻繁的new對象和delete對象,導致前一個指向的對象沒有刪除就new了一個新對象給指針,
最后之前的對象就無法使用,知道程序結束才能被釋放,這就是內存泄露了。正確的代碼寫法如下:
-----------------代碼-------------------------------
if(NULL != pA)//此處需要初始化時設置指針為空。
{
  delete pA;
  pA = new A;
}  else {
  pA = new A;
}
-----------------代碼-------------------------------

參考:
http://www.voidcn.com/article/p-qdhfbeaz-bmz.html
https://blog.csdn.net/keneyr/article/details/89364275
https://blog.csdn.net/liang841451955/article/details/80266744


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM