9

明确地将对象成员放在堆上(通过新的)是否被认为是不礼貌/不好的做法?我认为您可能希望允许客户端选择内存区域来实例化对象。我知道在某些情况下堆成员可能是可以接受的。如果你知道一种情况,你能描述一下吗?

4

4 回答 4

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

现在,当构造函数分配时,它将使用 aMyCustomAllocator而不是默认值。以下是有关编写自己的 STL 分配器的一些细节。

如果您担心将堆用于类中的某些存储可能是“不礼貌”,您可能需要考虑为您的类的用户提供这样的选项,以便他们可以指定在以下情况下如何分配事物您的默认策略不符合他们的需求。

于 2009-02-27T19:07:09.460 回答
10

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

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

另一方面,如果您的用户创建了许多类的实例以在堆栈上使用,那么使用对象而不是成员变量的指针将是有利的,因为相比之下堆分配/释放比较慢。在这种情况下避免堆更有效,当然考虑到上面的第一个项目符号。

于 2009-02-27T19:22:38.877 回答
1

类放置其成员的位置不如对它们的管理包含在类中重要;即客户和子类不必担心对象的成员变量。

最简单的方法是让它们成为堆栈变量。但是在某些情况下,比如如果你的类有一个像链表这样的动态数据结构,那就没有意义了。

但是,如果您确保您的对象自行清理,那么对于大多数应用程序来说应该没问题。

于 2009-02-27T19:20:53.467 回答
0

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

如果你有一堂课:

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

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

但这将由客户决定如何分配 MyClass 类本身,无论是在堆栈上还是在堆上:

MyClass* pMyClass = new MyClass;

或者

MyClass myClass;
于 2009-02-27T19:12:47.840 回答