2011-04-02 93 views
3

這是一個概念性問題,但我會提供一個特定的實例,我想知道這個。如果我有一個具有多個對象作爲屬性的類,最好在類中靜態分配它們還是在構建時動態分配它們?例如,我有以下類(發出的不必要的代碼)更好地分配堆棧或堆內類

class OutlinedText 
{ 
protected: 
    sf::Text BaseText; 
    sf::Text BackgroundText; 
} 

sf ::文本也是對象。我的問題是,是它更好地讓他們爲上述聲明,並進行初始化如下

OutlinedText::OutlinedText(std::string &text, sf::Color MainColor, sf::Color OffsetColor, sf::Font &font, sf::Vector2f Pos, unsigned int BaseFontSize, unsigned int BackFontSize, float Offset) 
{ 
    BaseText = sf::Text(text, font, BaseFontSize); 
    BaseText.SetColor(MainColor); 
    BackgroundText = sf::Text(text, font, BackFontSize); 
    BackgroundText.SetColor(OffsetColor); 
} 

,或者我應該讓他們爲指針,並與新的分配它們如下:

BaseText = new sf::Text(text, font, BaseFontSize); 
BaseText->SetColor(MainColor); 

和在析構函數中用delete刪除它們?我知道棧很快分配內存,但我認爲我現在已經雙重初始化了。這是一種情況,還是一種比另一種好?我仍然習慣於C#的做事方式,所以我很好奇C++的正確方法是什麼。如果我有一個基本的誤解,請糾正我。

在此先感謝

+0

在一個側面說明,看看使用初始化列表。 – 2011-04-02 04:04:50

回答

12

只要有可能,您應該避免顯式動態分配。動態分配相對昂貴,並且使對象生命週期管理更加困難。

請注意,您的問題有點令人誤解:在第一種情況下,BaseTextBackgroundText不一定分配在堆棧上。如果它們所屬的對象OutlinedText被分配在堆上或者是一個靜態變量,那麼它們根本就不存在於堆棧中。

我想我現在已經雙重初始化了。

您是:每個成員變量的初始化在進入構造函數體之前,那麼在構造函數體,你分配的成員變量,所以有效的,他們所得到的「雙初始化。」

你可以(在大多數情況下應該)使用the constructor's initialization list初始化成員變量:

OutlinedText::OutlinedText(std::string& text, 
          sf::Color MainColor, 
          sf::Color OffsetColor, 
          sf::Font& font, 
          sf::Vector2f Pos, 
          unsigned int BaseFontSize, 
          unsigned int BackFontSize, 
          float Offset) 
    : BaseText(text, font, BaseFontSize),   // This is the constructor's 
     BackgroundText(text, font, BackFontSize) // initializer list 
{ 
    BaseText.SetColor(MainColor); 
    BackgroundText.SetColor(OffsetColor); 
} 

這樣,成員變量只得到初始化一次(通過初始化列表的初始化),而不是兩次。

我應該把它們作爲指針,並用new來分配它們,並在析構函數中用delete來釋放它們?

不,你不應該寫在你的C++程序delete:你應該使用一個智能指針(如auto_ptrshared_ptr,或unique_ptr,這取決於你需要什麼樣的對象生存期),以確保動態分配的對象會自動銷燬。如果您需要存儲一組對象,則應使用標準庫容器之一,如vector,mapset,它們也會自動清理存儲在其中的對象。

手動管理一個動態分配的對象的生命週期是單調乏味的,很難得到正確的,應該避免。您不僅需要「在析構函數中清理」,還需要正確實現(或禁止自動生成)複製構造函數和複製賦值運算符。

C++與C#有着根本的不同,特別是在對象生命週期以及對象是如何生成,如何複製以及何時被銷燬的情況下。如果你真的想要學習這門語言,那麼一定要確保你有a good introductory C++ book

+0

添加有關初始化程序列表的註釋。 – 2011-04-02 10:42:06

+0

@Martin:好主意;我錯過了OP對他的評論,認爲他是在初始化事物。 – 2011-04-02 15:12:57

0

如果它們適合堆棧,並且可以將它們初始化爲ctor中的有用狀態,那麼堆棧變量會使您不斷檢查它們是否有效。

如果它們不能被設置到以後的某個點,然後使用新的和具有未設置變量爲NULL可能是有意義的,尤其是如果變量好好嘗試一下有一個明顯的不設定值

0

兩者既不,還是最好的工程答案,「這取決於!」

對於小對象,堆棧上的分配非常方便。這些對象會自動爲你構建,如果你想調用另一個ctor,你應該真的使用初始化列表來避免你提到的雙重初始化問題。

較大的對象或傳出該類的對象適用於堆。

0

通常,最好使用未動態分配的實例變量。

  • 你可以肯定,當對象使用它,它存在

  • 它需要較少的總體內存

  • 它是不太可能失敗

  • 複製/移動更簡單(它通常是自動的)

  • 存儲是自動的(智能指針實現s不一定是線程安全的)

有時,您需要動態分配它。通常情況下,你會知道什麼時候才能做到這一點:

  • 初始化工作(有時是必要的......也就是在某些情況下,一個非常糟糕的主意)

  • 多態性

  • 解耦(例如,使用PIMPL減少編譯依賴)

  • 共享存儲器(例如,它的裁判計數)

  • 其物理尺寸很大。即使物理尺寸很大,通過使構造函數保持私有狀態,強制客戶端在堆上分配容器通常也會更好。

跟你的例子:文本對象中的文本可能是動態分配的 - 文本對象本身可能會消耗幾個單詞。如果只有幾句話,並且不需要動態分配它,那麼一定要避免動態分配。

當你選擇動態時,總是使用某種形式的智能指針。

祝你好運!

2

正如詹姆斯回答了你的首要問題,我會觸摸別的東西,你說:

我知道堆棧快得多分配內存,但我覺得我的雙初始化我現在已經在路上。

你在技術上不是雙重初始化,但你所做的確實效率低下。作爲@Peter華內評論說,你應該使用初始化列表:

OutlinedText::OutlinedText(
    std::string& text, 
    sf::Color MainColor, 
    sf::Color OffsetColor, 
    sf::Font& font, 
    sf::Vector2f Pos, 
    unsigned int BaseFontSize, 
    unsigned int BackFontSize, 
    float Offset 
) 
    : BaseText(sf::Text(text, font, BaseFontSize)), 
    BackgroundText(sf::Text(text, font, BackFontSize)) 
{ 
    BaseText.SetColor(MainColor); 
    BackgroundText.SetColor(OffsetColor); 
} 

這個成語,在避免了明確的動態分配相結合,應該幾乎總是首選。

而且,它不會出現,您要修改textfont,所以你也許應該由const&路過他們,而不是由&。 (也可以考慮通過const&而不是按值傳遞MainColorOffsetColorPos只要他們比sizeof(void*)大。)