2

您好,我在 C++ 入门第 5 版的第 19 章。' Operator newand deletevs newanddelete表达式和位置new':

AFAIKoperator newoperator delete 分别分配和释放内存,但不在那里构造对象。另一方面,new-expression调用operator new分配内存,在该内存地址中构造一个对象,最后返回一个指向新分配和初始化的对象的指针。

所以为了理解起见,我试过这个:

struct Foo{
    Foo(){std::cout << "Foo()\n";}
    ~Foo(){std::cout << "~Foo()\n";}
    void bar() const{
        std::cout << "Foo::bar()\n";
    }
};

int main(){


    Foo* pf = (Foo*)operator new(sizeof(Foo)); // Foo() is not called! Is it UB?
    pf->bar(); // looks to work fine. Does it really?
    delete pf; // looks fine?


    Foo* pf2 = new Foo{}; // ok Foo() is called 
    pf2->bar(); // OK
    delete pf2; // OK ~Foo() is called

}

正如您在第一个示例中看到的那样,我直接调用了 operator new,我猜后者只分配内存但不在那里构造对象,最后它返回一个指向 void 的指针,指向新分配的成员。

所以我没有调用构造函数Foo()。现在为什么调用成员函数可以正常工作?pf->bar()? 还是这会产生未定义的行为?

第二个例子 ( pf2) 很简单。

  • 如果一个是安全和正确的,发生了什么?我怎么能使用未初始化的对象?谢谢!
4

2 回答 2

2

除非你使用placement new(基本上是普通的另一半new),否则不Foo存在对象,所以你不能在它上面调用成员函数。此外,如果你使用placement new,你必须自己调用析构函数:

Foo* pf = new (operator new(sizeof(Foo))) Foo;  // no cast needed
pf->bar();
pf->~Foo();
operator delete(pf);  // analogous, though not equivalent, to std::free
于 2021-05-31T23:23:25.640 回答
1
  • 现在为什么调用成员函数可以正常工作?pf->bar()? 还是这会产生未定义的行为?

这是未定义的行为。pf是一个Foo*不指向的实例,Foo所以你不能取消引用它。看起来它的工作原理与使用指针的原因相同,因为delete它可能看起来有效。在这种情况下,由于bar()不访问this,它类似于static成员函数,观察到的行为很可能是它会打印消息。但是你不能依赖这个,行为可能会因为没有明显的原因而改变。当您有未定义的行为时,您不应该期望 C++ 抱怨或以其他方式诊断。

  • delete pf; // looks fine?

delete pf;不好,因为pf不是由新表达式创建的(调用operator new不构成新表达式)。你需要用来operator delete释放内存。这又是未定义的行为。

  • 如果一个是安全和正确的,发生了什么?我怎么能使用未初始化的对象?

案例#1 不安全且不正确。您不能使用未初始化的对象。您可以使用 Placement new 对其进行初始化:

 void * memptr = operator new(sizeof(Foo));
 Foo* pf = new (memptr) Foo;

您仍然不能使用delete此处来销毁由放置新表达式创建的对象。您需要显式调用析构函数,然后用于operator delete单独释放内存。

pf->~Foo();
operator delete(memptr);
于 2021-05-31T23:26:38.730 回答