【问题标题】:C++: Store class instance in allocated memoryC ++:将类实例存储在分配的内存中
【发布时间】:2011-05-22 06:57:11
【问题描述】:

我有一堂课A。我正在使用的某个库为我分配给定数量的内存 sizeof A,并返回一个指向它的 void 指针。

A* pObj = (A*) some_allocator_im_forced_to_use( sizeof A );

现在如何在刚刚分配的内存中创建A 的新实例?

我试过了:

*pObj = A();

但这不起作用 - 在 A 的构造函数之后立即调用了析构函数。

【问题讨论】:

    标签: c++ pointers malloc


    【解决方案1】:

    你可以使用placement new:

    A* pObj = new ( some_allocator_im_forced_to_use( sizeof( A ) ) ) A;
    

    这会在函数some_allocator_im_forced_to_use 返回的位置构造一个新的A 实例。此函数需要返回一个 void* 指向足够的内存,并为相关实现中的 A 对象适当对齐。

    您可能必须手动调用此对象的析构函数,因为您将无法使用通常的delete pObj 来销毁它。

    pObj->~A();
    // Call custom allocator's corresponding deallocation function on (void*)pObj
    

    为了正确销毁对象并释放内存,您可以考虑使用带有自定义删除器的shared_ptr

    【讨论】:

      【解决方案2】:

      使用新的展示位置:

      new (pObj) A;
      

      您还需要以不同的方式销毁对象,因为delete 假定(错误地)您使用普通的旧new 创建了对象:

      pObj->~A();
      some_deallocator_im_forced_to_use( pObj );
      

      【讨论】:

        【解决方案3】:

        你需要'新位置'

        看到这个答案:What uses are there for "placement new"?

        【讨论】:

          【解决方案4】:

          【讨论】:

            【解决方案5】:

            有些人主张安置新。

            这很危险,到目前为止,两个使用示例都省略了重要的细节,例如包括 <new> 标头、限定调用(如 ::new ...)以及如何清理。

            安全的解决方案是为您的类或从您的类派生的类定义自定义分配和释放函数。下面的示例显示了后者。请注意,虚拟析构函数仅用于支持类派生;如果你不派生,你就不需要它。

            #include <stddef.h>
            #include <iostream>
            
            void* some_allocator_im_forced_to_use( size_t size )
            {
                std::cout << "Allocated " << size << " bytes." << std::endl;
                return ::operator new( size );
            }
            
            void some_deallocator_im_forced_to_use( void* p, size_t size )
            {
                std::cout << "Deallocated " << size << " bytes." << std::endl;
                ::operator delete( p );
            }
            
            struct A
            {
                int x_;
                A(): x_( 42 ) {}
                virtual ~A() {}
            };
            
            struct CustomAllocedA
                : A
            {
                void* operator new( size_t size )
                {
                    return some_allocator_im_forced_to_use( size );
                }
            
                void operator delete( void* p, size_t size )
                {
                    some_deallocator_im_forced_to_use( p, size );
                }
            };
            
            int main()
            {
                A* const    p    = new CustomAllocedA;
            
                std::cout << p->x_ << std::endl;
                delete p;
            }
            

            重要提示:尽管这本身是“安全的”,并且尽管这个特定示例是安全的,但您的代码的安全性最终取决于您的自定义分配器返回指向正确对齐内存的指针。

            干杯,

            【讨论】:

            • +1 覆盖 new 运算符绝对比使用placement new 好得多。
            【解决方案6】:

            如果你的分配器遵循std::allocator的模型,它应该有几个方法:

            • allocate
            • construct
            • destroy
            • deallocate

            它们几乎是不言自明的,重要的是您有责任调用它们:

            • 按正确的顺序
            • 不要忘记任何东西

            否则你会得到 UB 或泄漏。

            但我有点惊讶的是,您的分配器只需要大小,通常它还需要对象的对齐,除非它只是简单地使用目标上可能的最大对齐方式,而不管要分配的对象是什么。

            【讨论】:

              猜你喜欢
              • 2016-02-07
              • 2013-02-10
              • 2019-01-17
              • 2020-09-28
              • 2017-04-04
              • 2015-02-24
              • 1970-01-01
              • 2021-01-01
              • 1970-01-01
              相关资源
              最近更新 更多