C++ 最好在类中的堆栈或堆上进行分配

C++ 最好在类中的堆栈或堆上进行分配,c++,constructor,C++,Constructor,这更像是一个概念性的问题,但我将提供一个我对此感到疑惑的特定实例。如果我有一个类,它有几个对象作为属性,那么在类中静态地分配它们更好,还是在构建过程中动态地分配它们更好?例如,我有以下类(发出了不必要的代码) 文本也是两个对象。我的问题是,是否最好像上面声明的那样对它们进行初始化 OutlinedText::OutlinedText(std::string &text, sf::Color MainColor, sf::Color OffsetColor, sf::Font &f

这更像是一个概念性的问题,但我将提供一个我对此感到疑惑的特定实例。如果我有一个类,它有几个对象作为属性,那么在类中静态地分配它们更好,还是在构建过程中动态地分配它们更好?例如,我有以下类(发出了不必要的代码)

文本也是两个对象。我的问题是,是否最好像上面声明的那样对它们进行初始化

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++的正确方法是什么。如果我有一个基本的误解,请纠正我


提前感谢

尽可能避免显式动态分配。动态分配相对昂贵,并且使对象生命周期管理更加困难

请注意,您的问题有些误导:在第一种情况下,
BaseText
BackgroundText
不一定在堆栈上分配。如果它们所属的
OutlinedText
对象是在堆上分配的,或者是一个静态变量,那么它们在堆栈上根本不存在

我想我现在的生活方式是双重的

您是:每个成员变量都是在进入构造函数体之前初始化的,然后在构造函数体中,您分配给成员变量,因此它们实际上是“双重初始化”的

您可以(并且在大多数情况下应该)使用初始化成员变量:

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);
}
这样,成员变量只初始化一次(通过初始值设定项列表中的初始值设定项),而不是两次

我是否应该将它们作为指针,按如下方式分配给它们,并在析构函数中用delete解除分配它们

<不>:你不应该在C++程序中写代码<删除>代码>:你应该使用智能指针(例如,代码> AutoPPTR <代码>,<代码> SyddyPtR,或者<代码> UnQuyJPTR <代码>,这取决于你需要什么样的对象生存期),以确保动态分配的对象被自动销毁。如果需要存储对象集合,则应使用标准库容器之一,如
vector
map
set
,这些容器也会自动清理存储在其中的对象

手动管理动态分配的对象的生存期既繁琐又难以正确,应该避免。您不仅需要“在析构函数中清理”,还需要正确实现(或禁止自动生成)复制构造函数和复制赋值运算符


C++与C#有着根本的不同,特别是在对象生命周期、对象如何产生、如何复制以及何时销毁方面。如果你真的想学习这门语言,一定要确保你有

如果它们适合堆栈,并且您可以在ctor中将它们初始化为有用的状态,那么堆栈变量可以帮助您不断检查它们是否有效


如果在以后的某个时候才能设置它们,那么使用new并将未设置的变量设置为NULL可能是有意义的,特别是如果变量没有明显的未设置值,或者两者都没有,或者最好的工程答案是“视情况而定!”

对于小对象,堆栈上的分配非常方便。这些对象将自动为您构建,如果您想要调用一个备用的ctor,您应该真正使用初始值设定项列表来避免您提到的双重初始化问题


较大的对象或从类中传递出去的对象适合堆。

通常,最好使用不动态分配的实例变量

  • 当对象使用它时,您可以确保它存在

  • 它需要更少的总内存

  • 它不太可能失败

  • 复制/移动更易于实现(通常是自动的)

  • 存储是自动的(智能指针实现不一定是线程安全的)

有时,您需要动态地分配它。通常,您将知道何时执行此操作:

  • 延迟初始化(有时是必要的……在某些情况下,这也是一个非常糟糕的主意)

  • 多态性

  • 解耦(例如,使用PIMPL减少构建依赖)

  • 共享内存(例如,它的ref计数)

  • 它的物理尺寸很大。即使物理大小很大,也最好通过使构造函数私有化来强制客户机在堆上分配容器

举个例子:文本对象中的文本可能是动态分配的——文本对象本身可能会消耗一些单词。如果它只是几个字,并且没有必要动态分配,那么一定要避免动态分配

选择“动态”时,请始终使用某种形式的智能指针


祝你好运

当詹姆斯回答你的主要问题时,我将谈谈你说的其他一些话:

我知道堆栈分配内存的速度要快得多,但我认为我正在以现在的方式进行双重初始化

从技术上讲,您并没有双重初始化,但您所做的确实是低效的。正如@Peter Huene所评论的,您应该使用一个初始化列表:

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);
}
这种习惯用法,再加上避免显式动态分配,几乎总是首选

此外,您似乎没有修改
文本
字体
,因此
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);
}