【问题标题】:What is this second new?这第二个是什么新东西?
【发布时间】:2011-04-27 00:13:23
【问题描述】:

第二行是什么? (在回答另一个问题时看到。)

int * x = new int [1] ;
int * y = new (x) int;

在第二行之后 x 和 y 具有相同的值(指向同一个地方)。 y = x 和第二行有什么区别?是像构造函数还是什么的?

【问题讨论】:

  • 注意:放置 new 通常不与 int 一起使用(因为它没有优势(int 没有构造函数))。
  • @Martin:虽然new(x) int() 会有所不同,但可以通过模板使用。始终使用括号,即使使用 new[],也是一个好主意。
  • 指向您看到的其他问题的链接可能很有用。

标签: c++ syntax terminology new-operator placement-new


【解决方案1】:

这称为placement new。它允许您在已分配的内存中构造一个对象。

这个较早的帖子讨论了where and how it is useful for

【讨论】:

  • 我不完全明白!当我们再次新的东西时,它的指针会改变!但是这如何在不改变指针的情况下工作!看到VCEXPRESS8的输出:x之前的第二行0x005c4e58 x之后的第二行0x005c4e58都是一样的!
  • 它告诉编译器“执行通常的构造函数,但不是创建更多内存,而是使用x 的内存。”所以它不会修改x,而是覆盖你在第一行创建的int
  • @Green,没有新分配的新内存,这就是为什么您的示例中的y 指向与x 相同的内存地址。可以将其视为简单地省略内存分配步骤,并使用提供的内存块立即执行适当的构造函数调用。
  • 那么,如果我理解正确的话,你最终得到的是2个指向同一个内存块中的结构的指针?那为什么要使用 X 和 Y 呢?为什么不坚持使用 X?
  • @EKI,上面的例子使用了int类型,这里并没有太大的区别。但是,对于非原始类型,可能会有很大的不同。你可以例如分配一个大小合适的chars 数组,然后用placement new 为它初始化一个YourFavouriteType 对象。然后你有一个char* 和一个YourFavouriteType* 指向物理上相同的内存,但“看到”可能非常不同的东西。
【解决方案2】:

它是placement new。它在x指向的内存中构造一个新的int

如果你尝试:

int * x = new int [1];
*x = 5;
std::cout << *x << std::endl;
int * y = new (x) int;
*y = 7;
std::cout << *x << std::endl;

输出将是:

5
7

【讨论】:

  • x 没有任何反应。但是*x 可能会发生一些事情。请尝试以下操作:int * x = new int [1] ; *x = 5; std::cout &lt;&lt; *x &lt;&lt; std::endl; int * y = new (x) int; *y = 7; std::cout &lt;&lt; *x &lt;&lt; std::endl;
  • @Green Code:没错。放置 new 只是执行初始化。这就像在给定的分配内存上调用该类型的构造函数。
  • @Green Code:对于原始类型,是的,基本上没有区别。对于类类型,有很大的不同,因为构造函数会再次被调用。
  • @Geen 代码:新位置通常不这样使用。见下文。
  • 记住new(x) int != new(x) int()!
【解决方案3】:

第二个新是“安置新”。它执行初始化(即调用任何必要的构造函数)而不进行任何分配。当您需要创建自定义内存分配方案时,它很有用。

【讨论】:

    【解决方案4】:
    int * y = new (x) int; 
    

    这是按照放置新语法。

    编辑:除了自定义分配之外,placement new 还有助于重新初始化对象状态,如下所示。

    class Test
    {
        int startVal;
    public:
        Test()
        {
            startVal = 1;
        }
        void setVal(int val) { startVal = val; }
    };
    int main()
    {
        Test *p = new Test;  //Creates new object and initializes it with
                              //a call to constructor.
        p->setVal(10);  //Change object content.
        new(p) Test; //Reset object:
        //object pointed by p will be re-initialzed here by making
        //a call to constructor. startVal will be back to 1
    }
    

    如上面 cmets 中所述,对象状态重置也可以使用placement new 来实现。 Placement new 不分配内存,它在括号中的指定地址构造对象。

    【讨论】:

    • 这不是一个好主意。如果 Test 包含指针成员会发生什么。因为您没有在原始对象上调用析构函数,所以您可能会泄漏内存。如果你打算使用placement new来重置一个值(这通常是个坏主意)你必须先调用对象的析构函数(你可以通过指针手动调用析构函数)。
    • @Martin:好点。但是我看到在我们广泛使用的商业代码中使用了重新设置。它们在类中没有任何指针。不确定这是否是不好的做法。无论如何,谢谢你。
    【解决方案5】:

    这是新的展示位置。

    虽然您通常不会将它与整数类型一起使用。
    它通常用于构建缓冲区,然后在其中构建其他类型。

    // Allocate a buffer with enough room for two T objects.
    char* buffer   = new char[sizeof(T) * 2];
    
    // Allocate a T in slot zero
    T* t1 = new (buffer + 0 * sizeof(T)) T("Zero");
    
    // Allocate a T in slot one
    T* t2 = new (buffer + 1 * sizeof(T)) T("One");
    

    这就是基础。
    但请记住,用placement new 分配的对象不能delete 语句删除。这是因为delete 试图回收new 分配的内存(以及调用析构函数)。所以要正确使用这些对象,你必须手动调用那里的析构函数。

    t1->~T();
    t2->~T();
    

    不要忘记删除原始缓冲区。

    delete [] buffer;
    

    其他一些注意事项:
    人们经常看到缓冲区可以在堆栈上实现,从而自动释放

    char buffer[sizeof(T) * 2];
    

    不幸的是,这在技术上可能没问题(它可以编译)。但它不能保证可以工作,因为缓冲区的内存可能无法正确对齐以将 T 放置在其中。因此,您必须动态分配缓冲区(通过使用 new 它保证内存针对分配的任何大小的对象正确对齐(因此通过扩展,它也针对小于分配大小的任何大小对齐)。绕过的简单方法这个问题是使用 std::vector

    std::vector<char>    buffer(sizeof(T) * 2);
    T* t1 = new (&buffer[0] + 0 * sizeof(T)) T("Zero");
    T* t2 = new (&buffer[0] + 1 * sizeof(T)) T("One");
    

    placement new 的另一个用途是重置对象。
    我已经看到这样做了,但我更喜欢使用更标准的赋值运算符:

    T   obj1("Plop");
    obj1  = T("Another Plop");
    
    // Can be done like this:
    T   obj1("Plop");
    obj1.~T();
    new (&obj1) T("Another Plop");   // Seems excessive to me. But can be us-full
                                     // in some extreme situations.
    

    请记住,如果您使用重置方法,您必须先销毁旧对象(否则该对象可能无法正常运行)。

    【讨论】:

      猜你喜欢
      • 2013-08-12
      • 2017-04-04
      • 2012-10-24
      • 1970-01-01
      • 2019-08-17
      • 1970-01-01
      • 1970-01-01
      • 2021-12-11
      • 2015-06-12
      相关资源
      最近更新 更多