我有以下类结构:
class Bar {
private:
std::shared_ptr<std::vector<int>> y_;
public:
Bar(std::shared_ptr<std::vector<int>> p):
y_(p)
{}
int getFirstVal() {return y_->at(0);};
};
class Initialiser {
private:
std::vector<int> x_;
Bar bar_;
public:
Initialiser(std::vector<int>& v):
x_(v),
bar_(std::make_shared<std::vector<int>>(x_))
{}
void set_x(std::vector<int> x) {x_ = x;}
void check(std::vector<int> x){
set_x(x);
std::cout << "New value of x= " << bar_.getFirstVal() << std::endl;
}
};
int main(){
std::vector<int> z = {1,2,3};
Initialiser init(z);
std::vector<int> x_new = {4,5,6};
init.check(x_new); // should print 4
}
Run Code Online (Sandbox Code Playgroud)
在初始化器类中,我有一个整数向量和 Bar 类的实例作为私有成员。Bar 应使用指向整数向量的共享指针进行初始化,并且该向量应在初始化器和 Bar 之间共享。然而目前,如果初始化器调用 x 的 setter,这不会影响 bar 内的向量。我在check()中演示了这一点。为什么check()不打印值 4,但仍然打印值 1?
Initialiser(std::vector<int>& v):
x_(v),
bar_(std::make_shared<std::vector<int>>(x_))
{}
Run Code Online (Sandbox Code Playgroud)
您创建一个shared_ptr,它指向一个新向量,即x_的副本。当然,修改其中一个不会影响另一个。
直接的解决方案是让 Initializer 和 Bar 都保存向量的共享指针:
class Initialiser {
private:
std::shared_ptr<std::vector<int>> x_;
Bar bar_;
public:
Initialiser(std::vector<int>& v):
x_(std::make_shared<std::vector<int>>(v)),
bar_(x_)
{}
};
Run Code Online (Sandbox Code Playgroud)
但由于 Bar 对象是 Initialiser 对象的一部分,因此实际上根本不需要智能指针。Bar 可以简单地保存对向量的引用。这里不存在生命周期管理问题,因为 bar_ 永远不会比它的容器寿命长,容器也不会比 bar_ 寿命长。
class Bar {
private:
std::vector<int>& y_;
public:
Bar(std::vector<int>& p):
y_(p)
{}
int getFirstVal() {return y_.at(0);};
};
class Initialiser {
private:
std::vector<int> x_;
Bar bar_;
public:
Initialiser(std::vector<int>& v):
x_(v),
bar_(x_)
{}
};
Run Code Online (Sandbox Code Playgroud)
但请注意,x_ 和 bar_ 的顺序很重要,因此有点脆弱。成员变量的构造顺序是它们在类定义中出现的顺序,而不是它们在成员初始化列表中的顺序。如果顺序颠倒,程序的行为将是未定义的,因为 bar_ 将在 x_ 之前构造。
这种正确的顺序还确保 x_ 在对象销毁期间不会比 bar_ 活得更久,因为成员是按照与初始化相反的顺序销毁的。