在C++中,为什么"new"需要动态创建一个对象而不仅仅是分配?

peo*_*oro 6 c++ memory malloc alignment new-operator

我有这个琐碎的类层次结构:

class Base {
public:
    virtual int x( ) const = 0;
};

class Derived : public Base {
    int _x;
public:
    Derived( int x ) : _x(x) { }
    int x( ) const { return _x; }
};
Run Code Online (Sandbox Code Playgroud)

如果我malloc用来分配一个实例Derived,然后尝试访问多态函数x,程序崩溃(我得到一个分段错误):

int main( ) {
    Derived *d;
    d = (Derived*) malloc( sizeof(Derived) );
    *d = Derived( 123 );

    std::cout << d->x() << std::endl; // crash

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当然我的实际应用程序要复杂得多(它是一种内存池).


我很确定这是因为我分配的方式d:我没有使用new.

我知道贴片new操作符,这必须是我需要的,但我从来没有使用它并且有一些问题:

  • 为什么我的应用程序崩溃,如果我不使用new

    什么是new真正做到?

    为什么我不能只使用赋值运算符来指定由Derived( 123 );指向的内存区域的值d

  • 我还需要new用于非多态类型吗?

    POD怎么样?

  • 我上面链接C++ Faq中,它表示传递给placement的内存区域new必须与我正在创建的对象对齐.

    我知道对齐是什么,但我不知道如何检查我班级所需的对齐方式.

    malloc 手册说:

    malloc()和calloc()函数返回一个指向已分配内存的指针,该内存适合于任何类型的变量.

    我希望我的类所需的对齐是返回的类大小sizeof,以便表单中的任何地址address_returned_by_malloc + i * sizeof(my_class)都适合分配我的对象.

    我的希望是对的吗?

Foo*_*Bah 4

让我们顺着线往下走

  1. 如果我不使用 new,为什么我的应用程序会崩溃?

虚拟表已损坏。

虚拟表卡在分配的内存之后。当您new创建一个类时,生成的代码将正确设置 vtable。但是,malloc 不会正确初始化 vtable

要查看虚拟表,请运行 g++ -fdump-class-hierarchy

Vtable for Derived
Derived::_ZTV7Derived: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI7Derived)
16    Derived::x

Class Derived
   size=16 align=8
   base size=12 base align=8
Derived (0x10209fc40) 0
    vptr=((& Derived::_ZTV7Derived) + 16u) <-- notice how this is part of the structure
  Base (0x10209fcb0) 0 nearly-empty
      primary-for Derived (0x10209fc40)
Run Code Online (Sandbox Code Playgroud)

出于类似的原因,在不重载operator=的情况下,生成的汇编代码将只复制数据而不复制vtable[再次强调,编译器只知道复制数据,而不知道复制vtable]

如果您想查看具有有效 vtable 函数的基于指针的版本:

Derived e(123);
d = &e;
Run Code Online (Sandbox Code Playgroud)
  1. 对于非多态类型,我是否还需要使用 new ?

如果您使用的是虚函数,那么是的,即使对于非多态类型也是如此

  1. 我希望我的类所需的对齐是 sizeof 返回的类大小,以便任何形式为 address_returned_by_malloc + i * sizeof(my_class) 的地址都适合分配我的对象。

对齐不是问题。

  • Sizeof 不会跳过 vtable 大小。vtable大小包含在sizeof中,没问题。问题很简单,malloc 不会初始化 vtable(或其他任何东西)。如果OP在malloc数据上使用了新的放置,没问题 (2认同)