将对象通过引用传递给重载运算符 - C ++

8

我对C++还比较陌生。我看到人们通常会在运算符重载中通过引用传递对象。但是,我无法弄清楚什么时候真正需要这样做。就像下面的代码一样,如果我在operator+中声明对象c1和c2时删除了&符号,我仍然会得到相同的结果。当我们不想修改c1或c2时,在这种情况下是否有必要通过引用传递?

#include <iostream>

class Keys
{
private:
    int m_nKeys;

public:
    Keys(int nKeys) { m_nKeys = nKeys; }

    friend Keys operator+(const Keys &c1, const Keys &c2);

    int GetKeys() { return m_nKeys; }
};


Keys operator+(const Keys &c1, const Keys &c2)
{
    return Keys(c1.m_nKeys + c2.m_nKeys);
}

int main()
{
    Keys cKeys1(6);
    Keys cKeys2(8);
    Keys cKeysSum = cKeys1 + cKeys2;
    std::cout << "There are " << cKeysSum.GetKeys() << " Keys." << std::endl;
    system("PAUSE");
    return 0;
}

如果你不通过引用(或指针)作为参数传递对象,而是通过值传递它们,这意味着它们会被复制;某些对象的复制可能很耗费资源。 - piwi
如果你使用引用,就不需要将对象作为参数复制,因此如果将参数作为引用传递,可能会更有效率。 - EdChum
4个回答

10

操作符就像普通函数一样,只是有着“花哨”的名称 :)
(例如,使用operator+代替sum()

因此,您用于函数的相同参数传递规则也可以应用于重载的操作符。

特别地,当您有一个不便宜的参数需要复制(例如intfloat是廉价的参数;std::vector, std::string是代表不廉价的参数),并且在方法内部观察该参数时(即它是一个输入只读参数),那么您可以通过const引用传递(const &)

这样,基本上就像原始参数的地址被传递到函数中一样,因此没有深度复制涉及。深度复制可能非常昂贵,例如考虑具有大量元素的向量。

因此,总结一下,当:

  1. 参数只是不便宜进行复制(例如对于整数、浮点数等,不用担心:传值就可以了)
  2. 在函数/操作符实现中观察该参数(即它是输入只读参数)时

您使用const引用传递


@ Mr.C64 现在在 `ostream& operator<< (ostream &out, Something) { out << Something << endl; return out; }` 中,ostream 后面的 & 有什么作用? - QuestionMark
@Farzin:&符号被使用是因为out是一个可修改的参数,它不是只读输入参数。基本上,out是一个引用,指向你要追加自己文本的流。这个操作会修改流对象,所以你不能通过const引用传递它,必须去掉const - Mr.C64
@ Mr.C64:我指的是 ostream& operator 中的 & - QuestionMark
@Farzin:你是说在返回值中吗?那是因为你正在返回作为输入的相同ostream。如果你按值返回(即没有&ostream),那么它将是一个副本,而不是原始输入的一个副本。 - Mr.C64
1
谢谢您!您使用“便宜”的术语使得理解为什么应该通过引用传递变得更加容易。 - Aleksandr Hovhannisyan

3

如果你通过引用传递参数,那么不会复制对象,对于更复杂的类,这可以大大提高性能。

在这种情况下,性能成本可能很小,可以想象编译器可以将其全部优化掉,但仍然值得这样做。后来,Keys类可能会变得更加复杂。


1

传递引用的优点:

  1. 它允许我们使函数改变参数的值,这有时是很有用的。
  2. 因为不会复制参数,所以即使在使用大型结构体或类时也很快。
  3. 我们可以通过const引用传递来避免意外更改。
  4. 我们可以从函数返回多个值。

传递引用的缺点:

  1. 由于无法将非const引用指向字面量或表达式,因此引用参数必须是普通变量。
  2. 很难确定传递的参数是输入、输出还是两者兼而有之。
  3. 从函数调用无法判断参数是否会发生变化。传值和传引用的参数看起来相同,只有通过查看函数声明才能确定参数是按值传递还是按引用传递。这可能导致程序员没有意识到函数将更改参数的值。
  4. 由于引用通常是使用指针在C++中实现的,而解引用指针比直接访问指针要慢,因此访问通过引用传递的值比访问通过值传递的值要慢。

您可以阅读以下内容:

http://www.cs.fsu.edu/~myers/c++/notes/references.html


0
考虑一个包含1000万个条目的long向量。如果您原型化一个函数,如下所示:
void foo(vector<long> vl)
{
}

这将导致 vector<long> 的赋值运算符(或复制构造函数)需要复制所有这10m个元素。稍后,此临时对象 (vl) 的析构函数将释放内存并执行其他清理操作。这肯定会影响性能。

有些类专门用于同步提供程序(如关键部分等)和一些智能指针类,它们可以防止复制构造函数和/或赋值运算符的使用,以避免错误的赋值或对象创建。虽然移动构造函数或移动赋值运算符可能已经实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接