关于C++拷贝构造函数的疑惑

浏览:31日期:2023-03-24

问题描述

关于c++的拷贝构造函数.书中写到:

在C++中,下面三种对象需要调用拷贝构造函数!

对象以值传递的方式传入函数参数

对象以值传递的方式从函数返回

对象需要通过另外一个对象进行初始化;

针对上面的第二条,按照书中例子写出如下代码:

#include <iostream>using namespace std;class Point{public: Point(int xx = 0, int yy = 0) :x(xx),y(yy){} Point(Point &p){x = p.x;y = p.y;cout << 'copy function is Called' << endl; } int GetX(){ return x; } int GetY(){ return y; }private: int x, y;};Point fun(){ cout << 'fun() Calledn'; Point t(2, 2); return t;}int main(){ Point t1(1,1); Point t2 = fun(); cout << t2.GetX()<< endl;;}

以上代码能在visual studio 2013 环境 下顺利编译通过输出为fun() Calledcopy function is Calledx is 2

当在g++环境下编译时,却出现了错误

C:UsersHaleDesktoptest1>g++ -std=c++11 test22.cpp -o test22test22.cpp: In function ’int main()’:test22.cpp:30:17: error: no matching function for call to ’Point::Point(Point)’ Point t2 = fun(); ^test22.cpp:30:17: note: candidates are:test22.cpp:8:2: note: Point::Point(Point&) Point(Point &p){ ^test22.cpp:8:2: note: no known conversion for argument 1 from ’Point’ to ’Point&’test22.cpp:7:2: note: Point::Point(int, int) Point(int xx = 0, int yy = 0) :x(xx),y(yy){} ^test22.cpp:7:2: note: no known conversion for argument 1 from ’Point’ to ’int’C:UsersHaleDesktoptest1>g++ -std=c++11 test22.cpp -o test22test22.cpp:8:15: error: invalid constructor; you probably meant ’Point (const Point&)’ Point(Point p){ ^

没有匹配的方法?于是添加构造函数

Point(const Point &p){x = p.x;y = p.y;cout << 'const copy function is Called' << endl;}

再次编译,成功!结果如下

fun() Called2

但是刚刚新增的构造方法的方法并没有被调用,但是结果是正确的。说好的作为函数返回对象会调用构造函数呢?g++为什么会有这种处理?书上未能找到相关答案,网上搜寻也没能获得满意答案!

问题解答

回答1:问题一

当在g++环境下编译时,却出现了错误:没有匹配的方法?

报错原因是你的拷贝构造函数参数类型写的不对,少了const修饰符。在C++中,临时变量是不能绑定到非常量引用的。而楼主程序中函数Point fun()的返回值是临时变量,拷贝构造函数Point(Point &p)的参数是非常量引用,所以编译器会报错,提示没有匹配的函数。当把拷贝构造函数的参数改成常量引用后,临时变量就可以正常绑定,所以可以编译通过了。例如:

// 给出如下函数声明int f();void g1(int &i);void g2(const int &i);int n = 1;// 则调用结果为g1(0); // compile errorg1(n); // okg1(f()); // compile errorg2(0); // okg2(n); // okg2(f()); // ok问题二

刚刚新增的构造方法的方法并没有被调用,但是结果是正确的。说好的作为函数返回对象会调用构造函数呢?

这是因为C++标准中明确规定了编译器可以在一定情况下对函数返回值的拷贝/移动操作进行优化。

ISO C++11标准§8.5/16中作了如下规定(省略了上下文):

... In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

在§12.8/31中有:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. ...

所以,这只是编译器优化而已。楼主可以试试用Visual Studio的Release模式进行编译,应该会跟g++一样跳过拷贝构造函数的。

回答2:

查阅相关资料发现,在c++标准中有【返回值优化】。gcc/g++之所以没有调用拷贝构造函数,是因为gcc/g++将相关的操作做了优化,将没有必要的拷贝操作给省略掉了。具体解释可以参考知乎某答住的解释:

GCC是这么干的。凡是需要返回一个结构体的函数,调用这个函数的地方,会额外传给这个函数一个地址。之后该函数就将返回值直接构造到这个地址那里。这样的好处在于,返回值直接就构造在了合适的地方,不需要进行额外的拷贝构造。算是一个非常巧妙的实现方式,避免了不必要的拷贝构造。给GCC点个赞!!作者:李其迈 链接:https://www.zhihu.com/questio...来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

将visual studio的Debug模式改为Release发现也将这些没必要的操作给优化了结果fun() Calledx is 2

所以问题得以解决。

回答3:

我记得没错的话是因为C++11还是C++14那里可以不要const,之前是要求的,但是其实这个限制没有意义

回答4:

因为你事实上没有调用拷贝构造函数,而是直接给t2赋值了,你应该这样使用拷贝构造函数。

Point t2(fun);

相关文章: