C++ 中关于构造、析构的疑惑...

renzhewh 2010-10-31 10:20:09

以下的三个帖子涉及的话题可能有点老了(之间可能还有点联系),不过还是希望高手帮忙,不欢迎水帖
(所有的程序均运行于 VS 2008 下)

一、不使用 virtual 实现的 多态析构 (挺吸引人的)
注意:如果取消注释的部分,即添加了构造函数,生成对象的数量会减少,应该是编译器做的优化,本人想知道 什么书 会介绍这些 特性?其次是上述提到的优化可以禁止吗?
#include <iostream>
//#define CONSTRUCTOR_DEFINED

struct Base
{
#if defined CONSTRUCTOR_DEFINED
Base() {}
#endif
~Base() // note: it's not virtual
{ std::cout << "Base destroyed" << std::endl; }
};

struct Derived : Base
{
~Derived()
{ std::cout << "Derived destroyed" << std::endl; }
};

int main()
{
const Base &b = Derived();
return 0;
}

...全文
554 43 打赏 收藏 转发到动态 举报
写回复
用AI写文章
43 条回复
切换为时间正序
请发表友善的回复…
发表回复
renzhewh 2010-11-10
  • 打赏
  • 举报
回复

结帖,谢谢各位了

第一题:有兴趣的,可以参阅 C++ View 第5期 简化异常安全代码 中关于 ScopeGuard 的
实现

第二题:以 ri_aje 提够的标准论述作为 合法的 解释

第三题:原因不明,猜测编译器是 将ERROR_##msg() 视为一个指针
现改用 稻草人 函数

#define STATIC_CHECK(expr, msg)\
{\
struct ERROR_##msg makeT(); \
(void)sizeof(CompileTimeChecker<(expr)>(MakeT()));\
}




youdaping777 2010-11-03
  • 打赏
  • 举报
回复
学习,学习,讨论激烈!!
gules 2010-11-03
  • 打赏
  • 举报
回复
嗯,上述两条回答就解决这里第二个问题足矣!
pengzhixi 2010-11-03
  • 打赏
  • 举报
回复
至于已经构造的基类或者类成员,语言可以保证正常摧毁。
pengzhixi 2010-11-03
  • 打赏
  • 举报
回复
一个从未真正存在过的对象有必要去调用析构函数吗
renzhewh 2010-11-03
  • 打赏
  • 举报
回复
to ri_aje

上面引用的那句话 具体是在The C++ PL 特别版(中文版) 17.4 未处理的异常 一节中


38 39 说的我都清楚,我想知道的是为什么程序测试的结果与书上的不太一致,
ls 说过,在有未处理的异常时,即使是构造完全的对象,其析构函数也不会执行
(如此说能保证 不一定吧, 光说理论,似显得没劲,希望能以解决问题为中心)

还有单步调试的情况呢,以及第三题 至今也没人给出合理的解释

renzhewh 2010-11-03
  • 打赏
  • 举报
回复
我说我相信标准:具体的意思是,一些被重点推荐的书上说的,即认为是符合标准(比如 The C++ PL、Inside The C++ Object Model) ,老实说,还真不知道你引用的具体是
哪本书里的,(我人挺懒,一般不太喜欢翻那种标准书)

这里我想以上说的那些书中,它们应该不会是乱写的(或者是写得太早,不符合新标准,与标准出现了矛盾,我也不知道谁说的对(可能我有点先入为主,倾向于自己看到的)

“一切” 我在这两个字上加了引号,没有明说无法一般性的处理全局之类的对象

书不在我这,我现在只记得在特别版 14 章,另外,觉得这类书翻译的质量还算不错
原版与翻译差异应该不大吧
ri_aje 2010-11-03
  • 打赏
  • 举报
回复
我说的“不符合标准”,是针对你14楼中如下的陈述

5、若并不发生在try 区段内,或者没有匹配的catch 子句,那么系统必须
a、摧毁所有的 active local object
b、从堆栈中将当前的函数 unwind 掉
c、进行到程序堆栈中的下一个函数中去,然后重复 2——5。

这样陈述是不正确的,因为标准(15.5.1.1 and 15.5.1.2)明确规定,是否回退堆栈属于implementation-defined的行为,而你却说"b、从堆栈中将当前的函数 unwind 掉"。你一方面强调要相信标准,我把标准翻出来了,你又说“我觉得堆栈回退是会执行”,我也不知道说什么好了。

另外,“main 中使用 catch(...) 中接收“一切”异常的情形”,至少无法包括,全局变量,命名空间全局变量,和类静态变量构造过程中可能抛出的异常。

你关于TC++PL的引用,具体那章那节那句,说清楚了,我像我引用标准一样,这样也好查,另外,你读的是原版的还是翻译的?
renzhewh 2010-11-02
  • 打赏
  • 举报
回复
to ri_aje

仔细看了下在 19# 的回答,你说的【不符合标准】,我没有看到什么地方有问题,
也许你是认为我没提及:在有未处理异常时,会执行 terminate()。(或许我遗漏了哪点)

【首先你的 X 对象并没有构造完全,其次你这里可能不存在堆栈回退】
堆栈回退应该是会执行的,程序需要借助这一动作去寻找可以匹配的 catch 子句

以下这句
【由于 terminate() 已经执行,连它都没机会析构了。】
因为我觉得堆栈回退是会执行的,所以如果这是事实的话,那么析构函数是有机会
在 terminate 执行前完成的

22# 的回答,也觉得不太对
这时跟在 main 中使用 catch(...) 中接收“一切”异常的情形,应该是一样的
对于编译器来说,无需更改其原有的处理结构(指的是catch(...)的情形),
只是在进行堆栈回退时,执行析构函数(这也是我的个人看法,目前没有找到
异常体系的具体实现的相关说明)

这里我有一份资料可以作为依据(下午刚发现的 :-) ):
The C++ Programming language 异常处理 一章中提到: 当程序中有未处理的异常时,
堆栈回退中是否调用析构函数取决于实际系统的实现。在一些情况下,比如需要在
抛出异常后,实施唤醒机制,那么在堆栈回退中不执行析构函数就显得十分重要。
而有些特定系统,则决定了其必须在回退中执行析构函数。


至于你提到的【强制将你的所有代码加入到一个由编译器生成...】,我还是不太理解
希望你可以详尽的说明
ri_aje 2010-11-02
  • 打赏
  • 举报
回复
To 22 楼,
不可以,你仔细想想,如果可以的话,不是相当于C++语言强制将你的所有代码加入到一个由编译器生成,无所不包的 try catch 块了吗,那样的话还要显示的 try catch 干吗,而且这种想法也与 C++ 语言的设计思想背道而驰啊,我记得你在另一个帖子里刚引用了More Effective C++中关于 try catch 对代码大小和执行效率的负面影响,C++不会故意把这种负面影响肆意扩大的。
renzhewh 2010-11-01
  • 打赏
  • 举报
回复

STATIC_CHECK(1 > 2, 1_should_be_less_than_2);

gules 2010-11-01
  • 打赏
  • 举报
回复
不知道你是怎么用(写)STATIC_CHECK(false, ???);的?
renzhewh 2010-11-01
  • 打赏
  • 举报
回复
to gules

不好意思,那是我在调试这个程序时,将那句给去掉了,后来贴代码时,可能就忘了
上面才是最完整的版本

希望给出解释
gules 2010-11-01
  • 打赏
  • 举报
回复
[Quote=引用 28 楼 renzhewh 的回复:]
C/C++ code

不好意思,问题 3 的代码有问题,更正如下

template<bool>
struct CompileTimeChecker
{
CompileTimeChecker(...);
};
template<> struct CompileTimeChecker<false> {};
#define STATIC_CHECK(expr, msg)\……
[/Quote]

怎么又把class ERROR_##msg {};写宏定义里去了?
renzhewh 2010-11-01
  • 打赏
  • 举报
回复
to pengzhixi
[抛出异常的函数不可能自己做处理,只有调用该函数的应该做处理。]
我想应该是说【throw 所在的函数】

这个不一定,至少技术上是可以的,至于谁应该做处理,我还是更倾向于
"谁知道的信息更多,就由谁负责"
renzhewh 2010-11-01
  • 打赏
  • 举报
回复

不好意思,问题 3 的代码有问题,更正如下

template<bool>
struct CompileTimeChecker
{
CompileTimeChecker(...);
};
template<> struct CompileTimeChecker<false> {};
#define STATIC_CHECK(expr, msg)\
{\
class ERROR_##msg {}; \
(void)sizeof(CompileTimeChecker<(expr)>(ERROR_##msg()));\
}

出现的错误是:
error C2066: 转换到函数类型是非法的
error C2070: “CompileTimeChecker<false> (main::ERROR_test (__cdecl *)(void))”:
非法的 sizeof 操作数
gules 2010-11-01
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 renzhewh 的回复:]

最后第三题,现在还没人回答……
[/Quote]

第三题没问题啊(之前我确实没仔细看),只过在使用时要定义形如下面的数据类型:
class ERROR_testmsg {
};
并将testmsg作为msg。
pengzhixi 2010-11-01
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 renzhewh 的回复:]
to ri_aje

你说的没错,加上 exception handler 之后,可以输出。

可是这里我想要的是:遇到异常,就终止程序而不自己做异常处理,且可以将已构造的对象析构,这难道不可以吗? 再者,单步调试时,为什么会经过 X 的析构函数呢 ?

最后第三题,现在还没人回答……
[/Quote]
抛出异常的函数不可能自己做处理,只有调用该函数的应该做处理。
z405487120 2010-11-01
  • 打赏
  • 举报
回复
刷分下东西,也不知道能下不
z405487120 2010-11-01
  • 打赏
  • 举报
回复
刷分下东西,也不知道能下不
加载更多回复(23)

64,641

社区成员

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

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