c++ - 与智能指针(intrusive_ptr)一起使用的抽象基类-处理继承,多态性,可克隆性以及从工厂方法返回

标签 c++ inheritance boost polymorphism reference-counting

要求

  • 我正在编写一个名为RCObject的类,它表示“引用计数对象”;
  • RCObject类应该是抽象的,用作框架的基类(EC++3项目7);
  • 应该禁止在堆栈上创建RCObject子类的实例(MEC++1项目27);

    [添加:]

    [假定BearRCObject的具体子类]

    [这里的C.E.表示编译错误]
    Bear b1;                        // Triggers C.E. (by using MEC++1 Item 27)
    Bear* b2;                       // Not allowed but no way to trigger C.E.
    intrusive_ptr<Bear> b3;         // Recommended
    
    Bear* bs1 = new Bear[8];                   // Triggers C.E.
    container< intrusive_ptr<RCObject> > bs2;  // Recommended
    intrusive_ptr_container<RCObject> bs3;     // Recommended
    
    class SomeClass {
    private:
        Bear m_b1;                 // Triggers C.E.
        Bear* m_b2;                // Not allowed but no way to trigger C.E.
        intrusive_ptr<Bear> m_b3;  // Recommended
    };
    
  • 已更正:应该禁止声明/返回RCObject(和子类)的原始指针(不幸的是,我不认为存在强制实现它的实际方法,即,当用户不遵循时触发编译错误)。参见上面第3项中的示例源代码;
  • 就像Java中的 RCObject 一样,Cloneable子类的实例应该是可克隆的。 (MEC++1项目25);
  • 子类化RCObject的用户应该能够为其子类编写"Factory Methods"。即使返回值被忽略(未分配给变量),也不应有内存泄漏。与之接近的机制是Objective-C中的autorelease

    [添加: cschwanKos指出,返回“smart-pointer-to-RCObject”足以满足要求。 ]
  • 已清除: RCObject子类的实例应能够包含在适当的std::boost::容器中。我主要需要一个“类似于std::vector的”容器,一个“类似于std::set的”容器和一个“类似于std::map的”容器。基线是
    intrusive_ptr<RCObject> my_bear = v[10];
    


    m["John"] = my_bear;
    

    按预期工作;
  • 源代码应该可以使用具有有限的C++ 11支持的C++ 98编译器进行编译(确切地说是Visual Studio 2008和gcc 4.6)。

  • 更多信息
  • 我是Boost的初学者(Boost非常大,我需要一些时间来熟悉它。可能有现成的现成解决方案,但是很有可能我不知道这样的解决方案解);
  • 出于性能方面的考虑,我想使用 intrusive_ptr 而不是 shared_ptr ,但是我对两者都持开放态度,甚至对其他建议也持开放态度。
  • 我不知道 make_shared() allocate_shared() enable_shared_from_this ()是否可以帮助(顺便说一句,在Boost中, enable_shared_from_this ()似乎没有得到很大的 boost -甚至在smart pointer主页中也找不到)。
  • 我听说过“编写自定义分配器”,但是恐怕它太复杂了。
  • 我想知道RCObject是否应该从 boost::noncopyable 私下继承;
  • 我找不到满足我所有要求的任何现有实现;
  • 该框架是一个游戏引擎。
  • 主要目标平台是Android和iOS;
  • 我知道intrusive_ptr_add_ref()intrusive_ptr_release()以及如何使用Argument-Dependent Lookup (aka. Koenig Lookup)来实现它们;
  • 我知道如何将 boost::atomic_size_t boost:intrusive_ptr一起使用。

  • 类定义
    namespace zoo {
        class RCObject { ... };                  // Abstract
        class Animal : public RCObject { ... };  // Abstract
        class Bear : public Animal { ... };      // Concrete
        class Panda : public Bear { ... };       // Concrete
    }
    

    “非智能”版本-createAnimal()[工厂方法]
    zoo::Animal* createAnimal(bool isFacingExtinction, bool isBlackAndWhite) {
        // I wish I could call result->autorelease() at the end...
        zoo::Animal* result;
    
        if (isFacingExtinction) {
            if (isBlackAndWhite) {
                result = new Panda;
            } else {
                result = new Bear;
            }
        } else {
            result = 0;
        }
    
        return result;
    }
    

    “非智能”版本-main()
    int main() {
        // Part 1 - Construction
        zoo::RCObject* object1 = new zoo::Bear;
        zoo::RCObject* object2 = new zoo::Panda;
        zoo::Animal* animal1 = new zoo::Bear;
        zoo::Animal* animal2 = new zoo::Panda;
        zoo::Bear* bear1 = new zoo::Bear;
        zoo::Bear* bear2 = new zoo::Panda;
        //zoo::Panda* panda1 = new zoo::Bear;  // Should fail
        zoo::Panda* panda2 = new zoo::Panda;
    
        // Creating instances of RCObject on the stack should fail by following
        // the method described in the book MEC++1 Item 27.
        //
        //zoo::Bear b;                         // Should fail
        //zoo::Panda p;                        // Should fail
    
        // Part 2 - Object Assignment
        *object1 = *animal1;
        *object1 = *bear1;
        *object1 = *bear2;
        //*bear1 = *animal1;                   // Should fail
    
        // Part 3 - Cloning
        object1 = object2->clone();
        object1 = animal1->clone();
        object1 = animal2->clone();
        //bear1 = animal1->clone();            // Should fail
    
        return 0;
    }
    

    “智能”版本[未完成!]
    /* TODO: How to write the Factory Method? What should be returned? */
    
    #include <boost/intrusive_ptr.hpp>
    
    int main() {
        // Part 1 - Construction
        boost::intrusive_ptr<zoo::RCObject> object1(new zoo::Bear);
        boost::intrusive_ptr<zoo::RCObject> object2(new zoo::Panda);
        /* ... Skip (similar statements) ... */
        //boost::intrusive_ptr<zoo::Panda> panda1(new zoo::Bear); // Should fail
        boost::intrusive_ptr<zoo::Panda> panda2(new zoo::Panda);
    
        // Creating instances of RCObject on the stack should fail by following
        // the method described in the book MEC++1 Item 27. Unfortunately, there
        // doesn't exist a way to ban the user from declaring a raw pointer to
        // RCObject (and subclasses), all it relies is self discipline...
        //
        //zoo::Bear b;                         // Should fail
        //zoo::Panda p;                        // Should fail
        //zoo::Bear* pb;                       // No way to ban this
        //zoo::Panda* pp;                      // No way to ban this
    
        // Part 2 - Object Assignment
        /* ... Skip (exactly the same as "non-smart") ... */
    
        // Part 3 - Cloning
        /* TODO: How to write this? */
    
        return 0;
    }
    

    上面的代码(“智能版本”)显示了预期的使用模式。我不确定这种使用模式是否遵循使用智能指针的最佳实践。如果没有,请纠正我。

    类似问题
  • make shared_ptr not use delete(accepted answer看起来很优雅!!!是某种“自定义分配器”吗?在时间和空间效率方面我不确定它与 intrusive_ptr 相比)
  • intrusive_ptr in c++11(accepted answer提到 make_shared() enable_shared_from_this (),但我不理解“但是不允许您使用不同的智能指针类型来管理类型”部分)
  • Is there a way to increase the efficiency of shared_ptr by storing the reference count inside the controlled object?(仍在消化)
  • intrusive_ptr: Why isn't a common base class provided?(答案不够详细)
  • Is this a valid use of intrusive_ptr?(我从中学到了一些东西,但是这个问题的重点是“将原始指针传递给接受智能指针的函数”)
  • Reference counting with a generic intrusive pointer client(此人使用了"CRTP",但是我担心进一步的子类化会让我头疼-我应该让zoo::Panda单独从zoo::Bear扩展,还是应该让它同时扩展zoo::Bearintrusive_base<zoo::Panda>?)
  • Embedded reference count with Boost shared_ptr(accepted answer提到虽然std::enable_shared_from_this()应该可以,但 boost::enable_shared_from_this() 似乎有一些问题)

  • 引用文献
  • [EC++3]:有效的C++:55种改善程序和设计的特定方法(第三版),作者:Scott Meyers
  • 项目7:在多态基类
  • 中声明虚拟的析构函数


  • [MEC++1]:更有效的C++:改进程序和设计的35种新方法(第一版),作者:Scott Meyers
  • 项目25:虚拟化构造函数和非成员函数
  • 项目27:要求或禁止基于堆的对象。

  • 文章
  • [atomic]:boost::atomic的用法示例-Boost.org
  • [section]:引用计数


  • [CP8394]:智能指针来 boost 您的代码-CodeProject
  • [section]:intrusive_ptr-轻量级共享指针


  • [DrDobbs]:C++中侵入式引用计数对象的基类-Dobb博士
  • [page1]:第1页
  • [page2]:第2页
  • [page3]:第3页
  • 最佳答案

    make_shared在与引用计数器相同的分配块中创建类的实例。我不确定为什么您认为intrusive_ptr会具有更好的性能:当已经有无法删除的引用计数机制时,这很棒,但是在这里情况并非如此。

    对于克隆,我将其实现为一个自由函数,该函数需要一个智能指针并返回相同的指针。它是一个 friend ,并在base中调用私有(private)的纯虚拟克隆方法,该方法将共享指针返回至base,然后将快速智能指针强制转换为对派生对象的共享指针。如果您希望使用克隆作为方法,请使用crtp来复制它(为私有(private)克隆提供一个名称,例如secret_clone)。这为您提供了协变智能指针返回类型,而开销却很小。

    具有一系列基类的Crtp通常会同时传递基类和派生类。crtp类是从基类派生的,并具有返回派生的通常的self()

    工厂函数应返回智能指针。您可以使用自定义删除器技巧来获得销毁前的方法调用以进行最后的清理。

    如果您完全偏执,则可以阻止大多数方法来获取原始指针或对类的引用:智能指针上的block operator *。然后,到达原始类的唯一途径是对operator->的显式调用。

    要考虑的另一种方法是unique_ptr及其引用。您需要共享所有权和终身管理吗?确实使一些问题变得更简单(共享所有权)。

    请注意,悬空的弱指针会阻止使内存共享共享带回。

    始终使用智能指针的严重缺点是,不能在容器内部直接具有堆栈实例或实例。两者都可以极大地 boost 性能。

    关于c++ - 与智能指针(intrusive_ptr)一起使用的抽象基类-处理继承,多态性,可克隆性以及从工厂方法返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15474684/

    相关文章:

    c++ - 获取用户定义类的实例

    node.js - 检查nodejs中的继承

    c++ - allocate_shared 附加字节缓冲区

    c++ - 访问包含 boost::shared_ptr<CustomObj> 的 std::vector -> at() 是否返回共享指针的拷贝?

    c++ - opencv 命名空间和 c、c++ 函数

    c++ - 奇怪的编译器行为(C++)

    python - 实现代理对象

    C++ 构造函数成员初始化列表,对象切片

    c++ - 如何在 Windows 上安装 boost

    C++ 终止线程