C++中的私有operator delete

8
我正在为我的一个项目中的一组对象设计垃圾回收机制。我希望能够使用new动态分配这些对象,并且永远不需要调用delete
通过重载operator new来调用一个实现GC的专门的分配器对象是可以实现的(当分配了过多内存时触发收集)。然而,我有一个问题:用户仍然可以对这些对象执行delete,而我不想让这样的事情发生。
operator delete设置为私有是有问题的,因为C++处理构造失败的方式——如果operator new是公共的,则operator delete也应该是公共的。另一种被建议的替代方案是将operator newoperator delete都设置为私有,并只向用户公开工厂创建方法。我可以这样做,但感觉不太干净,需要编写额外的代码。 编辑:另一种方法是将operator delete设置为空(或抛出异常)。然后,为了实际释放对象,我的GC将显式调用析构函数,然后使用全局的::operator delete释放内存。 还有其他想法吗?

1
只是一个想法:当有人尝试删除一个对象时,抛出一个错误,这应该会教育他们不要这样做 ;) - Stormenet
离题:你如何追踪对象是否超出范围?为什么不使用共享指针? - rmflow
@Stormenet:虽然我本来想在垃圾回收器中使用delete,但我也可以这样做(如果该运算符是私有的,我可以将其设置为friend)。 - Eli Bendersky
1
@rmflow: 我实现了标记和清除垃圾回收,从已知的根节点开始跟踪所有内容。 - Eli Bendersky
如何使operator delete为空?但如果构造函数中有new表达式抛出异常,内存该如何释放?它需要是公共的理由,不仅仅是为了看起来对称;-) [编辑-d'oh,我可以自己回答这个问题。只需确保GC即使尚未构造也可以获取它。但我认为,如果这样做,您必须确保在用户删除对象的情况下不要两次调用析构函数,delete表达式调用析构函数,然后是operator delete,会抛出异常,一段时间后您会GC对象] - Steve Jessop
显示剩余5条评论
3个回答

2

就我个人而言,我认为将私有化和使用工厂模式相结合是更清晰的方法。仅使用new而不使用delete(或分配到智能指针)会让代码维护者感到困惑。

如果您可以表明指针来自GC收集的工厂(或由GC收集的工厂拥有),那么代码维护起来会更加清晰。通过使用工厂,您明确表示GC工厂是所有者,因此应该维护对象的生命周期:

class GCFactory
{
    public:
        template<T, P1>
        T& createGCObject(P1 const& p1) // Or return by pointer.
        {
            T* result = new T(p1);
            // Do stuff to register with garbage collector.

            // Then return object (I like reference) but I have not studied the
            // problem that hard so may be in-appropriate.
            return * result;
        }
        template<T, P1, P2>
        T& createGCObject(P1 const& p1, P2 const& p2)
        {
            T* result = new T(p1, p2);
            // Do stuff to register with garbage collector.

            return * result;
        }
        template<T, P1, P2, P3>
        T& createGCObject(P1 const& p1, P2 const& p2, P3 const& p3)
        {
            T* result = new T(p1, p2, p3);
            // Do stuff to register with garbage collector.

            return * result;
        }
};

这种方法的问题在于,当我有N个不同的对象需要以这种方式分配时,每个对象都有自己的构造函数(一个需要两个参数,另一个需要指针和引用等) - 这会导致大量的代码重复(工厂方法调用相应的构造函数)。 - Eli Bendersky
@Eli:如果对C++0x功能有适当的支持,您可以通过完美转发和可变参数模板解决这个问题。请参阅vector::emplace_back - Steve Jessop
@Steve:啊,C++0x,那个没能实现的承诺...不幸的是,我真的很想让它在几个C ++编译器上运行(包括MSVC,叹气。)所以目前这可能不是一个选项。希望在不久的将来会有所改观! - Eli Bendersky
@Eli Bendersky:通过将所有参数模板化,您可以使用一个方法来处理n个参数。因此,您只需要一个函数来处理所有只有一个参数的构造函数,一个函数来处理所有有两个参数的构造函数等等。虽然不是完美的解决方案,但它可以大大减少代码量。 - Martin York
@Eli:MSVC对C++0x功能的支持可能比你想象的要好 - 它不像C99,微软对C++0x持积极态度,并计划进行更全面的实现。但是,特性支持在多个实现中并不可靠。您还可以查看Boost的操作,它希望使用可变参数模板的地方会在支持它们的地方这样做,否则会退回到Martin在上面评论中提到的内容,即定义具有0 ... n-1个参数的n个模板。如果我没记错,n的典型值为10。 - Steve Jessop

0

将重载删除作为无操作。(需要更多字符)


这里存在一个问题:GC如何实际删除这些对象?我计划使用delete,尽管我猜我可以直接调用析构函数然后释放内存。 - Eli Bendersky
我想你是对的 - 我在“编辑”中添加了它,仍在寻找其他的想法;-) - Eli Bendersky
4
无法实际工作。编译器将 delete x 语句转换为 x->~Type(); Type::operator delete(x)。即对象*将首先被销毁,然后内存不会被无操作的 operator delete 释放。 - Jan Hudec

-2
boost::shared_ptr<Type> ptr = boost::make_shared<Type>(); 

你从不调用 new,也从不调用 delete。

为什么要重复造轮子?智能指针确实是更好的选择。


4
我明白“shared_ptr”存在,但我的问题不是关于它的。 - Eli Bendersky
2
@Chris:引用计数 != 垃圾回收。例如,shared_ptr 在处理循环引用时存在实际问题。 - Ben Voigt
@Ben:'shared_ptr在处理循环引用时存在实际问题' - 你能详细说明一下吗?我想说的是,我不会在2011年尝试重新发明轮子。 - Chris K
@Chris:如果对象C拥有指向对象A的shared_ptr,而对象A又持有指向B的shared_ptr,而对象B又持有指向C的shared_ptr,那么它们中的任何一个都永远不会被销毁。我认为这就是Ben所说的循环引用问题。 - ssube
@Chris:针对这个问题,“不重复造轮子”的方法是使用Boehm的C++垃圾收集器,它采用标记-清除算法。但是,Boehm的GC使用了很多平台相关的技巧,以尽可能地不引人注目 - 在C++中实现可移植的标记-清除收集需要用户的一些帮助,这似乎是Eli所追求的。 - Steve Jessop
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接