我不明白在这个例子中什么时候调用构造函数

I don't understand when the constructor is called in this example

本文关键字:什么时候 调用 构造函数 明白      更新时间:2023-10-16

嗨,我一直在尝试分析以下使用操作重载的代码。

#include <iostream>
using namespace std;
#define DBG(str) cout << str << endl
class Integer {
int n;
public:
Integer(int _n) : n(_n) { DBG("A"); };
Integer(const Integer& i) : n(i.n) { DBG("B"); };
Integer& operator=(const Integer& i) { DBG("C"); n = i.n; return *this; };
Integer& operator+=(const Integer& i) { DBG("D"); n += i.n; return *this; };
friend Integer operator+(const Integer& a, const Integer& b);
friend Integer operator*(const Integer& a, const Integer& b);
friend ostream& operator<<(ostream& os, const Integer& i);
};
Integer operator+(const Integer& a, const Integer& b) {
DBG("E"); return Integer(a.n + b.n);
}
Integer operator*(const Integer& a, const Integer& b) {
DBG("F"); return Integer(a.n * b.n);
}
ostream& operator<<(ostream& os, const Integer& i) {
DBG("G"); os << i.n; return os;
}
int main() {
Integer n1(1), n2(2);
Integer n = 5 + n1 + 2 * n2;
cout << n << endl;
}

结果是...

A // constructor called when n1 is created
A // constructor called when n2 is created
A // when is this called?
A // when is this called?
F // called when 2 * n2 is operated
A // called when Integer(a.n * b.n) is created in the multiplication function
E // called when 5 + n1 is operated
A // called when Integer(a.n + b.n) is created in the addition function
E // called when (5 + n1) + (2 * n2) is operated
A // called when Integer is created in the addition function
G // called when n is printed using cout
5 // value of n

现在我最麻烦的是 A 的第三次和第四次打印。在句子中Integer n = 5 + n1 + 2 * n2;正在创建对象 n 并将正确的值分配给 n,因此应该调用复制构造函数?我认为应该发生的是应该调用构造函数来制作(5 + n1 + 2 * n2)的临时对象,然后通过将其复制到 n,应该调用复制构造函数。我理解错了什么?

你能解释一下发生了什么吗?提前谢谢你。

问题出在这一行:

Integer n = 5 + n1 + 2 * n2;

没有intIntegeroperator+,但编译器默默地做了从intInteger的隐式转换,因为默认情况下所有单参数构造函数都可以用作转换方法。 这是您的实时代码(有所改进)。

这是C++的危险特性之一,因此在构造函数之前可以使用explicit关键字。如果添加它:

explicit Integer(const Integer& i) : n(i.n) { DBG("B"); };

编译器将报告错误,因为现在无法执行隐式转换,并且没有operator+(int, const Integer&)operator*(int, const Integer&)

您的operator+operator*函数接收const Integer&作为参数。

当计算此行时(5 + n1 + 2 * n2)5和2将自动转换为Integer

如果不希望转换它们,则应考虑为int创建运算符作为参数。

编辑:您也可以使用显式构造函数。

例如:explicit Integer(int _n){...}

感谢您的评论@formerlyknownas_463035818

仅当运算符函数的第一个参数不是为其定义运算符函数的目标类的对象时,才在运算符重载中使用 Friend 函数。 在您的情况下,Integer

Integer operator+(const Integer& a, const Integer& b) {
DBG("E"); return Integer(a.n + b.n);
}
Integer operator*(const Integer& a, const Integer& b) {
DBG("F"); return Integer(a.n * b.n);
}

,第一个参数确实是Integer类型的对象,它是您的目标类。您可能希望能够将Integer对象与基元整数类型一起使用为操作数。 将您的好友函数签名更改为:

Integer operator+(const int& a, const Integer& b) {
DBG("E"); return Integer(a + b.n);
}
Integer operator*(const int& a, const Integer& b) {
DBG("F"); return Integer(a * b.n);
}

请注意,使用a而不是a.n,因为当运算符函数定义为友元时,两个操作数都作为参数传递。(与第一个操作数是调用对象的非友元运算符函数不同)。因此,a包含类型为int的值,而不是类型Integer的值。

对包含DBG("A")的构造函数的匿名调用是 5 和 2 从int转换为Integer时发生的隐式转换的结果。

尽管您的代码可能有效,但这是不可靠的。 希望这有帮助。