C++ 使用智能指针跟踪可能被删除的数据成员
我有两个班C++ 使用智能指针跟踪可能被删除的数据成员,c++,smart-pointers,datamember,C++,Smart Pointers,Datamember,我有两个班A和B。我从a中确定地计算aB。对于每个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);
A
和B
。我从a
中确定地计算aB
。对于每个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
)的等价物?您可以从ComputeB()返回std::shared_ptr,并使my_B成为std::weake_ptr。大概是这样的:
std::shared_ptr<B> ComputeB() {
if (my_B.expired()) {
auto result = std::make_shared<B>(*this);
my_B = result;
return result;
} else {
return std::shared_ptr<B>(my_B);
}
}
private:
std::weak_ptr<B> my_B;
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;
};
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;
};
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;
};
std::shared_ptr ComputeB(){
如果(我的B.expired()){
自动结果=标准::使_共享(*此);
我的B=结果;
返回结果;
}否则{
返回std::shared_ptr(my_B);
}
}
私人:
标准:弱ptr我的B;
其思想是,ComputeB的任何调用方都将成为B实例的部分所有者,这意味着只有当所有共享的PTR都被销毁时,才会销毁该实例。弱\u ptr的目的是指向B实例而不拥有它,因此使用共享\u ptr和弱\u ptr,生存期根本不与A实例绑定
为了使B
对象在A
中一直处于活动状态,您应该在A
中有一个std::weak_ptr
类型的数据成员,这将允许访问创建的B
对象,只要该对象处于活动状态
computeB
的返回值将是std::shared_ptr
,该值将从std::弱_ptr
成员获取,或者如果后者持有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
的决定应是线程安全的。为此,您应尝试使用lock()
方法获取weak_ptr
持有的实际B
,然后仅当返回值为nullptr
时,才创建一个新值
代码如下所示:
std::shared_ptr<B> ComputeB() {
if (my_B.expired()) {
auto result = std::make_shared<B>(*this);
my_B = result;
return result;
} else {
return std::shared_ptr<B>(my_B);
}
}
private:
std::weak_ptr<B> my_B;
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;
};
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;
};
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;
};
保持恒定 在上面的代码中,对
ComputeB()
的调用不能在const a
对象上完成。如果我们想支持这个功能,我们需要一个常量版本的函数。就语义而言,我更喜欢将此方法(常量和非常量版本)重命名为getB
要显示建议的代码,该代码添加了对常量a
对象调用getB
的选项,我们还需要显示类B
的示例,该类可以保存对a
的常量或非常量引用。代码将如下所示:
std::shared_ptr<B> ComputeB() {
if (my_B.expired()) {
auto result = std::make_shared<B>(*this);
my_B = result;
return result;
} else {
return std::shared_ptr<B>(my_B);
}
}
private:
std::weak_ptr<B> my_B;
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;
};
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;
};
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;
};
关于使用union
管理同一指针的常量和非常量版本,请参见:
私有创建令牌 上面的代码允许任何人创建
B
的对象,这可能会导致不期望的可能性,例如通过获取const a&a
的构造函数创建非常量B
对象,从而在调用getOwner()
时可能从常量转换为非常量
一个好的解决方案可能是阻止B
的创建,并且只允许从类A
创建它。由于创建是通过make_shared
将B
的构造函数放入B
的private
部分来完成的,因此a
的friend
声明对a
没有帮助,调用new B
的不是a
。因此,我们采用私有令牌方法,如下代码所示:
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;
};
代码:您对智能指针做了哪些研究,您不清楚智能指针的哪一部分?@t.niese最后一行是我的问题。如果有人能用聪明的指针回答这个问题,那么我的问题就得到了回答。尽管我也试图让这个问题向非智能指针解决方案开放。“当B被破坏时,什么会导致
my_B
引用nullptr
(或whattype
)的等价物?”这是有意义的。对我来说,弱ptr
的工作原理与它的工作原理之间的内在区别是很奇怪的。如果ComputeB
返回一个B
(存储在堆栈上),这样当B
离开作用域时被删除时,weak\u ptr
将过期?不,weak\u ptr专门用于共享的\u ptr,因为它可以使用shared_ptr的内部引用计数来查看对象是否被破坏,但在我看来,您不能在这里返回常规的B实例,因为如果您这样做了,您将不得不在每次调用函数时创建一个新实例,并且每次都会用指向最新实例的指针覆盖my_B。请注意,expired
的使用会导致代码不是线程安全的。可能发生的情况是,my_B
在通过检查后立即过期-因此ComputeB将返回null ptr。最好使用my_B.lock()
如果它是null create,则返回它。@AmirKirsh“最好使用my_B.lock()
如果它是null create,则返回它。”如果您能澄清的话,您在这里的措辞有点混乱。