为什么我的运算符 + 重载调用我的复制构造函数,尽管它是通过引用传递的?

Why does my operator + overload call my copy constructor despite being passed by reference?

提问人:tameless 提问时间:6/21/2020 最后编辑:bhristovtameless 更新时间:6/21/2020 访问量:742

问:

我已经做了一些研究,但我无法找到对我的代码行为的解释(我可能没有问正确的问题)。我正在使用 C++,使用 VS2019。

该代码涉及在“数据结构和其他对象”教科书中找到的类。Points

我现在存在的类:

class point {
private:
    double x;
    double y;
public:
    //constructors
    point(double ix = 0.0, double iy = 0.0) {
        x = ix;
        y = iy;
    }
    point(const point &z) {
        x = z.getX();
        y = z.getY();
        cout << "copy constructor triggered" << endl;
        cout << x << "," << y << endl;
    }

    //assignment
    void operator = (const point& result) {
        x = result.getX();
        y = result.getY();
        cout << "assignment overload triggered" << endl;
        cout << x << "," << y << endl;
    }

    //getters
    double getX() const {
        return x;
    }
    double getY() const{
        return y;
    }

    //setters
    void setX(double newX) {
        x = newX;
    }
    void setY(double newY) {
        y = newY;
    } //other function members are omitted
}//end point() class

point operator + (const point& A, const point& B) { //Adds x1 to x2, y1 to y2
    point n;

    n.setX(A.getX()+ B.getX());
    n.setY(A.getY()+ B.getY());
    return n;
}

当我发现我无法在同一行上创建类的实例时,问题首先出现了,因为我尝试使用 operator+ 重载添加两个实例。

point p1(5, 3);
point p2(12, 9);
point p3 = p1 + p2; //gives the following error: class "point" has no suitable copy constructor

我发现我忘记了在我的复制构造函数中包含“const”而犯了一个错误。通过使用复制构造函数解决该问题,我能够使代码运行良好以提交我的任务,但我对我的理解并不满意。

在解决该问题的过程中,我还在类定义中添加了赋值运算符重载 (=)。通过一些控制台调试,我发现了一个奇怪的模式:

point p1(5, 3);
point p2(12, 9);
point p3;
p3 = p1 + p2; //calls both copy constructor and assignment instructor
point p4 = p1 + p2; // calls only copy constructor

point p5;
p5 = p1; //calls only assignment operator
point p6 = p2; // calls only copy constructor

所以现在,我想知道为什么 p3 = p1 + p2 同时调用复制构造函数和赋值重载。编译器在添加点时是否调用复制构造函数?我不认为会这样,因为重载使用默认构造函数创建一个临时点,然后使用 getter 和 setter 手动更改值,然后返回该临时点。我的理解是,只有在创建新对象时才应该调用复制构造函数,但事实并非如此。operator+p3

我还应该说,我们还没有讨论过将指针与类/对象一起使用。

这里的任何帮助都是值得赞赏的。 提前感谢您的任何帮助。

C++ 运算符重载 传递引用

评论

1赞 PaulMcKenzie 6/21/2020
赋值运算符应返回对当前对象的引用,而不是 。void
0赞 Ted Lyngmo 6/21/2020
除了@PaulMcKenzie提到的(应该是)之外 - 从成员函数开始,自由函数将变得轻而易举。void operator=(const point& result)point& operator=(const point& result)operator+=operator+
0赞 Paul Sanders 6/21/2020
无法重现,看起来 MSVC 没有按应有的方式进行复制省略。
0赞 PaulMcKenzie 6/21/2020
@PaulSanders 也许运行“调试”版本而不是发布版本?
0赞 Paul Sanders 6/21/2020
@PaulMcKenzie也许吧。海湾合作委员会似乎并不在乎。

答:

0赞 bhristov 6/21/2020 #1

创建临时点并更改其值不会调用复制构造函数。

您在此处返回一个对象,而不是指针或对对象的引用:

point operator + (const point& A, const point& B) { //Adds x1 to x2, y1 to y2

当您从函数返回时,将从函数内部的对象创建一个新对象,这就是调用复制构造函数的地方。

评论

0赞 Jeffrey 6/21/2020
NRVO 可以阻止此副本吗?
0赞 Ted Lyngmo 6/21/2020
@Jeffrey 自 C++17 以来,如果制作正确,这是一种保证。 将被复制 - 返回的值不会)A
0赞 tameless 6/21/2020
谢谢你的回答。因此,我一直在寻找一种方法来通过引用传回运算符重载,我发现当创建一个新值时,运算符重载(如 +)会按值传递:stackoverflow.com/questions/2337213/...是否无法通过引用返回 + 运算符?我假设一个非常大的对象的不必要的副本在计算上会很费力。将一个对象添加到另一个对象作为成员函数以避免该复制构造函数是否更好?
0赞 Ted Lyngmo 6/21/2020
@boddha 你不想改变,对吧?我先说。A = B + CBCoperator+=
0赞 PaulMcKenzie 6/21/2020
@boddha 所以我一直在寻找一种方法,通过引用传回运算符重载——不要试图用技巧来智取编译器。很多时候,这个技巧会适得其反,并且会产生较慢的代码。此外,C++基于值语义,而不是引用语义。它不像 Java 或其他语言那样工作,这并不意味着.对 、 、 和 的工作方式相同(或应该工作)。===intdoublefloatstd::stringYourClass