2011-04-02 68 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*)大。)