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