C++ C++;什么时候通过值与引用传递const struct参数有意义?

C++ C++;什么时候通过值与引用传递const struct参数有意义?,c++,parameters,reference,constants,C++,Parameters,Reference,Constants,我见过类似的问题,但我想澄清一下 假设一个基本C++类: class MyClass { public: struct SomeData { std::wstring name; std::vector<int> someValues; }; void DoSomething(const SomeData data); } 这对我来说似乎更有效率。如果我们省略了和,那么数据不是按值传递给DoSomething?我不知

我见过类似的问题,但我想澄清一下

假设一个基本C++类:

class MyClass
{
public:
    struct SomeData
    {
        std::wstring name;
        std::vector<int> someValues;
    };

    void DoSomething(const SomeData data);
}

这对我来说似乎更有效率。如果我们省略了
,那么
数据
不是按值传递给
DoSomething
?我不知道为什么在可以通过引用传递并避免复制的情况下,最好通过值传递
常量
参数?

传递
常量
值对调用方来说主要是信息性的,它显示了意图。这对于使代码易于阅读、理解和维护非常重要

如果编译器知道函数不修改其参数,那么它也可能添加一些额外的优化。例如,它可能会导致编译器根本不执行复制。

这是:

void DoSomething(const SomeData data);
这有点不寻常,因为虽然它不会为调用方(可以传递常量或非常量值)更改任何内容,但它限制了函数可以在内部执行的操作。这样做没有多大价值,也不常见

如果值的复制成本很高,包括如果它大于目标平台上大约两个指针,则通过引用传递(const或not)会更有效。换句话说,如果
SomeData
是一个包含两个整数的结构,那么按值传递可能会更有效。但是如果它包含一个
std::map
或一些更大的数据,最好通过引用传递它


例外情况是,如果函数无论如何都要复制值,那么最好按值获取,因为如果调用方允许,值可能会被“移动”而不是复制。

按值/引用传递和常量正确性是两个不同的概念。但是一起使用

传递值

 void DoSomething (SomeData data);
void DoSomething (SomeData& data);
当复制成本较低且不希望保留对外部对象的引用时,使用传递值。在某些情况下,该函数可以(如果它在类中)保留指向它的指针,并拥有自己的副本

通过参考传递

 void DoSomething (SomeData data);
void DoSomething (SomeData& data);
如果知道复制结构时可能会导致性能损失,请始终使用“按引用传递”。在某些情况下,此函数可以(如果它在类中)保留指向此对象的指针,并指向外部对象。保持指向外部对象的指针意味着您应该知道它的生命周期以及该外部对象何时超出范围。更重要的是,对外来对象的更改会显示在指针上

常数正确性

void DoSomething (const SomeData data);  // const for local copy
void DoSomething (const SomeData& data); //  const for callers object
添加
const
以按值或引用传递意味着此函数不会更改它。但没有或没有
&
决定了您试图添加修改安全性的对象<代码> const 是一个非常有用的C++工具,用于记录API,提供编译时安全性,允许更多编译器优化。
这篇文章。

void DoSomething(const SomeData)的最大问题是它将接口和实现混为一谈。从调用方的角度来看,
const
不会改变任何东西;该函数仍将接收副本,并且不会修改原始对象。实现本身的函数内部拷贝所做或不做的事情不应该打扰调用方,因此不应该在接口中表达

如果拷贝没有更改,
const
确实会使实现更加正确,但是将实现细节泄漏到接口中是一个很高的代价。我建议不要使用
void DoSomething(const SomeData数据)


与往常一样,在这里不应高估绩效收益或损失。这更多的是关于语义和约定。

当然,还有一个问题,就是如何确定创建副本以按值传递是否比按引用传递成本更高或更低。对于简单的情况,根据对象及其成员和引用在内存中的大小使用经验法则可能就足够了,但这些大小是由实现定义的(即,编译器之间可能会有所不同,尽管有一定的限制),因此经验法则并不总是很好地工作。如果对象的构造函数或析构函数表现不同,并增加或减少复制成本,那么经验法则也会失效。