局部函数里面创建对象返回一个对象是一个副本?!

baidu_28726667 2018-06-19 05:38:07
#include <iostream>
using namespace std;

class Person{
public:
Person(){
cout << "no param contructor!" << endl;
mAge = 10;
}
Person(int age){
cout << "param constructor!" << endl;
mAge = age;
}
Person(const Person& person){
cout << "copy constructor!" << endl;
mAge = person.mAge;
}
~Person(){
cout << "destructor!" << endl;
}
public:
int mAge;
int mId;
};

Person MyBusiness(){
Person q(10);
cout << "MyBusiness_q:" << (int*)&q << endl;
q.mId = 200;
return q;
}

int func()
{
int a = 10;
cout << "func_a:" << &a << endl;
return a;

}

int main(){
Person p = MyBusiness();
cout << "main_p:" << (int*)&p << endl;
cout << "mId:" << p.mId << endl;
int b= func();
cout << "main_b::" << &b << endl;

return 0;

}





问题1:这代码里面 Person MyBusiness()结束的时候,栈空间里面的东西应该没有了。 return q在消失前是copy一份吗?

问题2: Person p = MyBusiness(); 并不是Person p = q(10) 执行了一次构造函数?! 而是直接把 q里面的所有东西一一对应赋值给p ?! 所以p.Mid能打印出200 ?!

问题3: 问题2里面的赋值 是不触发构造函数吗? 为什么问题1里面Person MyBusiness()结束并没有执行析构函数而在main结束后执行了析构函数?!

补一个问题4: 为什么 两个对象p和q的地址是一样的? 我另外写了一个函数 func(), a和b这两个变量地址是不一样的




...全文
952 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
LandyTan 2018-06-20
  • 打赏
  • 举报
回复
引用 4 楼 qq_16961853 的回复:
在Ubuntu下,return q返回的其实是一个地址,这个q在超出其作用域时并没有被回收,而是返回给了调用者。return a返回的是一个值,因为a在超出其作用域时被回收了。 int 是基本数据类型;Person是用户自定义数据类型。
Person p = MyBusiness();执行这行代码的时候,p的地址是0x7fff90ebb120(注意,这时候并没有进入到MyBusiness函数)。 MyBusiness函数Person q(10);这行代码,并没有申请内存。q的地址是0x7fff90ebb120 在MyBusiness函数增加一行Person p(20),你会发现没有返回的p被回收了,且内存地址并不是main函数中p的地址。只有返回的那个q才与main函数中的p地址相同
LandyTan 2018-06-20
  • 打赏
  • 举报
回复

在Ubuntu下,return q返回的其实是一个地址,这个q在超出其作用域时并没有被回收,而是返回给了调用者。return a返回的是一个值,因为a在超出其作用域时被回收了。
int 是基本数据类型;Person是用户自定义数据类型。
棉猴 2018-06-20
  • 打赏
  • 举报
回复
VS2015+Windwos10的输出结果为

问题1:这代码里面 Person MyBusiness()结束的时候,栈空间里面的东西应该没有了。 return q在消失前是copy一份吗?
当一个函数的返回值是类的非引用类型时,在返回该值的时候,会调用这个类的拷贝构造函数将初始化非引用类型参数。对于如下代码
Person p = MyBusiness();

实际上调用了Person类的拷贝构造函数,创建了Person类的对象p,而拷贝构造函数的参数即为MyBusiness()函数中的q。你说的“ return q在消失前是copy一份吗”,实际上应该是在return q以后使用拷贝构造函数拷贝了以分,即变量p。

问题2: Person p = MyBusiness(); 并不是Person p = q(10) 执行了一次构造函数?! 而是直接把 q里面的所有东西一一对应赋值给p ?! 所以p.Mid能打印出200 ?!
 Person p = MyBusiness();

调用的是拷贝构造函数,但是在
	Person(const Person& person) {
cout << "copy constructor!" << endl;
mAge = person.mAge;
}

中只是对mAge进行了赋值,没有对mId进行赋值,所以
cout << "mId:" << p.mId << endl;

打印出来的mId的值是一个随机值。

问题3: 问题2里面的赋值 是不触发构造函数吗? 为什么问题1里面Person MyBusiness()结束并没有执行析构函数而在main结束后执行了析构函数?!
问题2中的赋值确实出发了构造函数,但是该构造函数不是在 MyBusiness()中,所以,不会在MyBusiness()中调用析构函数,而是在main结束后调用析构函数。
补一个问题4: 为什么 两个对象p和q的地址是一样的?
p和q的地址不一样,q是局部变量,在MyBusiness()结束后,该变量已经被释放!
sghcpt 2018-06-20
  • 打赏
  • 举报
回复
楼主,我在windows下vs2013,运行你上面的代码,输出不一样的结果:
  • 打赏
  • 举报
回复
weixin_42497759 2018-06-20
  • 打赏
  • 举报
回复
在 windows VC里,还和debug, release。
weixin_42497759 2018-06-20
  • 打赏
  • 举报
回复
在 windows VC里,还和debug, release
weixin_42497759 2018-06-20
  • 打赏
  • 举报
回复
在 windows VC里,还和debug, release
  • 打赏
  • 举报
回复
引用 10 楼 kristy_yy 的回复:

  • 打赏
  • 举报
回复
  • 打赏
  • 举报
回复
在 windows VC里,还和debug, release有很大关系
paschen 版主 2018-06-20
  • 打赏
  • 举报
回复
1、是的,按值返回就是通过复制 2、是的,按MyBusiness()返回的对象构造p,返回的是什么以,构造出来就是什么
liulilittle 2018-06-20
  • 打赏
  • 举报
回复
Person p = MyBusiness() 与 Person p() 是相同的它们都是在栈上分配。
例如:
Person p = MyBusiness();
return p;
返回到调用方(caller)与struct相同是经历过一次深入拷贝的;
下面提供一个例子用于说明。

--------------------test-codec
class Foo {
public:
int N = 0;
~Foo() {

}
Foo() {

}
};

Foo GetFoo() {
Foo foo = Foo();
foo.N = 10;
return foo;
}

int main()
{
Foo foo = GetFoo();
int n = foo.N;
return 0;
}
赵4老师 2018-06-19
  • 打赏
  • 举报
回复
理解讨论之前请先学会如何观察! 计算机组成原理→DOS命令→汇编语言→C语言(不包括C++)、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言(包括C++)、架构…… 对学习编程者的忠告: 多用小脑和手,少用大脑、眼睛和嘴,会更快地学会编程! 眼过千遍不如手过一遍! 书看千行不如手敲一行! 手敲千行不如单步一行! 单步源代码千行不如单步Debug版对应汇编一行! 单步Debug版对应汇编千行不如单步Release版对应汇编一行! 不会单步Release版对应汇编?在你想单步Release版C/C++代码片断的前面临时加一句DebugBreak();重建所有,然后在IDE中运行。(一般人我不告诉他!单步类的实例“构造”或“复制”或“作为函数参数”或“作为函数返回值返回”或“参加各种运算”或“退出作用域”的语句对应的汇编代码几步后,就会来到该类的“构造函数”或“复制构造函数”或“运算符重载”或“析构函数”对应的C/C++源代码处。 VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。

64,643

社区成员

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

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