【问题标题】:Are these placement new macros correct?这些放置新宏是否正确?
【发布时间】:2009-10-16 23:57:06
【问题描述】:

我制作了几个宏来更轻松地使用 Placement new。我只是想知道是否有任何明显的情况下这些不起作用。谢谢。

#define CONSTRUCT_INPLACE(TYPE,STORAGE,INIT)   ::new((TYPE*)STORAGE) TYPE INIT
#define DESTRUCT_INPLACE(TYPE,STORAGE)         ((TYPE*)STORAGE)->~TYPE()

【问题讨论】:

    标签: c++ macros placement-new


    【解决方案1】:

    我不是放置新位置方面的专家,但您如何定义宏存在一些问题。

    问题 1

    最明显的问题是使用转换(TYPE*)STORAGE 作为存储位置。这是不正确的。 Placement new 只是另一个 C++ 函数,它参与重载决议等操作。任意将内存转换为特定类型可能会导致放置 new 绑定到用户期望的不同 operator new。

    例如,有以下两个放置新的定义是有效的。您的宏可能会导致调用错误的宏。

    void * _cdecl operator new(size_t cbSize, void* pv);
    void * _cdecl operator new(size_t cbSize, SomeType* pv)-
    

    ...

    // These two call different overloads
    void* p = malloc(sizeof(SomeType));
    SomeType* f1 = CONSTRUCT_INPLACE(SomeType, p,()) 
    SomeType* f2 = new (p) SomeType();
    

    不久前我写了一篇博文,介绍如何使用这种类型的重载解决方案来实现自定义分配器。

    问题 2

    宏中的表达式 STORAGE 应该用括号括起来,以防止恶意宏扩展错误。

    ::new((TYPE*)(STORAGE)) TYPE INIT
    

    【讨论】:

      【解决方案2】:

      除了其他地方提到的问题之外,您的宏与模板类型的交互非常糟糕(即无法编译)。

      一般来说,当没有明显的好处时,应该避免使用宏。老实说,我在这里看不到真正的好处。

      例如

      template< class A, class B >
      class T
      {
      public:
      
          T(A y, B z) : x(y), w(z) {}
          A x;
          B w;
      };
      
      int main()
      {
          void* p = ::operator new(sizeof(T));
      
          CONSTRUCT_INPLACE(T<int, double>, p, (4, 5.0));
      
          DESTRUCT_INPLACE(T<int, double>, p);
      
          ::operator delete(p);
      
          return 0;
      }
      

      【讨论】:

      • 是的,好点。我仍然可以使用它们,但我必须对模板进行 typedef 并传入 typedef 而不是模板。
      【解决方案3】:

      看着他们,我想知道第一个 (TYPE*) 演员是干什么用的 - 毕竟,placement new 需要 void*

      但话又说回来,我想知道它们到底有什么用。他们所能做的就是进一步混淆一些一开始并不简单的事情。为什么你认为你需要它们?

      话虽如此,您是否查看过(诚然为数不多的)用于处理未初始化内存的标准库工具? (见this网站的下半部分。可能有一些你可以使用的东西。

      【讨论】:

        【解决方案4】:

        您的CONSTRUCT_INPLACE 对我来说似乎有点奇怪。用法是:

        void* p = <some memory location>;
        Foo* f = CONSTRUCT_INPLACE(Foo, p, Foo())
        
        // The macro-free version seems easier to read:
        Foo* f = new(p) Foo();
        

        DESTRUCT_INPLACE 至少比它替换的代码更容易阅读。

        但是说真的,你为什么要做这么多新的展示位置,以至于这些宏对你有用?把这些东西放在泳池课上,然后忘记它。

        【讨论】:

        • 其实应该是这样使用的:Foo* f = CONSTRUCT_INPLACE(Foo, p, () )
        【解决方案5】:

        Placement-new 操作符在最初的“原始”内存块中创建一个对象。 IE。传递给新位置的内存指针的正确类型是“void*”。正如您所做的那样,将其转换为类型“(TYPE *)”是无害的,但没用。 'DESTRUCT' 宏中的强制转换的意义也不是很清楚。

        顺便说一句,拥有这些宏有什么意义?你说他们“让使用placement new 更容易一些”,但他们似乎所做的只是执行一些完全不必要的演员表。

        【讨论】:

        • 存储块是一个字节块,我将它转换为 TYPE 指针,以便调用析构函数。这是针对自定义硬件系统上的一些丑陋的早期初始化代码。
        • 如果可以调用析构函数,则说明该对象之前已经构造过了。如果它已经被构造,你应该已经通过一个类型化的指针看到它 - 正是你的放置新宏返回给你的指针。当然,如果你以某种方式“丢失”了那个指针,那么你可以在“一大块字节”上调用析构函数......
        猜你喜欢
        • 2013-03-07
        • 2011-01-10
        • 2013-12-26
        • 1970-01-01
        • 1970-01-01
        • 2019-12-05
        • 2015-03-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多