我正在使用一个外部库,并且有一些代码让我停下来。基本上,有一个向量分配在循环内部(在堆栈上)。然后,该矢量通过引用传递给某个对象的构造函数,并用于初始化该对象的矢量字段之一,该字段未声明为引用。新创建的对象是否持有对不再存在的内容的引用?还是这只是复制向量的一种更有效的方式,在这种情况下,将其分配在堆栈上的事实没有任何区别?
这是一个最小的例子:
class Holder {
public:
Holder(vector<int>& vref) : vec(vref) {}
vector<int> vec;
}
Holder* MakeHolder() {
vector<int> v {1, 2};
return new Holder(v);
}
int main() {
Holder *h = MakeHolder();
}
答案 0 :(得分:4)
没有对已离开对象的引用,但我当然不会将其称为“有效”。在 ctor-initialiser 中没有std::move
的情况下,必须复制矢量。
您可以将std::move
放在其中,但是Holder
的使用会有些混乱。
我个人会按值输入向量,因此调用范围可以std::move
放入其中(或传递一个临时值,它将自动执行此操作),然后std::move
将构造函数参数放入新的会员。这样一来,您在整个时间里实际上只有一个向量。
class Holder {
public:
Holder(vector<int> vref) : vec(std::move(vref)) {}
vector<int> vec;
}
Holder* MakeHolder() {
vector<int> v {1, 2};
return new Holder(std::move(v)); // Or just `return new Holder({1,2});`
}
int main() {
Holder *h = MakeHolder();
}
而且,如果您想保持原始矢量不变(而不是从其移出),那也可以!只要将其传递,它就会被复制。事情将“正常工作”,而无需真正知道构造函数代码中的内容(您只需要知道它需要一个值)即可。
我要更改的另一件事是引入std::unique_ptr
,因为您当前存在内存泄漏:
class Holder {
public:
Holder(vector<int> vref) : vec(std::move(vref)) {}
vector<int> vec;
}
std::unique_ptr<Holder> MakeHolder() {
return std::make_unique<Holder>({1,2});
}
int main() {
auto h = MakeHolder();
}
(某些人会拼写MakeHolder()
的返回类型auto
,但不是我。我认为了解您要获得的东西很重要。例如,否则,您必须阅读代码来知道结果的所有权语义是什么!是原始指针吗?还有其他东西吗?)
答案 1 :(得分:3)
新创建的对象是否持有对不再存在的东西的引用?
不。 Holder
按值存储向量,因此它的向量和本地函数是不同的对象。
或者这仅仅是复制向量的一种更有效的方法,在这种情况下,将其分配在堆栈上的事实没有任何区别吗?
是的。如果
Holder(vector<int>& vref) : vec(vref) {}
曾经
Holder(vector<int> vref) : vec(vref) {}
然后,您首先必须将向量复制到vref
,然后必须将vref
复制到vec
。通过参考,您可以保存第一份副本。
另一种方法是
Holder(const vector<int>& vref) : vec(vref) {}
// or
Holder(vector<int> vref) : vec(std::move(vref)) {}
它允许您接受左值和右值。
答案 2 :(得分:0)
其成员变量是向量,而不是引用,因此它是Holder
自己的副本(每个std::vector
都私有拥有其元素。)
答案 3 :(得分:0)
新创建的对象是否持有对不再存在的东西的引用?
否,复制构造函数要求初始化成员变量vec
或者这仅仅是复制向量的一种更有效的方法,在这种情况下,将其分配在堆栈上的事实没有任何区别吗?
是的,但是常见的做法是使用const
引用来避免在按值传递参数时复制ctor:
Holder(cont vector<int>& vref) : vec(vref) {}
所以也许有人犯了一个错误并错过了const
或有其他原因不使用const引用。
注意:使用移动语义时,在某些情况下,通过(const)引用传递对象可能比按值传递效率更低。