gpt4 book ai didi

c++ - 使用智能指针跟踪可能被删除的数据成员

转载 作者:行者123 更新时间:2023-12-03 06:56:46 24 4
gpt4 key购买 nike

我有两个类 AB。我根据 A 确定性地计算出 B。对于每个 A,我想用 my_B 跟踪 B,只要它存在。 B 被破坏后,我希望将 my_B 更改为类似 nullptr 的内容。

class A{
// stuff
public:
B ComputeB(){
if (my_B is null){
B result = B(A);
my_B = B; // some kind of reference
return B(A);
}
else {
return my_B;
}
}

~A(){ /* Do I need a destructor? */ }
private:
WhatTypeHere my_B;
}

B 被析构时,什么会导致 my_B 引用 nullptr(或 WhatTypeHere 的等价物) )?

最佳答案

使用 shared_ptr 和 weak_ptr

为了保留您的B A 中的对象只要它还在使用,你就应该在 A 中有一个数据成员类型 std::weak_ptr<B>这将允许访问创建的 B对象,只要它还活着。

computeB 的返回值将是 std::shared_ptr<B>可以从 std::weak_ptr<B> 获得成员或创建者,如果后者持有 nullptr .


线程安全

决定是创建还是获取现有的B应该是线程安全的。为此,您应尝试获取实际的 Bweak_ptr 持有通过使用 lock()方法,则仅当返回值为 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;
};

保持常量

在上面的代码中,对 ComputeB() 的调用不能在 const A 上完成目的。如果我们想要支持,我们需要有这个函数的 const 版本。对于语义问题,我更喜欢将此方法(constnon-const 版本)重命名为 getB .

展示添加调用选项的建议代码 getBconst A 上对象,我们还需要展示类 B 的示例它能够保存对 Aconst非常量 引用.代码将如下所示:

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的使用要管理同一指针的 constnon-const 版本,请参阅:

工作示例:http://coliru.stacked-crooked.com/a/f696dfcf85890977


私有(private)创建 token

上面的代码允许任何人创建 B 的对象这可能会导致不希望的可能性,比如创建一个非常量 B通过获取 const A& a 的构造函数对象,导致在调用 getOwner() 时可能从 const 转换为非常量.

一个好的解决方案可能是阻止创建 B并且只允许它来自 A 类.由于创建是通过 make_shared 完成的放 B private 中的构造函数节Bfriend A的声明没有帮助,它不是 A那是打电话 new B它是 make_shared .因此,我们采用如下代码所示的私有(private) token 方法:

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

关于c++ - 使用智能指针跟踪可能被删除的数据成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63932964/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com