Siu*_*ji- 19 c++ polymorphism inheritance boost reference-counting
RCObject
"引用计数对象"的类;RCObject
应该是抽象的,作为框架的基类(EC++ 3 Item 7);RCObject
应禁止在堆栈上创建子类的实例(MEC++ 1 Item 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
};
Run Code Online (Sandbox Code Playgroud)CLARIFIED:RCObject
应该禁止声明/返回指向(和子类)的原始指针(不幸的是,我认为没有一种实用的方法来强制执行它,即当用户不遵循时触发编译错误).参见上面第3项中的示例源代码;
RCObject
子类的实例应该像Cloneable
Java 一样可以克隆.(MEC++ 1第25项);用户子类化RCObject
应该能够为其子类编写"工厂方法".即使忽略返回的值(未分配给变量),也不应有内存泄漏.与此相近的机制是autorelease
Objective-C;
CLARIFIED:RCObject
子类的实例应该能够包含在适当的std::
或boost::
容器中.我主要需要一个"类似" std::vector
的容器,一个"类似" std::set
的容器和一个"类似" std::map
的容器.基线就是这样
intrusive_ptr<RCObject> my_bear = v[10];
Run Code Online (Sandbox Code Playgroud)
和
m["John"] = my_bear;
Run Code Online (Sandbox Code Playgroud)
按预期工作;
intrusive_ptr
而不是shared_ptr
,但我对他们两个甚至任何其他建议持开放态度;make_shared()
,allocate_shared()
,enable_shared_from_this
()可以帮助(顺便说一句,enable_shared_from_this
()似乎不高推动了升压-它甚至不能在找到智能指针主页);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
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
/* 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;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码("智能版")显示了预期的使用模式.我不确定这种使用模式是否遵循使用智能指针的最佳实践.如果没有,请纠正我.
make shared_ptr不使用delete(接受的答案看起来很优雅!!!它是某种"自定义解除分配器"吗?我不确定它intrusive_ptr
在时间和空间效率方面的比较)
intrusive_ptr在C++ 11(该接受的答案提到的make_shared()
和enable_shared_from_this
() ,但我不明白"但不允许你使用不同的智能指针类型管理型"的一部分)
intrusive_ptr:为什么不提供通用基类?(答案不够详细)
这是intrusive_ptr的有效用法吗?(我从中学到了一些东西,但这个问题的焦点在于"将原始指针传递给接受智能指针的函数")
与通用侵入指针客户端的引用计数(这一个使用"CRTP",但恐怕进一步的子类都会让我头疼-我应该zoo::Panda
从扩大zoo::Bear
独自一人,或者我应该让双方扩大zoo::Bear
和intrusive_base<zoo::Panda>
?)
嵌入式引用计数与Boost shared_ptr(接受的答案提到虽然std::enable_shared_from_this()
应该没问题,boost::enable_shared_from_this()
似乎有一些问题)
make_shared
在与引用计数器相同的分配块中创建类的实例。我不确定为什么您认为intrusive_ptr
会有更好的性能:当已经存在无法删除的引用计数机制时,这很好,但这里的情况并非如此。
对于克隆,我将其实现为一个自由函数,该函数接受智能指针并返回相同的值。它是一个朋友,并调用基类中的私有纯虚拟克隆方法,该方法返回基类的共享指针,然后执行快速智能指针转换为派生的共享指针。如果您更喜欢克隆作为方法,请使用 crtp 复制此方法(为私有克隆命名,例如secret_clone
)。这为您提供了协变智能指针返回类型,并且开销很小。
具有一系列基类的 Crtp 通常会让您同时传入基类和派生类。crtp 类从基类派生,并具有self()
返回派生类的通常功能。
工厂函数应该返回智能指针。您可以使用自定义删除器技巧来获取预销毁方法调用以进行最后的清理。
如果您完全偏执,您可以阻止大多数获取原始指针或对类的引用的方法:阻止智能指针上的运算符*。那么到达原始类的唯一途径是显式调用operator->
.
另一种需要考虑的方法是unique_ptr
引用相同的内容。您需要共享所有权和终身管理吗?它确实使一些问题变得更简单(共享所有权)。
请注意,悬空弱指针会阻止共享内存被回收。
始终使用智能指针的一个严重缺点是您无法在容器内直接拥有堆栈实例或实例。这两者都可以带来显着的性能提升。
归档时间: |
|
查看次数: |
3038 次 |
最近记录: |