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


對象指針

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

在這個例子中,我們定義了一個坐標的類(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)。接下來看一下如下的具體范例。

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

對象指針代碼實踐

題目描述:

/* 示例要求

定義Coordinate類

    數據成員:m_iX和m_iY

    聲明對象指針,並通過指針操控對象

 

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

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

頭文件(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這兩個對象成員指針)。

 


免責聲明!

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



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