拷贝构造函数的形参不加const时遇到的情况

cwtlps 2016-08-26 11:48:34
《程序员面试宝典》10.4中的最后一题。
源程序如下:
#include<iostream>
using namespace std;
class B{
private:
int data;
public:
B(int i):data(i){
cout<<"constructed"<<endl;
}
B(B &b){
data=b.data;
cout<<"copy"<<endl;
}
~B(){cout<<"destructed"<<endl;}
};

B play(B b){
return b;
}

int main()
{
play(5);
return 0;
}

问题描述:我自己添加了拷贝构造函数(但是形参没有加const),然后这样编译就会报错。报错如下:
destructed.cpp: In function ‘int main()’:
destructed.cpp:26:8: error: no matching function for call to ‘B::B(B)’
play(5);
^
destructed.cpp:26:8: note: candidates are:
destructed.cpp:13:2: note: B::B(B&)
B(B &b){
^
destructed.cpp:13:2: note: no known conversion for argument 1 from ‘B’ to ‘B&’
destructed.cpp:10:2: note: B::B(int)
B(int i):data(i){
^
destructed.cpp:10:2: note: no known conversion for argument 1 from ‘B’ to ‘int’
destructed.cpp:19:3: error: initializing argument 1 of ‘B play(B)’
B play(B b){
^
但是如果加上const就可以了。查找问题的时候,发现有一个概念是:转换构造函数的概念(只有一个参数,且这个参数不是本类的const引用时,就是转换构造函数),不知道是不是对这个结果有影响。
现在,我就很纠结为什么加上const,就能够编译成功,不加const就编译错误?已经查了很多内容,却都没有这相关的知识点,所以就来求助各位c++大神,各位项目大神给予一下指导。非常感谢
...全文
772 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
戒之迷糊 2018-02-09
  • 打赏
  • 举报
回复
很有帮助,刚好也碰到const问题,通过你的帖子已经解决
lm_whales 2016-08-27
  • 打赏
  • 举报
回复
引用 4 楼 cwtlps 的回复:
[quote=引用 1 楼 lm_whales 的回复:] 类型转换产生的是个临时变量(右值对象) B& 是左值引用 所以 不匹配 所以没有合适的,构造函数,初始化 形参变量Play 函数的形参对象
请问,“类型转换产生的是个临时变量”,是下边这样理解吗:在执行play(5)时, B tmp(5); B b(tmp); 还是直接 B b(5); 因为第一个,产生了临时对象tmp,然后调用拷贝构造函数,然后根据“临时对象无法绑定到非常量左值引用上”,编译会报错。 第二个的话,并不产生临时变量,而是直接产生了形参。这就不会涉及到“临时对象无法绑定到非常量左值引用上”这个问题了。请问形参算是临时变量吗???[/quote] 形参是函数调用时创建的临时变量 形参本身,在函数定义的时候,并没有定义 形参的创建时,是匿名的 在函数进入函数体的时候,才有名字 具体过程是 由实参 5 ,生成一个临时变量 B(5) 入栈,或者放入寄存器 call play 。。。 在 play 中,栈中,b 就是 刚才 入栈的那个B(5)。。。。。。。。。。。。。。。。。。。。。。 由常量 5 做类型转换得到的 临时变量 B(5), 和参数 b的关系,是从栈中地址(或者寄存器)的对应关系,形成的一一对应关系 因为是引用参数,现在需要压栈(或者存储到寄存器中)的是地址 而参数类型为 B& ,不是一个常量引用参数,所以绑定失败 。。。。。。。。。。。。。。。
cwtlps 2016-08-27
  • 打赏
  • 举报
回复
引用 1 楼 lm_whales 的回复:
类型转换产生的是个临时变量(右值对象) B& 是左值引用 所以 不匹配 所以没有合适的,构造函数,初始化 形参变量Play 函数的形参对象
请问,“类型转换产生的是个临时变量”,是下边这样理解吗:在执行play(5)时, B tmp(5); B b(tmp); 还是直接 B b(5); 因为第一个,产生了临时对象tmp,然后调用拷贝构造函数,然后根据“临时对象无法绑定到非常量左值引用上”,编译会报错。 第二个的话,并不产生临时变量,而是直接产生了形参。这就不会涉及到“临时对象无法绑定到非常量左值引用上”这个问题了。请问形参算是临时变量吗???
cwtlps 2016-08-27
  • 打赏
  • 举报
回复
谢谢@lm_whales,@ri_aje两位的回复,您们的回复让我找到了问题所在。 只是在我将主函数更改为: int main() { B b1(5); play(b1); return 0; } 以后,却可以正常编译运行了,这个时候,b1作为形参b的初始化值时调用copy构造函数,这个我能理解。只是play(b1)返回对象b1,这个时候主函数也是产生一个临时对象,并且用返回值来对这个临时对象进行初始化,这个时候也是调用copy构造函数。是可以运行的。 私下里认为:play(5),是调用B(int)构造函数产生了一个临时变量,而临时变量是const变量,因此不能用非const引用对它进行引用。因此第一个主函数出现了错误(“临时对象无法绑定到非常量左值引用上”)。 第二个主函数中b1不是临时变量,是一个非const变量,因此可以用非const引用对其进行引用。 这又变成类似于下边的问题: char c; char *p=&c; const char* q=p; //指向非const变量的指针可以赋值给指向const变量的指针。 但是 const char c; char *q=&c; //error!!!因为指向const变量的指针不能赋值给指向非const变量的指针。 再次感谢两位的回复及忠告。
ri_aje 2016-08-27
  • 打赏
  • 举报
回复
错误的原因是临时对象无法绑定到非常量左值引用上。 楼主看点好书吧,比如 c++ primer。
cwtlps 2016-08-27
  • 打赏
  • 举报
回复
补充一句: int a=1; const int b=a; //没问题!!!
cwtlps 2016-08-27
  • 打赏
  • 举报
回复
经过两位热心大神的点拨以后,我又用gdb进行了debug。学习编程不debug,怎么可以。现在把发现分享如下: 1、const引用可以初始化为 “常量、表达式、函数”; 如:const int &a=1; int i=3; const int &a=i+5; int max(int b,int c){ return b>c?b:c; } const int &a=max(4,5); 这都是可以的。但是普通引用却不可以,普通引用要求引用初始化为同类型的变量。 下边主要分析上边帖子中play(5)调用的过程(根据debug说的) 首先主函数中只有play(5); 那么这个主函数的栈帧中就没有局部变量。 程序运行到play(5)的时候,会先调用B(5)这个构造函数构造一个对象,这个对象就是形参b。(可以说它不是临时对象,就是形参,认为形参就是函数的局部变量,没有什么区别),构造完毕以后,开始进入函数体中执行return b;这个时候就有意思了,这个时候会调用拷贝构造函数B(b)构造一个临时对象(此时才真正产生临时对象),并且这个临时对象不属于主调函数main,也不属于被调函数play,而是存放在play的栈帧后边(不在它们的栈帧中)。临时变量产生以后,编译器左看右看,觉得没啥用,就立即调用析构函数把这个临时对象给析构了,接下来是把形参b析构了。(注意析构顺序)。这个说明完毕!! 如果在主调函数中将play(5)换为B a=play(5);这个时候,主调函数main的栈帧中就有一个空间是给a对象的,虽然它里边的值还不知道,可能是随意的值,但是这个内存空间是占上了。接下来是和上边一样的执行,只是,产生临时对象时,这个临时对象不再是放在play栈帧的后边了,而是放在主调函数栈帧中的a的内存空间中了,也就是说临时变量现在就是a了,和它合为一体了,属于主调函数了。接下来,执行析构函数,此时析构的不是临时对象了,析构的是play的形参b了。然后返回主调函数,然后才析构a(也就是那个临时对象)。 这个说明完毕!! 如果主调函数将play(5)换为const B&a=play(5); 这个时候,产生的临时对象不属于主调函数也不属于被调函数,也是放在play的栈帧后边,然后调用析构函数,析构对象不是临时对象,是形参b,然后返回主调函数,然后才调用这个临时对象。 也就说const引用延长了临时对象的生存周期!!! 因此,得出编程tip:能用const引用做形参的时候,最好用const引用形参,最好不替换成普通引用
lm_whales 2016-08-27
  • 打赏
  • 举报
回复
类型转换产生的是个临时变量(右值对象) B& 是左值引用 所以 不匹配 所以没有合适的,构造函数,初始化 形参变量Play 函数的形参对象

64,701

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧