使用 shared_ptr 和 weak_ptr
为了让您的B 对象在A 中一直处于使用状态,您应该在A 中有一个std::weak_ptr<B> 类型的数据成员,这将允许访问创建的@987654329 @对象,只要它还活着。
computeB 的返回值将是 std::shared_ptr<B>,如果后者持有 nullptr,则可以从 std::weak_ptr<B> 成员获取或创建。
线程安全
创建或获取现有B的决定应该是线程安全的。为此,您应尝试使用lock() 方法获取weak_ptr 持有的实际B,然后仅当返回值为nullptr 时创建一个新的。
代码如下所示:
class A {
// stuff
public:
std::shared_ptr<B> ComputeB() {
std::shared_ptr<B> shared_b = my_B.lock();
if (!shared_b){
shared_b = std::make_shared<B>(*this);
my_B = shared_b;
}
return shared_b;
}
// no need for a destructor, unless "stuff" needs one
// ~A(){}
private:
std::weak_ptr<B> my_B;
};
复制和分配
上述类在复制和赋值中的行为是有问题的,因为默认的复制构造函数和默认的赋值运算符会执行成员方式的复制/赋值,这可能导致两个不同的A 持有一个weak_ptr相同的B。很可能这不是您想要的,尤其是如果 A 是可变的(即可以更改其内部值)。
为了呈现复制和赋值的建议代码,我们假设A 拥有一个 int 成员。代码将如下所示:
class A {
int i;
public:
A(int i1): i(i1) {}
void set(int i1) { i = i1; }
std::shared_ptr<B> ComputeB() {
std::shared_ptr<B> shared_b = my_B.lock();
if (!shared_b){
shared_b = std::make_shared<B>(*this);
my_B = shared_b;
}
return shared_b;
}
A(const A& a): i(a.i) {}
A& operator=(const A& a) { i = a.i; return *this; }
~A() {}
private:
std::weak_ptr<B> my_B;
};
保持常量
在上面的代码中,不能在 const A 对象上调用 ComputeB()。如果我们想支持我们需要这个函数的 const 版本。就语义而言,我更喜欢将此方法(const 和 non-const 版本)重命名为 getB。
为了展示建议的代码,添加在const A 对象上调用getB 的选项,我们还需要展示一个类B 的示例,它能够保存一个 const 或非常量对A的引用。代码将如下所示:
class A {
int i;
// to prevent code duplication for the const and non-const versions
template<typename AType>
static auto getB(AType&& a) {
std::shared_ptr<B> shared_b = a.my_B.lock();
if (!shared_b){
shared_b = std::make_shared<B>(std::forward<AType>(a));
a.my_B = shared_b;
}
return shared_b;
}
public:
A(int i1): i(i1) {}
void set(int i1) { i = i1; }
std::shared_ptr<B> getB() {
return getB(*this);
}
std::shared_ptr<const B> getB() const {
return getB(*this);
}
A(const A& a): i(a.i) {}
A& operator=(const A& a) { i = a.i; return *this; }
~A() {}
private:
mutable std::weak_ptr<B> my_B;
};
对于 B:
class B {
union Owner {
A* const ptr;
const A* const const_ptr;
Owner(A& a): ptr(&a) {}
Owner(const A& a): const_ptr(&a) {}
} owner;
public:
B(A& a): owner(a) {}
B(const A& a): owner(a) {}
const A& getOwner() const {
return *owner.const_ptr;
}
A& getOwner() {
return *owner.ptr;
}
};
关于使用union管理同一指针的const和non-const版本,见:
工作示例:http://coliru.stacked-crooked.com/a/f696dfcf85890977
私有创建令牌
上面的代码允许任何人创建 B 的对象,这可能会导致不希望的可能性,例如通过获取 const A& a 的构造函数创建非 const B 对象,从而可能会从 const 转换为 non -const 调用getOwner()时。
一个好的解决方案可能是阻止B 的创建并仅允许来自A 类。由于创建是通过make_shared 将B 的构造函数放在B 的private 部分中完成的,而A 的friend 声明将无济于事,因此调用的不是A new B 是 make_shared。因此,我们采用私有令牌方法,如下代码所示:
class A {
int i;
// only authorized entities can create B
class B_PrivateCreationToken {};
friend class B;
template<typename AType>
static auto getB(AType&& a) {
std::shared_ptr<B> shared_b = a.my_B.lock();
if (!shared_b){
shared_b = std::make_shared<B> (
std::forward<AType>(a),
B_PrivateCreationToken{} );
a.my_B = shared_b;
}
return shared_b;
}
public:
// public part as in above version...
private:
mutable std::weak_ptr<B> my_B;
};
对于 B:
class B {
union Owner {
A* const ptr;
const A* const const_ptr;
Owner(A& a): ptr(&a) {}
Owner(const A& a): const_ptr(&a) {}
} owner;
public:
B(A& a, A::B_PrivateCreationToken): owner(a) {}
B(const A& a, A::B_PrivateCreationToken): owner(a) {}
// getOwner methods as in above version...
};
代码:http://coliru.stacked-crooked.com/a/f656a3992d666e1e