免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 3013 | 回复: 14
打印 上一主题 下一主题

[C++] 为什么VC为单个delete也生成scalar deleting destructor? [复制链接]

论坛徽章:
2
2015年迎新春徽章
日期:2015-03-04 10:16:532015元宵节徽章
日期:2015-03-06 15:53:22
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-09-28 11:11 |只看该作者 |倒序浏览
本帖最后由 cdsfiui 于 2016-09-28 11:55 编辑

对于对象数组而言,C++编译器会生成隐藏的代理函数,来用一个for循环初始化new出来的数组,用for循环来析构delete[]的对象。
这个在C++对象模型相关的书籍和文章里面都已经讲得比较清楚了。

但是我有个问题,我发现VC的debug版本,为单个delete对象,也生成一个scalar deleting destructor代理函数。
这个让我觉得不明白,因为我觉得没有这样的必要,单个对象的析构没有for循环,也没有析构到一半处理异常的问题。
  
为什么VC会这样? 我贴了一下代码在这里,很简单的:
  1. struct C{
  2.      C(){
  3.          cout << "ctor\n";
  4.      }
  5.      ~C(){
  6.          cout << "dtor\n";
  7.      }
  8. };
  9. int main(int argc, char *argv[])
  10. {
  11.      C obj;//栈对象的析构没有涉及到scalar deleting destructor
  12.      C*p = new C();
  13.      delete p;//这里是我的问题所在,单个对象为什么需要scalar deleting destructor?
  14.      return 0;
  15. }
复制代码
debug版下面生成的汇编代码如下,在main函数的尾部:
  1.     delete p;
  2. 00E55D3F  mov         eax,dword ptr [p]   
  3. 00E55D42  mov         dword ptr [ebp-0F8h],eax   
  4. 00E55D48  mov         ecx,dword ptr [ebp-0F8h]   
  5. 00E55D4E  mov         dword ptr [ebp-104h],ecx   
  6. 00E55D54  cmp         dword ptr [ebp-104h],0   
  7. 00E55D5B  je          main+0D2h (0E55D72h)   
  8. 00E55D5D  push        1   
  9. 00E55D5F  mov         ecx,dword ptr [ebp-104h]   
  10. 00E55D65  call        C::`scalar deleting destructor' (0E514E7h)   
  11. 00E55D6A  mov         dword ptr [ebp-124h],eax   
  12. 00E55D70  jmp         main+0DCh (0E55D7Ch)   
  13. 00E55D72  mov         dword ptr [ebp-124h],0   
  14.      return 0;
  15. 00E55D7C  mov         dword ptr [ebp-0ECh],0   
  16. 00E55D86  mov         dword ptr [ebp-4],0FFFFFFFFh   
  17. 00E55D8D  lea         ecx,[obj]   
  18. 00E55D90  call        C::~C (0E514DDh)   
  19. 00E55D95  mov         eax,dword ptr [ebp-0ECh]   
  20. }
复制代码
看到C obj的析构是在地址00E55D90 call        C::~C

而delete p的析构是在地址00E55D65 call        C::`scalar deleting destructor' (0E514E7h)   
我尝试编译这个程序的release版,未优化的版本,一样的结论。
  
问题
1. 为什么有这个区别? 如果堆上面的对象需要一个代理函数来调用析构+free,为什么构造的时候不需要一个代理函数来malloc+构造,而在析构的时候却需要。这个显得不对称。
2. 我用gcc也测试了一下这个程序,不加优化选项,也没有看到有个scalar deleting destructor之类的结构,加上调试信息,看反汇编也是直接callq xxxx <C::~C()>
  
还请板上朋友解释解释哈!

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
2 [报告]
发表于 2016-09-28 11:28 |只看该作者
这是VC的特性,也叫bug。
VC是可以用delete p来析构new T[xx]创建出来的数组的,这么做其实不符合标准,但VC在标准明确这一点之前就这么做了,所以只好留到今天。

论坛徽章:
14
巨蟹座
日期:2013-11-19 14:09:4615-16赛季CBA联赛之青岛
日期:2016-07-05 12:36:0515-16赛季CBA联赛之广东
日期:2016-06-29 11:45:542015亚冠之全北现代
日期:2015-07-22 08:09:472015年辞旧岁徽章
日期:2015-03-03 16:54:15巨蟹座
日期:2014-12-29 08:22:29射手座
日期:2014-12-05 08:20:39狮子座
日期:2014-11-05 12:33:52寅虎
日期:2014-08-13 09:01:31巳蛇
日期:2014-06-16 16:29:52技术图书徽章
日期:2014-04-15 08:44:01天蝎座
日期:2014-03-11 13:06:45
3 [报告]
发表于 2016-09-28 12:16 |只看该作者
windoze 发表于 2016-09-28 11:28
这是VC的特性,也叫bug。
VC是可以用delete p来析构new T[xx]创建出来的数组的,这么做其实不符合标准,但 ...

VC不标准的地方真多,比如通过基类指针删除派生类数组

  1. #include <iostream>

  2. struct base
  3. {
  4.     virtual ~base() { std::clog<<"~base("<<base_value<<")\n"; }

  5.     int base_value;
  6. };
  7. struct derived : base
  8. {
  9.     virtual  ~derived() { std::clog<<"~derived("<<derived_value<<")\n"; }

  10.     int derived_value;
  11. };

  12. int main( void )
  13. {
  14.     derived* pd = new derived[3];
  15.     pd[0].base_value=0, pd[0].derived_value=1;
  16.     pd[1].base_value=2, pd[1].derived_value=3;
  17.     pd[2].base_value=4, pd[2].derived_value=5;

  18.     base* pb = pd;
  19.     delete[] pb;
  20. }
复制代码

输出竟然是正常的,不可思议
~derived(5)
~base(4)
~derived(3)
~base(2)
~derived(1)
~base(0)



论坛徽章:
95
程序设计版块每日发帖之星
日期:2015-09-05 06:20:00程序设计版块每日发帖之星
日期:2015-09-17 06:20:00程序设计版块每日发帖之星
日期:2015-09-18 06:20:002015亚冠之阿尔艾因
日期:2015-09-18 10:35:08月度论坛发贴之星
日期:2015-09-30 22:25:002015亚冠之阿尔沙巴布
日期:2015-10-03 08:57:39程序设计版块每日发帖之星
日期:2015-10-05 06:20:00每日论坛发贴之星
日期:2015-10-05 06:20:002015年亚冠纪念徽章
日期:2015-10-06 10:06:482015亚冠之塔什干棉农
日期:2015-10-19 19:43:35程序设计版块每日发帖之星
日期:2015-10-21 06:20:00每日论坛发贴之星
日期:2015-09-14 06:20:00
4 [报告]
发表于 2016-09-28 13:01 |只看该作者
回复 3# bruceteen

这个 GCC 也是允许的……

论坛徽章:
2
2015年迎新春徽章
日期:2015-03-04 10:16:532015元宵节徽章
日期:2015-03-06 15:53:22
5 [报告]
发表于 2016-09-28 13:11 |只看该作者
bruceteen 发表于 2016-09-28 12:16
VC不标准的地方真多,比如通过基类指针删除派生类数组

析构函数不加virtual的话,就都挂了。

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
6 [报告]
发表于 2016-09-28 14:09 |只看该作者
就在各位大神讨论标准的时候我已经用好几种非c/c++语言写了hello world

论坛徽章:
14
巨蟹座
日期:2013-11-19 14:09:4615-16赛季CBA联赛之青岛
日期:2016-07-05 12:36:0515-16赛季CBA联赛之广东
日期:2016-06-29 11:45:542015亚冠之全北现代
日期:2015-07-22 08:09:472015年辞旧岁徽章
日期:2015-03-03 16:54:15巨蟹座
日期:2014-12-29 08:22:29射手座
日期:2014-12-05 08:20:39狮子座
日期:2014-11-05 12:33:52寅虎
日期:2014-08-13 09:01:31巳蛇
日期:2014-06-16 16:29:52技术图书徽章
日期:2014-04-15 08:44:01天蝎座
日期:2014-03-11 13:06:45
7 [报告]
发表于 2016-09-29 08:37 |只看该作者
MMMIX 发表于 2016-09-28 13:01
回复 3# bruceteen

这个 GCC 也是允许的……

不可能吧,我用mingw和gcc都试过
我代码中删除的数组,假如A派生了B
B* pb = new B[100];
A* pa = pb;
中pb[1]和pa[1]的地址是不一样的,因为A和B的size不一样大。

论坛徽章:
95
程序设计版块每日发帖之星
日期:2015-09-05 06:20:00程序设计版块每日发帖之星
日期:2015-09-17 06:20:00程序设计版块每日发帖之星
日期:2015-09-18 06:20:002015亚冠之阿尔艾因
日期:2015-09-18 10:35:08月度论坛发贴之星
日期:2015-09-30 22:25:002015亚冠之阿尔沙巴布
日期:2015-10-03 08:57:39程序设计版块每日发帖之星
日期:2015-10-05 06:20:00每日论坛发贴之星
日期:2015-10-05 06:20:002015年亚冠纪念徽章
日期:2015-10-06 10:06:482015亚冠之塔什干棉农
日期:2015-10-19 19:43:35程序设计版块每日发帖之星
日期:2015-10-21 06:20:00每日论坛发贴之星
日期:2015-09-14 06:20:00
8 [报告]
发表于 2016-09-29 08:43 |只看该作者
回复 5# cdsfiui

析构函数不加virtual的话,就都挂了。


destructor 不加 virtual 的话就是 undefined behavior 了。

论坛徽章:
95
程序设计版块每日发帖之星
日期:2015-09-05 06:20:00程序设计版块每日发帖之星
日期:2015-09-17 06:20:00程序设计版块每日发帖之星
日期:2015-09-18 06:20:002015亚冠之阿尔艾因
日期:2015-09-18 10:35:08月度论坛发贴之星
日期:2015-09-30 22:25:002015亚冠之阿尔沙巴布
日期:2015-10-03 08:57:39程序设计版块每日发帖之星
日期:2015-10-05 06:20:00每日论坛发贴之星
日期:2015-10-05 06:20:002015年亚冠纪念徽章
日期:2015-10-06 10:06:482015亚冠之塔什干棉农
日期:2015-10-19 19:43:35程序设计版块每日发帖之星
日期:2015-10-21 06:20:00每日论坛发贴之星
日期:2015-09-14 06:20:00
9 [报告]
发表于 2016-09-29 08:47 |只看该作者
回复 7# bruceteen

源码 c04.cpp:

  1. #include <iostream>

  2. struct base
  3. {
  4.         virtual ~base() { std::clog<<"~base("<<base_value<<")\n"; }

  5.         int base_value;
  6. };
  7. struct derived : base
  8. {
  9.         virtual  ~derived() { std::clog<<"~derived("<<derived_value<<")\n"; }

  10.         int derived_value;
  11. };

  12. int main( void )
  13. {
  14.         derived* pd = new derived[3];
  15.         pd[0].base_value=0, pd[0].derived_value=1;
  16.         pd[1].base_value=2, pd[1].derived_value=3;
  17.         pd[2].base_value=4, pd[2].derived_value=5;

  18.         base* pb = pd;
  19.         delete[] pb;
  20. }

复制代码

$ g++ --version
g++ (GCC) 6.1.1 20160621 (Red Hat 6.1.1-3)


$ g++ -g -Wall -std=c++14    c04.cpp   -o c04

$ ./c04
~derived(5)
~base(4)
~derived(3)
~base(2)
~derived(1)
~base(0)


论坛徽章:
95
程序设计版块每日发帖之星
日期:2015-09-05 06:20:00程序设计版块每日发帖之星
日期:2015-09-17 06:20:00程序设计版块每日发帖之星
日期:2015-09-18 06:20:002015亚冠之阿尔艾因
日期:2015-09-18 10:35:08月度论坛发贴之星
日期:2015-09-30 22:25:002015亚冠之阿尔沙巴布
日期:2015-10-03 08:57:39程序设计版块每日发帖之星
日期:2015-10-05 06:20:00每日论坛发贴之星
日期:2015-10-05 06:20:002015年亚冠纪念徽章
日期:2015-10-06 10:06:482015亚冠之塔什干棉农
日期:2015-10-19 19:43:35程序设计版块每日发帖之星
日期:2015-10-21 06:20:00每日论坛发贴之星
日期:2015-09-14 06:20:00
10 [报告]
发表于 2016-09-29 08:50 |只看该作者
回复 7# bruceteen

倒是 clang++ 好像只调用了 base 的 destructor:
$ clang++ --version
clang version 3.8.0 (tags/RELEASE_380/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix


$ clang++ -std=c++14 -Wall -g c04.cpp -o c04.clang

$ ./c04.clang
~base(4)
~base(2)
~base(0)


评分

参与人数 1信誉积分 +10 收起 理由
cdsfiui + 10 很给力!

查看全部评分

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP