C++ 如何使用C++;安置新作品?

C++ 如何使用C++;安置新作品?,c++,placement-new,C++,Placement New,这个问题是为了确认我正确理解了这个概念,并就使用方式和可能的优化采纳了专家意见 我正在努力理解“新的安置”,下面是我提出的计划 #include <iostream> #include <new> class A { int *_a; public: A(int v) {std::cout<<"A c'tor clalled\n";_a= new int(v);} ~A() {std::cout<<"A d'tor clalled\

这个问题是为了确认我正确理解了这个概念,并就使用方式和可能的优化采纳了专家意见

我正在努力理解“新的安置”,下面是我提出的计划

 #include <iostream>
 #include <new>

 class A {
 int *_a;
 public:
 A(int v) {std::cout<<"A c'tor clalled\n";_a= new int(v);}
 ~A() {std::cout<<"A d'tor clalled\n"; delete(_a);}
 void testFunction() {std::cout<<"I am a test function &_a = "<<_a<<" a = "<<*_a<<"\n";}
};
int main()
{
    A *obj1 = new A(21);
    std::cout<<"Object allocated at "<<obj1<<std::endl;
    obj1->~A();
    std::cout<<"Object allocated at "<<obj1<<std::endl;
    obj1->testFunction();
    A *obj2 = new(obj1) A(22);
    obj1->testFunction();
    obj2->testFunction();
    delete(obj1);// Is it really needed now? Here it will delete both objects.. so this is not the right place.
    //obj1->testFunction();
    //obj2->testFunction();
    return 0;
}
我有以下问题

  • 这是演示新位置的正确示例吗
  • 成员a是动态分配的(没有新位置)。那么,为什么它会获得与obj1和obj2相同的地址呢。这只是巧合吗
  • D'tor打15号线电话是一种好的做法吗

还请指出,你看到的任何东西,我可以改善或只是不尝试。任何好的参考或阅读都是受欢迎的。

这可能是为了,在回答您的问题之前,让我对您的源代码稍加评论

A *obj1 = new A(21);
std::cout<<"Object allocated at "<<obj1<<std::endl;
obj1->~A();
这是UB。您已经破坏了对象,不应该对其调用任何方法

18    A *obj2 = new(obj1) A(22);
19    obj1->testFunction();
20    obj2->testFunction();
虽然
obj1
obj2
是完全相同的对象,但请注意,这是一个okayish

21    delete(obj1);// Is it really needed now? Here it will delete both objects.. so this is not the right place.
你的评论是错误的。你不是在删除两个对象,而是在删除一个,以后还会删除更多

22    obj1->testFunction();
23    obj2->testFunction();
这也是UB,不要对解构或删除的对象调用方法。 关于你的问题:

成员_a是动态分配的(没有新的位置)。那么,为什么obj1和obj2的地址相同呢。这只是巧合吗

不要将它们称为
obj1
obj2
,因为这两个变量指向同一个对象,但是是的,这是巧合。在第一个对象被销毁并释放内存后,第二个对象分配了刚刚释放的相同内存量,分配器决定给您完全相同的内存

D'tor打15号线电话是一种好的做法吗

不,不是。很少有例子说明为什么需要调用析构函数,其中一个例子是对象是通过放置新对象创建的。在您的示例中,这没有副作用,因为在解构旧对象之后,您在同一位置构造了一个新对象,并且新对象与旧对象的类型相同,否则这可能会以某种方式严重损坏

现在,在删除之后,再了解一下您的评论。让我们看看一个新的和一个新的位置实际上做了什么

一种新的方法是:

  • 从操作系统为新对象分配内存
  • 调用新对象上的构造函数,地址(
    this
    )设置为分配器获得的内存块
删除的作用正好相反:

  • 调用对象的析构函数
  • 释放内存块
现在来看placement new:placement new只跳过第一步(分配内存),调用新对象的构造函数,并将
this
设置为您传递的地址。因此,placement new的反面就是调用析构函数,因为不存在placement delete

这意味着对于代码来说,在调用析构函数后,第一个对象死亡,但您从未归还内存,这就是您可以在该内存中构造新对象的原因。现在,当您调用delete时,第一个对象不再存在,只有它使用的内存,但相同的内存现在被第二个对象阻塞,因此,当您调用delete时,您不删除两个对象,只删除第二个对象(解构它,然后释放内存块)


您可以阅读更多关于主题placement new和何时调用析构函数的信息,它非常非常简单:
new
可以被认为是在做两件事:

  • 分配内存
  • 在分配的内存中构建对象的位置
  • 无法保证实现实际使用了
    malloc
    ,但通常是这样。您不能假设它是关于实现的,但是为了理解它,这是一个OK的假设

    因此,以下内容被认为是等效的:

    auto obj1 = new std::string("1");
    // ↑ can be thought of as equivalent to ↓ 
    auto obj2 = (std::string*)malloc(sizeof(std::string));
    new(obj2) std::string("2");
    
    同样的情况也适用于
    删除

    delete obj1;
    // ↑ can be thought of as equivalent to ↓ 
    obj2->~string();
    free(obj2);
    
    然后,当您看到
    new
    delete
    时,您可以很容易地对它们进行推理,了解它们的真正含义:分配之后是构造函数调用,析构函数调用之后是释放

    当您使用placement
    new
    时,您已经决定单独处理第一步。内存仍然必须以某种方式分配,您只需完全控制它是如何发生的以及内存来自何处

    因此,您必须分别跟踪两件事:

  • 记忆的生命周期

  • 对象的生存期

  • 下面的代码演示了它们是如何相互独立的:

    #include <cstdlib>
    #include <string>
    #include <new>
    
    using std::string;
    
    int main() {
        auto obj = (string*)malloc(sizeof(string));  // memory is allocated
        new(obj) string("1");  // string("1") is constructed
        obj->~string ();       // string("1") is destructed
        new(obj) string("2");  // string("2") is constructed
        obj->~string ();       // string("2") is destructed
        free(obj);             // memory is deallocated
    }
    
    但这没关系:

    void ub() {
        alignas(string) char buf[sizeof(string)]; // memory is allocated
        new(buf) string("1");                     // string("1") is constructed
        buf->~string();                           // string("1") is destructed
    }                                             // memory is deallocated
    
    请注意如何使用正确对齐自动缓冲区。对于任意类型,缺少
    alignas
    ,会导致UB。它可能看起来有效,但那只会误导你

    有一些特定的类型,不调用析构函数和不正确对齐内存不会导致UB,但是您永远不应该假设类型存在这种情况。调用析构函数并进行对齐,如果结果证明不必要,则不会花费任何费用-不会为此类类型生成额外代码

    struct S {
      char str[10];
    }
    
    < C++如何放置新作品?< /P>

    我正在努力理解“新的安置”,下面是我提出的计划

     #include <iostream>
     #include <new>
    
     class A {
     int *_a;
     public:
     A(int v) {std::cout<<"A c'tor clalled\n";_a= new int(v);}
     ~A() {std::cout<<"A d'tor clalled\n"; delete(_a);}
     void testFunction() {std::cout<<"I am a test function &_a = "<<_a<<" a = "<<*_a<<"\n";}
    };
    int main()
    {
        A *obj1 = new A(21);
        std::cout<<"Object allocated at "<<obj1<<std::endl;
        obj1->~A();
        std::cout<<"Object allocated at "<<obj1<<std::endl;
        obj1->testFunction();
        A *obj2 = new(obj1) A(22);
        obj1->testFunction();
        obj2->testFunction();
        delete(obj1);// Is it really needed now? Here it will delete both objects.. so this is not the right place.
        //obj1->testFunction();
        //obj2->testFunction();
        return 0;
    }
    
    这两个答案都很好。但你也想知道它是如何工作的,因此,我将补充大会的解释:


    • A*obj1=新的A(21)


    • A*obj2=new(obj1)A(22)



    这就是它的工作原理,足够清楚,不需要更多的解释,对吗?

    更清楚地说,取消引用已被
    delete
    ed调用的指针将调用UB。对于有关行为的问题-是的,正如您所怀疑的那样。第22行和第23行至调用未定义的行为
    void ub() {
        alignas(string) char buf[sizeof(string)]; // memory is allocated
        new(buf) string("1");                     // string("1") is constructed
        buf->~string();                           // string("1") is destructed
    }                                             // memory is deallocated
    
    struct S {
      char str[10];
    }
    
    call operator new(unsigned long)
    mov esi, 21
    mov rdi, rax
    mov rbx, rax
    call A::A(int)
    
      mov esi, 22
      mov rdi, rbx
      call A::A(int)