aliasing constructor
shared_ptr 提供了这样的一个构造函数,形式如下:
template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;
这个构造函数称为 aliasing constructor。
这个构造函数的作用如下:
引用自【1】aliasing constructor
The object does not own p, and will not manage its storage. Instead, it co-owns x’s managed object and counts as one additional use of x. It will also delete x’s pointer on release (and not p).
It can be used to point to members of objects that are already managed.
给一个用法用来介绍 The object does not own p:
// shared_ptr constructor example
#include <iostream>
#include <memory>
struct C { int* data; };
int main() {
//....
std::shared_ptr<C> obj(new C);
{
obj->data = new int{};
std::cout << *obj->data << std::endl;
}
std::shared_ptr<int> p(obj, obj->data);
{//共享计数块
std::cout << "use_count:\n";
std::cout << "p: " << p.use_count() << '\n';
std::cout << "value:\n";
std::cout << "*p: " << *p << '\n';
}
//手动delete
std::cout << p.get() << std::endl;
delete p.get();
{
std::cout << "use_count:\n";
std::cout << "obj: " << obj.use_count() << '\n';
std::cout << "p: " << p.use_count() << '\n';
}
int * m = new int{};
std::cout << m << std::endl;
{//reset
std::cout << p.get()<<std::endl;
//在此之前我们并没有释放 p指向堆空间地址的所有权,但是没有异常,是因为p之前构造时传入的指针并没有被p“占有”。
p.reset(m);
std::cout << *p << '\n';
std::cout << "obj: " << obj.use_count() << '\n';
std::cout << "p: " << p.use_count() << '\n';
std::cout << "*p: " << *p << '\n';
}
//不推荐这种删除方式,除非是 aliasing
delete p.get();
//如果你没有 释放所有权,不好意思,你挂了。
p.reset();
return 0;
}
owner_before
owner_before的作用使得三种情况保证在shared_ptr加入关联容器种,可以正确的进 行插入,首先我们要知道关联容器的插入操作是根据等价来进行插入的,1.对于具有继承关系的类,shared_ptr 重载<符号进比较,由decltype(_Always_false<_Ty1>::value? _Left.get() : _Right.get())>
推断类型关系,同样owner_before也可以进行比较,与<相同,2. 当分享指针是
std::sharedptr<void>
类型该怎么比较,和3. 对于由aliasing constructor 构造的shared_ptr该如何比较这时就用到了owner_before。
第一个例子:
//作者:陈硕
//链接:https://www.zhihu.com/question/24816143/answer/29080141
class BaseA { int a; };
class BaseB { double b; };
class Derived: public BaseA, public BaseB {};
int main()
{
std::shared_ptr<Derived> pd(new Derived);
std::shared_ptr<BaseB> pb(pd);
printf("%p %p\n", pd.get(), pb.get());
printf("%d %d\n", pd < pb, pb < pd); // 0 0
printf("%d %d\n", pd.owner_before(pb), pb.owner_before(pd)); // 0 0
std::shared_ptr<void> p0(pd), p1(pb);
printf("%p %p\n", p0.get(), p1.get());
printf("%d %d\n", p0 < p1, p1 < p0); // 1 0
printf("%d %d\n", p0.owner_before(p1), p1.owner_before(p0)); // 0 0
}
第二个例子:
std::shared_ptr<C> obj(new C);
{
obj->data = new int{};
std::cout << *obj->data << std::endl;
}
std::shared_ptr<int> p(obj, obj->data);
auto a = p.owner_before(obj);
auto b = obj.owner_before(p);
在陈硕的博客中指出,引用计数是分配在堆上的,是为了在任何情况下指针都可以正常的析构则在引用计数的块中保留了类型信息。
std::shared_ptr<void> p0(pd), p1(pb);
这种情况,即使p0,p1是负责析构的分享指针也能够正常的析构所保存的对象。
同样在 aliasing constructor 能正确析构也是这个原理。
为了更方便的使用owner_before提供了std::owner_less这个方法,方法说明如下:
This class defines function objects that perform an owner-based comparison between shared_ptr and/or weak_ptr objects.
This is a replacement for less (or operator<) to be used for these types when sorting needs to be based on their owned pointer instead of their stored pointer (which is what is compared by operator<).
The stored pointer of a shared_ptr object (i.e., the pointer it dereferences to) may be different from the owned pointer (i.e., the pointer deleted on object destruction) if the shared_ptr object is an alias (alias-constructed objects and their copies).
补充:
我记得在《Effective STL 50条有效使用STL的经验》 种第19条种说等价与相等的区别:
我用自己的描述如下:
相等的概念是基于 operator ==的,而等价关系是基于在“已有排序的区间种对象值的相对顺序”也就是说等价关系是 a<b and b<a , 是基于operator < ,这里还有一个坑就是当判断两个对象等价则 ,若等价则等价关系为 false 。这里有一个名词,叫严格的弱序化。如果有兴趣可以查一查。