C++中RTTI動態類型識別、靜態成員、變數的四種儲存方式

來源:互聯網
上載者:User

本文主要來講解C++中的RTTI機制,也就是動態類型識別技術,之前我在學習《C++編程思想》的時候學習過這個技術,現在也算是複習一下吧,因為RTTI在MFC程式設計中也是有一定用處的。

 

先來看一段程式,這段程式中就是使用了RTTI,

#include <typeinfo><br />#include <iostream><br />#include <fstream><br />using namespace std;</p><p>ofstream out("out.txt");</p><p>class GraphicImage{</p><p>protected:<br />char name[80];</p><p>public:<br />GraphicImage(){<br />strcpy(name , "GraphicImage");<br />}<br />virtual void play(){</p><p>out<<"Display a generic image /n/n";<br />}<br />char* getName(){<br />return name ;<br />}</p><p>};</p><p>class GIFImage: public GraphicImage{</p><p>public:<br />GIFImage(){<br />strcpy(name , "GIFImage");<br />}<br />void display(){</p><p>out<<"Display a GIF file /n/n";<br />}<br />};</p><p>class PICImage: public GraphicImage{</p><p>public:<br />PICImage(){</p><p>strcpy(name , "PICImage");<br />}<br />void display(){</p><p>out<<"Display a PICT file/n/n";<br />}<br />};</p><p>void processFile(GraphicImage* type){</p><p>if( typeid(GIFImage) == typeid(*type)){<br />((GIFImage*)type)->display();<br />}</p><p>else if( typeid(PICImage) == typeid(*type)){<br />((PICImage*)type)->display() ;<br />}<br />else<br />out<<"Unknown type!"<<(typeid(*type)).name()<<"/n/n";<br />}</p><p>int main(void) {</p><p>GraphicImage* gImage = new GIFImage();<br />GraphicImage* pImage = new PICImage();</p><p>processFile(gImage);<br />processFile(pImage);</p><p>return 0;<br />}</p><p>

 

執行結果如下:

Display a GIF file

 

Display a PICT file

這裡最主要的函數為processFile()函數,在函數內部就是通過typeid()這個運運算元來動態判斷對象的類型的。

在使用RTTI技術時,要注意的是:1、需要包含標頭檔#include <typeinfo.h> 2、typeid()運算元的參數可以是類名,也可以是對象指標。

 

好了,簡單地講了一下RTTI後,再來說一下C++中與類的靜態成員相關的一些性質。

 

static 成員變數不屬於對象的一部份,而是類的一部份,所以程式可以在還沒有誕生任何對象的時候就處理此種成員變數。但首先你必須初始化它。

 

那麼到底應該怎麼初始化它呢?對於這個問題,我之前一直很困惑,知道怎麼做,卻不知道為什麼,但是看了下面的講解,原理就清楚了。

不要把static 成員變數的初始化動作安排在類的建構函式中,因為建構函式可能一再被調用,而變數的初值卻只應該設定一次。也不要把初始化動作安排在標頭檔中,因為它可能會被包含許多地方,因此也就可能被執行許多次。你應該在實現檔案中且類別以外的任何位置設定其初值。例如在main 之中,或全域函數中,或任何函數之外。

 

對於C++類中的靜態成員變數要有這樣的一個認識:靜態成員變數不是類的對象所擁有的,它在類對象存在之前就已經存在,所以可以看成是一個全域的變數。

 

好,接下來再來想一下靜態成員函數是怎麼樣的呢?

由於static 成員函數不需要藉助任何對象,就可以被調用執行,所以編譯器不會為它暗加一個this 指標。也因為如此,static 成員函數無法處理類別之中的non-static 成員變數。

所以,static 成員函數「沒有this 參數」的這種性質,正是我們的MFC 應用程式在準備callback 函數時所需要的。

 

好了,講完上面的這些東西,我們繼續看看C++中四種變數的儲存方式,如果是類對象的變數的話,就牽涉到建構函式和解構函式執行的問題。有一點很明確,一般情況下,類的對象的建構函式是從父類到子類的順序執行的,而解構函式執行循序是恰好反過來。

 

我們先來看幾個類物件變數的聲明:

1、

void MyFunc()
{
 CFoo foo; // 在堆棧(stack)中產生foo 對象
 ...
}

2、

void MyFunc()
{
...
CFoo* pFoo = new CFoo(); // 在堆(heap)中產生對象
}

3、

CFoo foo; // 在任何函數範圍之外做此動作

4、

void MyFunc()
{
static CFoo foo; // 在函數範圍(scope)之內的一個靜態對象
...
}

 

第1、2種情況是c++在堆棧(stack)、堆(heap)中分配記憶體時,就會隱含地調用CFoo的建構函式。

第3情況,就不一樣了:由於它的聲明是全域的(靜態全域對象),所以它的構造式調用動作必須靠startup 碼幫忙。

startup 碼是什嗎?是更早於程式進入點(main 或WinMain)執行起來的碼,由C++ 編譯器提供,被連接到你的程式中。startup 碼可能做些像函數庫初始化、進程資訊設立、I/O stream 產生等等動作,以及對static 對象的初始化動作(也就是調用其構造式)。

第4種情況:相當於(地區靜態對象),類似C 語言中的靜態地區變數,只會有一個實體(instance)產生,而且在固定的記憶體上(既不是stack 也不是heap)。它的構造式在控制權第一次移轉到其聲明處(也就是在MyFunc 第一次被調用)時被調用。

另外,對於以new 方式產生出來的地區對象,當對象誕生時其構造式被執行。析構式則在對象被delete 時執行。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.