RCObject
的课程,代表"参考计数对象&#34 ;; RCObject
应该是抽象的,作为框架的基类(EC++3第7项); 应禁止在堆栈上创建RCObject
子类的实例(MEC++1第27项);
[已添加:]
[假设Bear
是RCObject
]
[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
};
CLARIFIED:应该禁止声明/返回指向RCObject
(和子类)的原始指针(很遗憾,我不认为存在实用的强制执行方式)它,即当用户不遵循时触发编译错误)。请参阅上面第3项中的示例源代码;
RCObject
子类的实例应该像Java中的Cloneable
一样可以克隆。 (MEC++1项目25); 子类化RCObject
的用户应该能够为其子类编写"Factory Methods"。即使忽略返回的值(未分配给变量),也不应有内存泄漏。与Objective-C相近的机制是autorelease
;
CLARIFIED: RCObject
子类的实例应该能够包含在适当的std::
或boost::
容器中。我主要需要一个&#34; std::vector
- 喜欢&#34;容器,&#34; std::set
- 喜欢&#34;容器和&#34; std::map
- 喜欢&#34;容器。基线就是
intrusive_ptr<RCObject> my_bear = v[10];
和
m["John"] = my_bear;
按预期工作;
intrusive_ptr
代替shared_ptr
,但我对他们都持开放态度,甚至是其他任何建议; make_shared()
,allocate_shared()
,enable_shared_from_this
()是否有帮助(顺便提一下,enable_shared_from_this
()似乎没有在Boost中得到高度推广 - 它不能甚至可以在smart pointer主页中找到; RCObject
是否应该私下从boost::noncopyable
继承; 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
}
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;
}
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;
}
上述代码(&#34; Smart Version&#34;)显示了预期的使用模式。我不确定这种使用模式是否遵循使用智能指针的最佳实践。如果没有,请纠正我。
make shared_ptr not use delete(accepted answer看起来很优雅!!!它是某种&#34;自定义deallocator&#34; s?我不确定它与{{3}的比较在时间和空间效率方面)
intrusive_ptr
(intrusive_ptr in c++11提及accepted answer和make_shared()
,但我不理解&#34;但是这不允许你使用不同的智能指针类型管理类型&#34; part)
intrusive_ptr: Why isn't a common base class provided?(我从中学到了一些东西,但是这个问题的重点是&#34;将原始指针传递给接受智能指针的函数&#34;)
Is this a valid use of intrusive_ptr?(这个使用了Reference counting with a generic intrusive pointer client,但我担心进一步的子类化会让我头疼 - 我应该zoo::Panda
从zoo::Bear
延伸单独使用,还是应该同时延伸zoo::Bear
和intrusive_base<zoo::Panda>
?)
"CRTP"(Embedded reference count with Boost shared_ptr提到虽然std::enable_shared_from_this()
应该没问题,accepted answer似乎有一些问题)
boost::enable_shared_from_this()
]:有效的C ++:55改进程序和设计的具体方法(第3版)作者:Scott Meyers
答案 0 :(得分:2)
make_shared
在与引用计数器相同的分配块中创建类的实例。我不确定为什么你认为intrusive_ptr
会有更好的表现:当你已经有一些你无法删除的引用计数机器时它很棒,但这不是这里的情况。
对于克隆,我会将它实现为一个免费的函数,它需要一个智能的pojnter并返回相同的内容。它是一个朋友,并在base中调用私有的纯虚拟克隆方法,返回一个指向base的共享指针,然后执行快速智能指针转换为派生的共享指针。如果您更喜欢克隆作为方法,请使用crtp复制此方法(为私有克隆提供类似secret_clone
的名称)。这为您提供了协变智能指针返回类型,而且开销很小。
具有一系列基类的Crtp通常会传递基类和派生类.crtp类派生自base并且具有返回派生的通常self()
。
工厂函数应该返回智能指针。你可以使用自定义删除技巧来获得一个pre-destroy methid调用以进行上次清理。
如果你是完全偏执狂,你可以阻止大多数方法来获得一个原始指针或类的引用:智能指针上的块运算符*。然后,到原始类的唯一途径是显式调用operator->
。
另一种需要考虑的方法是unique_ptr
并引用它们。您需要共享所有权和终身管理吗?它确实使一些问题变得更简单(共享所有权)。
请注意,悬空的弱指针可防止内存被共享带来回收。
始终使用智能指针的一个严重缺点是您不能直接在容器内部使用堆栈实例或实例。这两者都可以带来严重的性能提升。
答案 1 :(得分:1)
- 应禁止在堆栈上创建
醇>RCObject
子类的实例([MEC ++ 1] [mec ++ 1] Item 27);
你的理由是什么? MEC ++给出了“能够自杀的对象”的例子,这可能在游戏框架的背景下有意义。是这样的吗?
如果你坚持避免使用更简单的解决方法,应该可以使用足够智能的智能指针。
请注意,如果是这种情况,您可能还希望禁止使用new[]
在堆栈上创建此类对象的数组 - 这也可以防止删除单个对象。您可能还希望禁止将RCObject用作子对象(其他类中的成员)。这意味着您完全不允许使用RCObject值,并且客户端代码只能通过智能指针处理它们。
- 应该避免声明/返回指向
醇>RCObject
(和子类)的原始指针(不幸的是,我认为没有办法通过发出编译错误来强制执行它);
然后你必然会有一些弱点指示说“我对这个对象很感兴趣,但我不能让它保持活力”。
- 子类化
醇>RCObject
的用户应该能够为其子类编写[“Factory Methods”] [factory_method]。即使忽略返回的值(未分配给变量),也不应有内存泄漏。
此类函数将返回一个临时智能指针对象,引用计数等于1.如果此临时值不用于初始化另一个(从而进一步递增引用计数),它将清理对象。你很安全。
- 的内容 醇>
RCObject
子类的实例应该能够包含在std::
或boost::
容器中(或任何适当的容器)。我主要需要类似于std::vector
,std::set
和std::map
;
这种不同意(3)。如果你坚持要在堆上单独创建对象并通过智能指针(而不是值)传递,那么你还应该使用智能指针的容器。
- 出于性能方面的考虑,我想使用[
intrusive_ptr
] [intrusive_ptr]而不是[shared_ptr
] [shared_ptr],但我对他们两个甚至任何其他建议都持开放态度。
你不是过早优化吗?
此外,我相信使用侵入式指针会消除使用弱引用的可能性 - 正如我之前提到的那样,这很可能是你需要的。
- 我想知道
RCObject
是否应该私下从[boost::noncopyable
] [noncopyable]继承;
如果您不允许使用值类型的变量并提供虚拟克隆,那么可能不需要公共副本构造函数。您可以制作私有副本ctor并在定义克隆时使用它。