请教这两种方法定义一个类的区别

karen99 2016-04-18 05:02:48
想产生一个类型为Paras 的变量,以为这两种方法是等价的,运行却发现只有第一种工作,第二种Menu里的许多初始化的东西都丢了,请教大家为什么呢?谢谢!

方法1
Menu temp(string("filenames.csv"));
Paras param(temp);

方法2
Paras param(Menu(string("filenames.csv")));


...全文
130 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
karen99 2016-04-19
  • 打赏
  • 举报
回复
CheHW老师,真高兴又见到你!本来都不好意思用这种琐碎的问题来打扰你,虽然你总是很耐心地接受我的打扰:) 你的回复让我太崇拜了!我需要进一步理解并向你请教才行,我会给你写邮件的。再次感谢,再次开心:)
引用 7 楼 chehw_1 的回复:
Paras类的构造函数在内部实现时,有可能仅保存了Menu的指针。 如果是这种情况,Paras param(Menu(string("filenames.csv")); 将是一个不确定性行为。 其中Menu(string("filenames.csv") 会在栈上分配空间并初始化一个临时的类实例。若Paras类只是保存Menu类的指针,且在这个Menu实例析构前没有额外用到这个类实例,该变量的构造函数可能会在编译时被编译器优化掉。同时,Paras类成员变量所保存的指针指向的是一个已经销毁的对象,结果将无法确定。
chehw_1 2016-04-18
  • 打赏
  • 举报
回复
Paras类的构造函数在内部实现时,有可能仅保存了Menu的指针。 如果是这种情况,Paras param(Menu(string("filenames.csv")); 将是一个不确定性行为。 其中Menu(string("filenames.csv") 会在栈上分配空间并初始化一个临时的类实例。若Paras类只是保存Menu类的指针,且在这个Menu实例析构前没有额外用到这个类实例,该变量的构造函数可能会在编译时被编译器优化掉。同时,Paras类成员变量所保存的指针指向的是一个已经销毁的对象,结果将无法确定。 比如:

/****************
 * tc1.cpp
*/

#include <iostream>
#include <stdio.h>

class Menu
{
public:
	Menu():items_count(0), data(0){
		printf("Menu()\n");
	}
	Menu(const char * file_name)
	{
		printf("Menu(const char *), p = %p\n", this);
		items_count = 10;
		data = 1;
	}
	virtual ~Menu(){
		//~ printf("destory\n");
		//~ printf("destroyed %p\n", this); // 如果在析构时用到了这个类实例的指针,那么编译器将不会把构造函数优化掉
	}
public:
	void dump()
	{
		printf("paras.data = %d\n", data);
		printf("paras.items_count = %d\n", items_count);
	}
protected:
	int items_count;
	int data;
	
};

class Paras
{
public:
	Paras(){}
	Paras(const Menu & menu)
	{
		m_pmenu = (Menu *)&menu;
	}
protected:
	Menu * m_pmenu;
public:
	void dump(){
		printf("m_menu = %p\n", m_pmenu);
		m_pmenu->dump();
	}
};

int main(int argc, char **argv)
{
	Paras param(Menu("abc"));
	param.dump();
	return 0;
}
在上面的代码中,如果在编译时不做优化,那么Menu类实例可以正常初始化: $ g++ -o tc1 tc1.cpp $ ./tc1 ====== 输出结果 ====== Menu(const char *), p = 0x7ffdded57060 m_menu = 0x7ffdded57060 paras.data =1 paras.items_count = 10 如果进行优化: $ g++ -O2 -o tc1_opt tc1.cpp $ ./tc1 ====== 输出结果 ====== Menu(const char *), p = 0x7ffeef088100 m_menu = 0x7ffeef088100 paras.data = 0 paras.items_count = 0 通过反汇编, $ objdump -d tc1 > tc1.S $ objdump -d tc1_opt > tc1_opt.S 对比这两个.S文件可以发现,在未优化的代码中会包含Menu构造函数的具体实现: 4006db: 48 89 e6 mov %rsp,%rsi 4006de: bf 44 09 40 00 mov $0x400944,%edi 4006e3: 31 c0 xor %eax,%eax 4006e5: c7 44 24 08 0a 00 00 movl $0xa,0x8(%rsp) 4006ec: 00 4006ed: c7 44 24 0c 01 00 00 movl $0x1,0xc(%rsp) 而在-O2优化下,这部分代码不存在了。
karen99 2016-04-18
  • 打赏
  • 举报
回复
非常感谢!这也是我的理解,就是不知道为什么会有不同的结果,显然我只能用第一种方案,否则代码不工作
引用 3 楼 studyCBC 的回复:
1与2的区别会产生一个拷贝构造,不应该会这样
karen99 2016-04-18
  • 打赏
  • 举报
回复
您指教地很对,我就是那样调试,在类里面看某个变量的值是对的,出来那个值就没有了,但是用第二种方法就没有这个现象,不知道怎么再更深一步跟踪了,所以才来请教大家
引用 1 楼 zhao4zhong1 的回复:
理解讨论之前请先学会如何观察! 计算机组成原理→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执行停在该断点处的时候。
karen99 2016-04-18
  • 打赏
  • 举报
回复
您指教地很对,我就是那样调试,在类里面看某个变量的值是对的,出来那个值就没有了,但是用第一种方法就没有这个现象,不知道怎么再更深一步跟踪了,所以才来请教大家
引用 1 楼 zhao4zhong1 的回复:
理解讨论之前请先学会如何观察! 计算机组成原理→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执行停在该断点处的时候。
七擒关羽 2016-04-18
  • 打赏
  • 举报
回复
1与2的区别会产生一个拷贝构造,不应该会这样
小灸舞 2016-04-18
  • 打赏
  • 举报
回复
代码功能归根结底不是别人帮自己看或讲解或注释出来的;而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。 提醒:再牛×的老师也无法代替学生自己领悟和上厕所! 单步调试和设断点调试(VS IDE中编译连接通过以后,按F10或F11键单步执行,按Shift+F11退出当前函数;在某行按F9设断点后按F5执行停在该断点处。)是程序员必须掌握的技能之一。
赵4老师 2016-04-18
  • 打赏
  • 举报
回复
理解讨论之前请先学会如何观察! 计算机组成原理→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,646

社区成员

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

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