白話C++系列(13)-- 對象指針、對象成員指針

對象指針

所謂對象指針,顧名思義就是有一個指針,其指向一個對象,下面經過一個例子來講明這樣一個問題。ios

在這個例子中,咱們定義了一個座標的類(Coordinate),其有兩個數據成員(一個表示橫座標,一個表示縱座標)。當咱們定義了這個類以後,咱們就能夠去實例化它了。若是咱們想在堆中去實例化這個對象呢,就要以下所示:函數

經過new運算符實例化一個對象後(這個對象就會執行它的構造函數),而對象指針p就會指向這個對象。咱們的重點是要說明p與這個對象在內存中的相關位置以及它們之間的對應關係。學習

當咱們經過這樣的方式實例化一個對象後,它的本質就是在內存中分配出一塊空間,在這塊空間中存儲了橫座標(m_iX)和縱座標(m_iY),此時m_iX的地址與p所保存的地址應該是一致的,也就是說p所指向的就是這個對象的第一個元素(m_iX)。若是想用p去訪問這個元素,很簡單,就能夠這樣來訪問(p -> m_iX或者p -> m_iY),也能夠在p前加上*,使這個指針變成一個對象,而後經過點號(.)來訪問相關的數據成員(如(*p).m_iY)。接下來看一下以下的具體範例。spa

注意:這裏的new運算符能夠自動調用對象的構造函數,而C語言中的malloc則只是單純的分配內存而不會自動調用構造函數。3d

對象指針代碼實踐

題目描述:指針

/* 示例要求code

定義Coordinate類對象

    數據成員:m_iX和m_iYblog

    聲明對象指針,並經過指針操控對象內存

 

    計算兩個點,橫、縱座標的和

/* **************************************/

頭文件(Coordinate.h)

class Coordinate
{
public:
    Coordinate();
    ~Coordinate();
public:
    int m_iX;
    int m_iY;
};

源程序(Coordinate.cpp

#include"Coordinate.h"
#include<iostream>

using namespace std;

Coordinate::Coordinate()
{
    cout <<"Coordinate()"<< endl;
}
Coordinate::~Coordinate()
{
    cout <<"~Coordinate()"<< endl;
}

主調程序(demo.cpp

#include"Coordinate.h"
#include<iostream>

#include<stdlib.h>

using namespace std;

int main()
{
    /* 使用兩種方法定義對象指針 */
    Coordinate *p1 = NULL;//定義一個對象指針
    p1 = new Coordinate; //讓p1指向一段內存,這裏也能夠寫成p1 = new Coordinate(),由於其默認構造函數沒有參數
    Coordinate *p2 = new Coordinate();
    
    /* 使用兩種方法讓對象指針訪問數據成員 */
    p1->m_iX = 10;
    p1->m_iY = 20;
    (*p2).m_iX = 30;
    (*p2).m_iY = 40;
    cout << p1->m_iX +(*p2).m_iX << endl;
    cout << p1->m_iY +(*p2).m_iY << endl;
    delete p1;
    p1 = NULL;
    delete p2;
    p2 = NULL;
    system("pause");
    return 0;
}

運行結果:

此外,做爲對象指針來講,還能夠指向棧中的一塊地址,怎麼來作呢?咱們來修改一下主調程序以下:

#include"Coordinate.h"
#include<iostream>
#include<stdlib.h>

using namespace std;

int main()
{
    ///* 使用兩種方法定義對象指針 */
    //Coordinate *p1 = NULL;//定義一個對象指針
    //p1 = new Coordinate; //讓p1指向一段內存,這裏也能夠寫成p1 = new Coordinate(),由於其默認構造函數沒有參數
    //Coordinate *p2 = new Coordinate();
    //
    ///* 使用兩種方法讓對象指針訪問數據成員 */
    //p1->m_iX = 10;
    //p1->m_iY = 20;
    //(*p2).m_iX = 30;
    //(*p2).m_iY = 40;
    //cout << p1->m_iX +(*p2).m_iX << endl;
    //cout << p1->m_iY +(*p2).m_iY << endl;
    //delete p1;
    //p1 = NULL;
    //delete p2;
    //p2 = NULL;

    Coordinate p1; //從棧中實例化一個對象p1
    Coordinate *p2 = &p1; //讓對象指針p2指向p1
    p2->m_iX = 10;
    p2->m_iY = 20;

    //這裏咱們來打印p1的橫座標和縱座標,來講明是對象指針p2操縱了對象p1
    cout <<"對象p1這個點的座標是:("<< p1.m_iX <<","<< p1.m_iY <<")"<< endl; 

    system("pause");
    return 0;
}

對象成員指針

對象成員指針是什麼呢?那麼咱們來想想,以前咱們學習過對象成員。對象成員,就是做爲一個對象來講,它成爲了另一個類的數據成員。而對象成員指針呢,則是對象的指針成爲了另一個類的數據成員了。

咱們先來回顧一個熟悉的例子,以下:

左邊呢,咱們定義了一個點的座標類,它的數據成員有點的橫座標和縱座標;右邊呢,咱們定義了一個線段類,在這個線段類中,須要有兩個點(一個起點和一個終點),咱們用點A和點B來表示,咱們當時用的是座標類的對象,分別是m_coorA和m_coorB。如今呢,咱們要把它們變成指針,以下:

初始化的時候呢,與對象成員初始化的方法能夠是同樣的,使用初始化列表來初始化,只不過如今是指針了,因此咱們賦初值NULL。

除了可使用初始化列表進行初始化之外,還可使用普通的初始化,好比說,在構造函數中,寫成以下方式:

固然,更多的是下面的狀況,由於咱們這是兩個指針,必定要指向某一個對象,纔可以進行操做,纔會有意義。而它指向的就應該是兩個點的座標對象:

在這裏面,指針m_pCoorA指向了一個座標對象(1,3),m_pCoorB指向了另一個座標對象(5,6)。那麼,這就至關於在構造函數當中,咱們從堆中分配了內存。既然在構造函數當中從堆中分配了內存,那麼咱們就須要在析構函數中去把這個內存釋放掉,這樣纔可以保證內存不被泄漏。

此外呢,做爲對象成員和對象成員指針還有另一個很大的不一樣。做爲對象成員來講,若是咱們使用sizeof這個對象的話,它就應該是裏面全部對象的體積的總和(以下圖所示)

  而對象成員指針則不一樣,咱們來看一看剛剛對象成員指針咱們定義的時候是如何定義的。咱們能夠看到,咱們定義的時候呢,是寫了兩個指針做爲它的對象成員。而咱們知道,一個指針在32位的編譯器下面,它只佔4個基本內存單元,那麼兩個指針呢,則佔8個基本內存單元,而咱們前面所講到的Coordinate類呢,它有兩個數據成員,這兩個數據成員都是int型的,因此呢,每個數據成員都應該佔4個基本的內存單元。那麼這樣算下來呢,咱們來想想,若是咱們使用sizeof來判斷一個line這樣的對象,到底有多大呢?若是在line這個對象中定義的是對象成員(即兩個Coordinate),那麼這兩個Coordinate每個就應該都佔8個基本內存單元,那麼兩個呢,就應該佔16個基本內存單元,打印出來就應該是16,可是如今呢,line對象中是兩個對象成員指針,那麼每個對象成員指針應該只佔4個基本內存單元,因此sizeof(line)計算出來就應該是8,加起來是這兩個指針的大小的總和。

內存中的對象成員指針

當實例化line這個對象的時候,那麼兩個指針(m_pCoorA和m_pCoorB)也會被定義出來,因爲兩個指針都是指針類型,那麼都會佔4個基本內存單元。若是咱們在構造函數當中,經過new這樣的運算符從堆中來申請內存,實例化兩個Coordinate這樣的對象的話呢,這兩個Coordinate對象都是在堆中的,而不在line這個對象當中,因此剛纔咱們使用sizeof的時候呢,也只能獲得8,這是由於m_pCoorA佔4個基本內存單元,m_pCoorB佔4個基本內存單元,而右邊的兩個Coordinate對象並不在line這個對象的內存當中。當咱們銷燬line對象的時候呢,咱們也應該先釋放掉堆中的內存,而後再釋放掉line這個對象。

對象成員指針代碼實踐

/* 對象成員指針

要求:

定義兩個類:

    座標類:Coordinate

    數據成員:m_iX和m_iY

    成員函數:構造函數、西溝函數、數據成員封裝函數

    線段類:Line

    數據成員:點A指針 m_pCoorA,點B指針m_pCoorB

    成員函數:構造函數、析構函數、信息打印函數

/* **************************************/

頭文件(Coordinate.h

class Coordinate
{
public:
    Coordinate(int x, int y);
    ~Coordinate();
    int getX();
    int getY();
public:
    int m_iX;
    int m_iY;
};

源程序(Coordinate.cpp)

#include"Coordinate.h"
#include<iostream>

using namespace std;

Coordinate::Coordinate(int x, int y)
{
    m_iX = x;
    m_iY = y;
    cout <<"Coordinate()  "<< m_iX <<","<< m_iY << endl;
} 
Coordinate::~Coordinate()
{
    cout <<"~Coordinate()  "<< m_iX <<","<< m_iY << endl;
}
int Coordinate::getX()
{
    return m_iX;;
}
int Coordinate::getY()
{
    return m_iY;;
}

頭文件(Line.h

#include"Coordinate.h"

classLine
{
public:
    Line(int x1, int y1, int x2, int y2);
    ~Line();
    void printInfo();
private:
    Coordinate *m_pCoorA;
    Coordinate *m_pCoorB;
};

源程序(Line.cpp

#include"Line.h"
#include<iostream>

using namespace std;

Line::Line(int x1, int y1, int x2, int y2)
{
    //從堆中實例化兩個座標對象,並使指針m_pCoorA和m_pCoorB分別指向這兩個對象
    m_pCoorA = new Coordinate(x1, y1);
    m_pCoorB = new Coordinate(x2, y2);
    cout <<"Line()"<< endl;
}
Line::~Line()
{
    delete m_pCoorA;
    m_pCoorA = NULL;
    delete m_pCoorB;
    m_pCoorB = NULL;
    cout <<"~Line()"<< endl;
}
voidLine::printInfo()
{
    cout <<"printInfo()"<< endl;
    cout <<"("<< m_pCoorA->getX() <<","<< m_pCoorA->getY() <<")"<< endl;
    cout <<"("<< m_pCoorB->getX() <<","<< m_pCoorB->getY() <<")"<< endl;
}

主調函數(demo.cpp

首先咱們只實例化一個線段對象(同時傳入四個參數),而後就銷燬這個對象,不作其餘操做,以下:

#include"Line.h"
#include<iostream>
#include<stdlib.h>

using namespace std;

int main()
{
    //從堆中實例化一個線段對象,並傳入四個參數
    Line *p = new Line(1,2, 3, 4);
    delete p;
    p = NULL;


    system("pause");
    return 0;
}

咱們來看一下運行結果:

從這個運行結果來看,首先實例化了一個點座標對象A,而後又實例化了一個點座標對象B,接着才實例化了一個線段的對象;因爲後面調用了delete,A和B就會觸發這兩個Coordinate對象的析構函數,最後調用Line自己的析構函數。

此外,咱們如今在main函數中打印一下信息,經過p來調用printInfo()函數,同時經過sizeof來計算一下其大小,以下代碼:

int main()
{
    //從堆中實例化一個線段對象,並傳入所個參數
    Line *p = new Line(1,2, 3, 4);
    p->printInfo();

    delete p;
    p = NULL;

    cout <<sizeof(p) << endl;
    cout <<sizeof(Line) << endl;

    system("pause");
    return 0;
}

再來看一下運行結果:

從運行結果看,經過p是能夠正常調用信息打印printInfo()函數的(屏幕中間已經打印出信息打印函數名,而且也打印出了A點座標和B點座標)。最後,打印出4和8,告訴咱們,指針p自己大小爲4,而Line對象大小爲8(說明Line僅僅包含m_pCoorA和m_pCoorB這兩個對象成員指針)。

相關文章
相關標籤/搜索