shared_ptr中的owner_before解析
shared_ptr中的owner_before成员函数
shared_ptr中的owner_before成员函数的功能为“判断两个指针是否指向同一对象”。
shared_ptr中的owner_pointer和stored_pointer
shared_ptr指针其实根据属性不同可分为两种:
① owner pointer所有权拥有指针:
所有权拥有指针顾名思义就是指“这个指针指向整个自定义数据类型的对象而非该对象的成员数据”。
② stored pointer指向存储在内部的数据的指针:
stored pointer看名称也可以很清晰的理解:这个指针指向自定义数据类型对象的一部分。
两者的关系如下图所示:
什么是“C++中的弱序”?
① ptr和ptr1是不是同类型指针;
② ptr指针指向的地址小于ptr1指针指向的地址;
满足以上两种条件,我们就可以称“ptr<ptr1”。我们很可能想到“求同存异”,这四个字很符合“C++中弱序”的特点,因此我们要理解弱序就必须要理解“弱序到底求得什么同存的什么异”!
但是,我们的疑惑又来了:ptr与ptr1满足什么条件才可以被称之为“同类型指针”呢?
首先,我们知道shared_ptr有两种属性,因此ptr与ptr1的搭配方式有四种:
ptr指针属性 |
ptr1指针属性 |
stored_pointer |
owner_pointer |
owner_pointer |
stored_pointer |
owner_pointer |
owner_pointer |
stored_pointer |
stored_pointer |
每种搭配方式举例说明:
⑴ ptr1是stored_pointer而ptr是owner_pointer
代码示例:
#include <iostream>
using namespace std;
#include <memory>
struct A
{
int age;
double mark;
};
int main()
{
shared_ptr<A> ptr = make_shared<A>();
shared_ptr<int> ptr1(ptr, &ptr->age);
cout << "ptr指针指向的地址为" << ptr.get() << endl;
cout << "ptr1指针指向的地址为" << ptr1.get() << endl;
cout << "ptr1.owner_before(ptr) = " << ptr1.owner_before(ptr) << endl;
cout << "ptr.owner_before(ptr1) = " << ptr.owner_before(ptr1) << endl;
}
运行结果:
在程序中我们看到:
指针名称 |
指针指向的目标 |
ptr |
A类类型对象 |
ptr1 |
A类类型对象中的age成员数据 |
“弱序”中,将指向同一对象的shared_ptr指针作为同一类型的指针:
此时,向上面所示的stored_pointer和owner_pointer两个指针在“弱序”中属于同一类型的指针。
由于ptr和ptr1是同类型指针而且ptr指针等于ptr1指针指向地址,因此:
无论参数顺序如何,都不会影响最终的结果。
⑵ ptr和ptr1指针均为stored_pointer属性的shared_ptr指针
代码示例:
#include <iostream>
using namespace std;
#include <memory>
struct A
{
int age;
double mark;
};
int main()
{
shared_ptr<A> ptr = make_shared<A>();
shared_ptr<int> ptr1(ptr, &ptr->age);
shared_ptr<double> ptr2(ptr, &ptr->mark);
cout << "ptr1指向的地址为" << ptr1.get() << endl;
cout << "ptr2指向的地址为" << ptr2.get() << endl;
cout << "ptr1.owner_before(ptr2) = " << ptr1.owner_before(ptr2) << endl;
cout << "ptr2.owner_before(ptr1) = " << ptr2.owner_before(ptr1) << endl;
}
运行结果:
我们看到ptr1与ptr2指向的地址此时不一样了,但是无论参数位置如何,owner_before成员函数返回值仍为0,这说明“指向同一个自定义类类型对象中不同数据成员的两个stored_pointer属性的shared_ptr指针在‘弱序’中属于同一类型的指针”。ptr与ptr1关系如下所示:
⑶ ptr与ptr1均属于owner_pointer属性的shared_ptr指针
代码示例:
#include <iostream>
using namespace std;
#include <memory>
struct A
{
int ageA;
double markA;
};
struct B
{
int ageB;
double markB;
};
struct AB : public A, public B
{
};
int main()
{
shared_ptr<A> ptr = make_shared<A>();
shared_ptr<B> ptr1 = make_shared<B>();
shared_ptr<AB> ptr2 = make_shared<AB>();
cout << "ptr指向的地址为" << ptr.get() << endl;
cout << "ptr1指向的地址为" << ptr1.get() << endl;
cout << "ptr2指向的地址为" << ptr2.get() << endl;
cout << "ptr(指向class A的对象)与ptr1(指向class B的对象)" << endl;
cout << "ptr > ptr1 = " << ((int)ptr.get() > (int)ptr1.get()) << endl;
cout << "ptr < ptr1 = " << ((int)ptr.get() < (int)ptr1.get()) << endl;
cout << "ptr.owner_before(ptr1)" << ptr.owner_before(ptr1) << endl;
cout << "ptr1.owner_before(ptr)" << ptr1.owner_before(ptr) << endl;
cout << "ptr(指向class A的对象)与ptr2(指向class AB的对象)" << endl;
cout << "ptr > ptr2 = " << ((int)ptr.get() > (int)ptr2.get()) << endl;
cout << "ptr < ptr2 = " << ((int)ptr.get() < (int)ptr2.get()) << endl;
cout << "ptr.owner_before(ptr2)" << ptr.owner_before(ptr2) << endl;
cout << "ptr2.owner_before(ptr)" << ptr2.owner_before(ptr) << endl;
cout << "ptr1(指向class B的对象)与ptr2(指向class AB的对象)" << endl;
cout << "ptr1 > ptr2 = " << ((int)ptr1.get() > (int)ptr2.get()) << endl;
cout << "ptr1 < ptr2 = " << ((int)ptr1.get() < (int)ptr2.get()) << endl;
cout << "ptr1.owner_before(ptr2)" << ptr1.owner_before(ptr2) << endl;
cout << "ptr2.owner_before(ptr1)" << ptr2.owner_before(ptr1) << endl;
}
运行结果:
ptr,ptr1与ptr2的指针指向如下:
我们看上述的继承关系也可以看出“同类型”的指针对如下:
指针名称 |
指针名称 |
ptr |
ptr2 |
ptr1 |
ptr2 |
我们看到上述的“同类型指针对”其实都是“含有继承关系的”,因此在两个owner_pointer属性的shared_ptr指针有继承关系且我们用指向派生类对象的指针初始化指向基类对象的指针时,这两个指针属于“弱序中的同类型指针”。
最终,我们在分析结果时就可以看出“当ptr与ptr2指向的类类型对象含有继承关系时,ptr.owner_before(ptr2)||ptr2.owner_before(ptr) == 0“,即ptr与ptr2这两个指针在”弱序排列规则中“属于同等地位的指针,也就是我们常说的”==”只不过此时的”等于“是针对于弱序规则中的ptr和ptr2两个指针。
对owner_before成员函数进行总结
我们使用“shared_ptr/weak_ptr中的owner_before成员函数“去判断两个指针是否指向”同一对象“并不想知道”两个指针指向地址的先后顺序“,因此我们可以这样做”先使用C++逻辑运算中的逻辑或操作(||),然后再总体上取个非(!)操作“:
#include <iostream>
using namespace std;
#include <memory>
struct A
{
int age;
double mark;
};
int main()
{
shared_ptr<A> ptr = make_shared<A>();
shared_ptr<int> ptr1(ptr, &ptr->age);
cout << "ptr与ptr1指针是否指向同一对象:" << !(ptr.owner_before(ptr1) || ptr1.owner_before(ptr)) << endl;
}
为什么使用逻辑或(||)操作?
因为ptr.owner_before(ptr1)返回true必须满足两个条件:
① ptr和ptr1两个指针指向的对象属于“同一类型的对象“;
② ptr指向的地址必须小于ptr1指向的地址;
当我们将ptr.owner_before(ptr1)的返回结果与ptr1.owner_before(ptr)的返回结果进行逻辑或(||)操作后,(ptr.owner_before(ptr1)||ptr1.owner_before(ptr))这个整体返回的值就排除了“ptr与ptr1两个指针指向地址先后“的干扰,因此如果” (ptr.owner_before(ptr1)||ptr1.owner_before(ptr)) “返回0就代表ptr与ptr1指针指向同一个类型的对象。
但是这样的看有些不爽,我们想要“当“ptr与ptr1指针指向同一个类型的对象”时,返回true,你却此时给我返回false,那么我就取个反就OK了,因此最终用于判断两个指针是否指向同一个类型对象的代码为“!(ptr.owner_before(ptr1)||ptr1.owner_before(ptr))”。
为什么不用>,<逻辑比较符号来进行“弱序中的比较操作”?
① 因为在shared_ptr模板类中重载>,<等逻辑符号两边的操作对象的数据类型必须相同,例如:
在上述例子中,ptr指向class A的对象,ptr1指向class AB的对象,此时如果我们使用逻辑运算符来进行比较,一看操作符两端操作对象类型就不可以,一边为class A类型一边为class AB类型,纵使class A与class AB两个类之间有继承关系,重载的逻辑运算符也不会比较出来什么结果,即没用。
代码示例:
#include <iostream>
using namespace std;
#include <memory>
struct A
{
int age;
int mark;
};
int main()
{
shared_ptr<A> ptr = make_shared<A>();
shared_ptr<int> ptr1(ptr, &ptr->age);
cout << (ptr1 > ptr) << endl; // 因为>逻辑操作符两端操作对象类型不同,因此该行出错
}
运行结果:
② 不会进行“弱序规则下的大小比较”,只根据指针所指向的地址去判断指针的大小顺序
代码示例:
#include <iostream>
using namespace std;
#include <memory>
struct A
{
int age;
int mark;
};
int main()
{
shared_ptr<A> ptr = make_shared<A>();
shared_ptr<int> ptr1(ptr, &ptr->age), ptr2(ptr, &ptr->mark), ptr3(nullptr);
cout << (ptr1 < ptr2) << endl; // 由于ptr1,ptr2指向同一对象,无论如何比较弱序在弱序规则下,ptr1应该与ptr2属于同一类型
cout << (ptr1 > ptr2) << endl;
}
运行结果:
显然逻辑运算符执行的功能不是“弱序下的指针比较大小”。如果遵从弱序下的排序规则,无论使用>还是<号进行比较,最终的逻辑结果都应该是0,即在弱序下ptr与ptr1两个指针处于同等地位。