开发者

Better to allocate on the stack or heap within a class

This was more of a conceptual question, but I'll provide a particular instance where I'm wondering about this. If I have a class that has several objects as properties, is it better to allocate them statically within the class, or dynamically during construction? For example, I have the following class (unecessary code emitted)

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

sf::Text are both objects as well. My question is, is it better to have them as declared above, and initialize them as follows

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);
}

or, should i have them as pointers and allocate them with new as follows:

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

and deallocate them with delete in the destructor? I know the stack allocates memory alot faster, but I think I'm double initializing the way I have now. SO is it a case by case thing or is one always better then the other? I'm still used to the C# way of doing things, so I was curious what the proper way to do this for C++开发者_如何学编程 is. If I have a fundamental misunderstanding, please correct me.

Thanks in advance


Wherever possible, you should avoid explicit dynamic allocation. Dynamic allocation is relatively expensive and makes object lifetime management more difficult.

Note that your question is somewhat misleading: in the first case, BaseText and BackgroundText aren't necessarily allocated on the stack. If the OutlinedText object of which they are a member is allocated on the heap or is a static variable, then they won't exist on the stack at all.

I think I'm double initializing the way I have it now.

You are: each of the member variables is initialized before the constructor body is entered, then in the constructor body, you assign to the member variables, so effectively they are getting "double initialized."

You can (and in most cases should) use the constructor's initialization list to initialize member variables:

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);
}

This way, the member variables only get initialized once (via the initializers in the initializer list), not twice.

Should I have them as pointers and allocate them with new as follows and deallocate them with delete in the destructor?

No: you should not have to write delete in your C++ program: you should use a smart pointer (e.g., auto_ptr, shared_ptr, or unique_ptr, depending on what kind of object lifetime you need) to ensure that dynamically allocated objects are automatically destroyed. If you need to store a collection of objects, you should use one of the Standard Library containers, like vector, map, or set, which also automatically clean up the objects that you store in them.

Manually managing the lifetime of a dynamically allocated object is tedious and difficult to get right and should be avoided. You don't just need to "clean up in the destructor," you also need to correctly implement (or suppress automatic generation of) a copy constructor and a copy assignment operator.

C++ is fundamentally different from C#, especially with respect to object lifetimes and how objects come into existence, how they are copied, and when they are destroyed. Definitely make sure that you have a good introductory C++ book if you really want to learn the language.


As James answered your primary question, I'll touch on something else you said:

I know the stack allocates memory alot faster, but I think I'm double initializing the way I have now.

You're not technically double-initializing, but what you're doing is indeed inefficient. As @Peter Huene commented, you should be using an 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(sf::Text(text, font, BaseFontSize)),
    BackgroundText(sf::Text(text, font, BackFontSize))
{
    BaseText.SetColor(MainColor);
    BackgroundText.SetColor(OffsetColor);
}

This idiom, in combination with avoiding explicit dynamic allocation, should be nearly always preferred.

Also, it doesn't appear that you're modifying text or font, so you should probably be passing them by const& rather than by &. (Also consider passing MainColor, OffsetColor, and Pos by const& rather than by value if they're larger than sizeof(void*).)


If they will fit on the stack and you can initialise them to a useful state in the ctor then stack variables save you constantly checking for them being valid.

If they can't be set until some later point then using new and having an unset variable as NULL might make sense, especially if the variable doens't have an obvious not-set value


Both and neither, or the best engineering answer yet, "It depends!"

For small objects, allocation on the stack is great and convenient. The objects will be constructed for you automatically, and if you want to invoke an alternate ctor you should really use the initializer list to avoid the double-initializing issue you mention.

Larger objects, or objects that are passed out of the class, are appropriate for the heap instead.


generally, it is best to use an instance variable which is not allocated dynamically.

  • you can be sure it exists when the object uses it

  • it will require less overall memory

  • it is less likely to fail

  • copy/move is simpler to implement (it's often automatic)

  • storage is automatic (smart pointer implementations are not necessarily thread safe)

sometimes, you'll need to allocate it dynamically. typically, you'll know when to do this:

  • lazy initialization (sometimes it's necessary... it's also a really bad idea in some cases)

  • polymorphism

  • decoupling (e.g. using PIMPL to reduce build dependencies)

  • shared memory (e.g. it's ref-counted)

  • its physical size is large. even when the physical size is large, it's often better to to force the clients to allocate the container on the heap by making the constructors private.

wrt your example: the text in the text objects is likely allocated dynamically - the text object itself likely consumes a few words. if it's only a few words and there is no need to allocate it dynamically, definitely avoid dynamic allocation.

when you choose dynamic, always use some form of smart pointer.

good luck!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜