通过引用将栈分配的值传递给C ++中的构造函数

时间:2018-07-31 16:52:47

标签: c++ reference initialization

我正在使用一个外部库,并且有一些代码让我停下来。基本上,有一个向量分配在循环内部(在堆栈上)。然后,该矢量通过引用传递给某个对象的构造函数,并用于初始化该对象的矢量字段之一,该字段未声明为引用。新创建的对象是否持有对不再存在的内容的引用?还是这只是复制向量的一种更有效的方式,在这种情况下,将其分配在堆栈上的事实没有任何区别?

这是一个最小的例子:

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();
}

4 个答案:

答案 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)引用传递对象可能比按值传递效率更低。