关于堆上成员变量的C ++礼仪

时间:2009-02-27 18:50:49

标签: c++ heap

将对象成员显式放在堆上(通过new)是否被视为不礼貌/不良做法?我认为您可能希望允许客户端选择内存区域来实例化对象。我知道可能存在堆成员可能被接受的情况。如果您知道某种情况,请说明一下吗?

4 个答案:

答案 0 :(得分:13)

如果你有一个专为复制语义设计的类,并且你不必要地分配/释放一堆内存,我可以看到这是不好的做法。但总的来说,事实并非如此。有很多类可以使用堆存储。只要确保你没有内存泄漏(在析构函数中释放内容,引用计数等),你就可以了。

如果您想要更多灵活性,请考虑让您的用户指定Allocator。我会解释一下。

某些课程,例如std::vector,字符串,地图等需要它们所代表的数据结构的堆存储。它不被认为是不礼貌的;当你有一个自动分配的vector时,用户应该知道在调用vector构造函数时分配了一个缓冲区:

void foo() {
    // user of vector knows a buffer that can hold at least 10 ints
    // gets allocated here.
    std::vector<int> foo(10);  
}

同样,对于std::string,您知道有一个内部堆分配char*。每个string实例是否有一个通常取决于STL实现;通常他们被引用计数。

但是,对于几乎所有的STL类,用户执行可以选择放置的位置,因为他们可以指定分配器。 vector定义如下:

template <typename T, typename Alloc = DefaultAllocator<T> >
class vector {
    // etc.
};

在内部,vector使用Alloc(默认为T的默认分配器)来分配缓冲区和它可能需要的其他堆存储。如果用户不喜欢默认分配策略,他们可以指定自己的策略:

vector<int, MyCustomAllocator> foo(10);

现在,当构造函数分配时,它将使用MyCustomAllocator而不是默认值。以下是一些details on writing your own STL allocator

如果你担心在你的班级中使用堆来存储某些存储可能是“不礼貌”,你可能会考虑给你的类用户这样一个选项,以便他们可以指定事情的方式如果您的默认策略不符合他们的需要,则分配。

答案 1 :(得分:10)

我根本不认为这是不好的做法。您可能希望通过new显式分配成员变量的原因有很多种。这里有一些不在我的脑海里。

  • 假设您的班级有一个非常大的缓冲区,例如512kb或1MB。如果此缓冲区未存储在堆上,则如果用户创建了类的多个局部变量,则可能会超出默认堆栈空间。在这种情况下,在构造函数中分配缓冲区并将其存储为指针是有意义的。
  • 如果您正在进行任何类型的引用计数,则需要一个指针来跟踪实际指向数据的对象数量。
  • 如果您的成员变量的生命周期与您的类不同,那么指针就是您的选择。一个完美的例子就是懒惰的评估,你只需要为用户要求的成员付费。
  • 虽然它不一定对用户有直接的好处,但编译时间是使用指针而不是对象的另一个原因。如果在类中放置一个对象,则必须在头文件中包含定义该对象的头文件。如果使用指针,则可以转发声明类,并且只包含在需要它的源文件中定义类的头文件。在大型项目中,使用前向声明可以通过减少编译单元的总体大小来大大加快编译时间。

另一方面,如果您的用户创建了很多类的实例以供在堆栈上使用,那么使用对象代替成员变量的指针将是有利的,因为堆分配/解除分配比较慢。在这种情况下避免堆更有效,当然考虑到上面的第一个子弹。

答案 2 :(得分:1)

在课堂上放置其成员的重要性不如课程中的管理权限;即客户端和子类不应该担心对象的成员变量。

最简单的方法是使它们成为堆栈变量。但在某些情况下,例如,如果您的类具有动态数据结构,如链接列表,则没有意义。

但是如果你确保自己的对象在自己之后清理,那对大多数应用程序来说应该没问题。

答案 3 :(得分:0)

嗯,我真的不明白你的问题。

如果您有课程:

class MyOtherClass;
class MyClass
{
  MyOtherClass* m_pStruct;
};

然后,MyClass的客户端没有真正选择如何分配m_pStruct。

但是客户决定如何在堆栈上或堆上分配MyClass类本身:

MyClass* pMyClass = new MyClass;

MyClass myClass;