所謂對象指針,顧名思義就是有一個指針,其指向一個對象,下面經過一個例子來講明這樣一個問題。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這兩個對象成員指針)。