操作员删除功能的放置形式

时间:2013-06-30 15:58:08

标签: c++ placement-new

在他的新书TC++PL4,中,Stroustrup在a once usual practice regarding user-controlled memory allocation and placement new上略有不同 - 或者更具体地说,关于神秘的“位置delete。”在书的教派。 11.2.4,Stroustrup写道:

  

“placement delete”运算符不执行任何操作,除非可能通知垃圾收集器已删除的指针不再安全地派生。

这意味着声音编程实践将遵循a call to placement delete对析构函数的显式调用。

足够公平。但是,没有比隐藏

更好的语法来调用展示位置delete
::operator delete(p);

我问的原因是Stroustrup的教派。 11.2.4没有提到这种奇怪的语法。事实上,Stroustrup没有详述此事;他根本没有提到任何语法。我模糊地不喜欢::operator的外观,它将命名空间解析的问题插入到与名称空间无关的内容中。没有更优雅的语法吗?

作为参考,这里是Stroustrup在更全面的背景下的引用:

  

默认情况下,运算符new在免费商店中创建其对象。什么   如果我们想在其他地方分配对象?...我们可以放置对象   通过提供具有额外参数的分配器函数和任何地方   然后在使用new时提供额外的参数:

void* operator new(size_t, void* p) { return p; }

void buf = reinterpret_cast<void*>(0xF00F);
X* p2 = new(buf) X;
     

由于这种用法,提供额外的new(buf) X语法   operator new()的参数称为放置语法。   请注意,每个operator new()都将大小作为其第一个参数   并且隐式提供分配的对象的大小。   operator new()运算符使用的new由。{1}}选择   通常的参数匹配规则;每个operator new()都有   一个size_t作为它的第一个参数。

     

“placement”operator new()是最简单的分配器。它   在标准标题<new>中定义:

void* operator new (size_t, void* p) noexcept;
void* operator new[](size_t, void* p) noexcept;

void* operator delete (void* p, void*) noexcept; // if (p) make *p invalid
void* operator delete[](void* p, void*) noexcept;
     

“放置delete”操作员除了可能通知a之外什么也不做   垃圾收集器,删除的指针不再安全   的。

Stroustrup然后继续讨论与竞技场一起使用展示位置new。他似乎没有再次提到展示位置delete

3 个答案:

答案 0 :(得分:2)

首先:没有。

但是记忆的类型是什么?确切地说,它没有一个。那么为什么不使用以下内容:

typedef unsigned char byte;

byte *buffer = new byte[SIZE];

Object *obj1 = new (buffer) Object;
Object *obj2 = new (buffer + sizeof(Object)) Object;
...
obj1->~Object();
obj2->~Object();

delete[] buffer;

这样您就不必担心放置删除了。只需将整个事物包装在一个名为Buffer的类中,然后就可以了。

修改

我考虑过你的问题并尝试了很多事情,但我没有找到你称之为placement delete的机会。当您查看<new>标题时,您会看到此函数为空。为了完整起见,我会说它就在那里。即使使用模板,你也可以手动调用析构函数,你知道吗?

class Buffer
{
    private:
        size_t size, pos;
        byte *memory;

    public:
        Buffer(size_t size) : size(size), pos(0), memory(new byte[size]) {}

        ~Buffer()
        {
            delete[] memory;
        }

        template<class T>
        T* create()
        {
            if(pos + sizeof(T) > size) return NULL;
            T *obj = new (memory + pos) T;
            pos += sizeof(T);
            return obj;
        }

        template<class T>
        void destroy(T *obj)
        {
            if(obj) obj->~T(); //no need for placement delete here
        }
};


int main()
{
    Buffer buffer(1024 * 1024);

    HeavyA *aObj = buffer.create<HeavyA>();
    HeavyB *bObj = buffer.create<HeavyB>();

    if(aObj && bObj)
    {
        ...
    }

    buffer.destroy(aObj);
    buffer.destroy(bObj);
}

这个类只是一个竞技场(Stroustrup称之为)。当您必须分配许多对象并且不希望每次调用new的开销时,您可以使用它。恕我直言,这是一个新的/删除的唯一的用例。

答案 1 :(得分:2)

如果您不想使用::,则根本不需要。事实上,你通常不应该(不想)。

您可以为::operator new::operator delete提供替换(以及数组变体,但您绝不应该使用它们。)

但是,您也可以为一个类重载operator newoperator delete(是的,再次,您可以执行数组变体,但仍然不应该使用它们。)

使用类似void *x = ::operator new(some_size);的内容会强制分配直接转到全局operator new,而不是使用特定于类的(如果存在)。当然,一般来说,如果存在,则要使用特定于类的类(如果不存在,则使用全局类)。这正是您使用void *x = operator new(some_size);得到的结果(即没有范围解析运算符)。

与往常一样,您需要确保newdelete匹配,因此在使用{{1}时,您应该只使用::operator delete删除内存分配它。大多数情况下,您不应在任何一个上使用::operator new

主要的例外是当/你实际为某个班级写::operator new时。这些通常会调用operator delete来获取大量内存,然后将其分成对象大小的内容。要分配那么大的内存块,它通常(总是?)必须明确指定::operator new,否则它最终会调用自己来分配它。显然,如果它在分配数据时指定::operator new,则还需要指定::operator new来匹配。

答案 2 :(得分:2)

  

这意味着声音编程实践将通过调用放置删除来跟随对析构函数的显式调用。

不,不。 IIUC Stroustrup并不意味着放置delete 必要来通知垃圾收集器内存不再使用,他的意思是除此之外它没有做任何事情。所有释放函数都可以告诉不再使用垃圾收集器内存,但是当使用放置new自己管理内存时,为什么还要垃圾收集器来调整内存呢?

  

我模糊地不喜欢::operator的外观,它将命名空间解析的问题插入到与名称空间无关的内容中。

“正确”它 与名称空间有关,使其有资格引用“全局operator new”将其与类类型的任何重载operator new区分开来。< / p>

  

不存在更优雅的语法吗?

你可能永远不想打电话给它。如果您使用placement delete并且构造函数抛出异常,则编译器将为放置new运算符调用。由于没有内存要解除分配(因为起搏new没有分配任何内容)所有它都可能将内存标记为未使用。