为啥向量被视为按值传递,即使它是通过 const 引用传递的?

Posted

技术标签:

【中文标题】为啥向量被视为按值传递,即使它是通过 const 引用传递的?【英文标题】:Why is a vector treated as if it was passed by value even when it is passed by const reference?为什么向量被视为按值传递,即使它是通过 const 引用传递的? 【发布时间】:2015-10-12 10:56:34 【问题描述】:

我最近编写了一个非常简单的class,它负责在std::vector 中查找最小值和最大值,结果发现,即使我将集合作为const reference 传递给class ' 构造函数,然后从外部更改向量(即,将一个元素推入其中或从中删除一个元素),class 内的向量保持不变。

#include <vector>
#include <algorithm>

class MinMaxFinder

public:
    MinMaxFinder(const std::vector<int>& numbers) : numbers(numbers)
    
    
    const int Min() const
    
        this->findMinAndMax();
        return this->min;
    
    const int Max() const
    
        this->findMinAndMax();
        return this->max;
    

protected:
    std::vector<int> numbers;
    int min;
    int max;

    void findMinAndMax()
    
        // std::minmax_element call to find min and max values
    
;

我假设传递引用的目的是不复制大对象,但是我的代码现在的工作方式似乎确实复制了集合。

#include <vector>
#include <iostream>
#include "MinMaxFinder.h"

int main(int argc, char* argv[])

    std::vector<int> v 1, -6, 12, 158, -326 ;
    MinMaxFinder mmf(v);

    v.push_back(-9999999);
    v.push_back(9999999);

    auto min = mmf.Min();
    auto max = mmf.Max();

min 的值将是-326max 的值将是158,尽管很明显9999999 的值远大于158

我设法通过将私有成员 numbers 的定义更改为 std::vector&lt;int&gt;&amp; numbers 来解决此问题,但这是正确的解决方案吗?

但是,为什么内部collection的状态会一直保持不变,这不违背const ref传递的定义吗?

【问题讨论】:

您的类中的 vector 不是 引用,因此它接收 vectorcopy /i> 通过引用传递给构造函数。 @Galik:请不要在 cmets 部分写答案。下面有三个很好的答案,它们所属的地方。 @LightnessRacesinOrbit 对于答案来说太琐碎了,很可能是错字。 @Galik:无关紧要。 答案不在 cmets 部分中。如果您认为这个问题太琐碎而无法回答,不要回答!!!!!! 您是在故意违反 Stack Exchange 模型!此外,我不认为这是一个错字。 @LightnessRacesinOrbit 那么我们应该如何发现一个问题是否应该在没有一些 cmets 的情况下基于它是“一个简单的印刷错误”而关闭?每次我们怀疑有错别字时,我们都必须提供答案吗? 【参考方案1】:

std::vector&lt;int&gt; numbers; 应该是 const std::vector&lt;int&gt;&amp; numbers; 这样您就可以引用一个对象,而无需复制或更改它。

但是说真的,这门课有什么意义呢?巨大的矫枉过正!只需使用std::min_elementstd::max_elementstd::minmax_element..

auto minimun = std::min_element(numbers.begin(),numbers.end());

【讨论】:

我猜关键是要避免对向量进行两次迭代。在这种情况下,请使用std::minmax_element。问题的评论中已经提到了。 因此,如果我将A 类型的引用传递给对象的构造函数并将引用分配给实际上是A 类型的值的成员,它会被复制吗?将numbers的类型从值更改为引用有帮助,正如我在问题中提到的那样......我不太用C++编程,直接调用s标准算法是否更常见,例如std::minmax_element ,而不是创建一个类来包装它(std::minmax_element 是我用来查找最小值和最大值的算法)? 你总是复制一些东西。在这种情况下 - 内存地址,而不是整个向量,这没关系。编译器将通过阻止编译来阻止您更改引用所指向的内容【参考方案2】:

我假设传递引用的目的是为了不复制大对象

是的,没错,“调用”构造函数时不会复制大对象。

但是,您的数据成员是一个值,而不是引用,因此会立即复制参数以初始化该成员。

就像这样写:

std::vector<int> v;
const std::vector<int>& ref = v;
std::vector<int> v2 = ref;

然后想知道为什么v2 是副本。 :)

【讨论】:

这说明了问题。我主要不是用 C++ 编程,所以有些术语很混乱。【参考方案3】:

在构造时调用的成员初始化numbers(numbers)进行深层复制。

如果您要计算构造的最小值和最大值,则不需要将 numbers 保留为类成员。

可以reference 存储为类成员,但是您必须小心将类实例的生命周期限制为向量所在的生命周期在范围内,否则引用可能悬垂。我建议不要这样做。

【讨论】:

我不想在构造函数中计算最小值和最大值,而是在 Min()Max() 方法中计算,因为我希望在向量从外部范围更改时重新计算值,但是看到存储引用可能带来的问题多于好处,在我的代码中使用纯 std::minmax_element 可能是最好的方法。【参考方案4】:

成员变量numbers 未声明为引用,因此在初始化时将被复制,即使ctor 的参数是通过引用传递的。尝试将声明更改为:

protected:
    const std::vector<int>& numbers;

【讨论】:

这有点危险,因为您可以轻松地将临时参数作为 const-ref 参数传递。最后得到一个悬空引用。

以上是关于为啥向量被视为按值传递,即使它是通过 const 引用传递的?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中通过引用与按值将向量传递给函数

为啥要在 C++ 中按值传递对象 [重复]

按值返回和通过 const 引用传递时避免临时构造

复制赋值运算符应该通过 const 引用还是按值传递?

按值传递比通过 const 引用传递更快的经验法则?

在 Delphi 中,为啥传递接口变量有时需要它是 const 参数?