这个程序为什么会敲蛋?

薛定谔之死猫 码窑株式会社 码农  2008-11-13 01:11:11
先不管存储泄漏,从C++语义上说这两个程序应该是一致的吧,怎么一个能预期运行,一个就敲蛋了呢,初学C++,帮忙解释下
程序一
#include <iostream>

#define interface class

using namespace std;

interface A{
public:
virtual void fun_a()=0;
};

interface B{
public:
virtual void fun_b()=0;
};

class C:public A,public B{
public:
void fun_a(){cout<<"function a"<<endl;}
void fun_b(){cout<<"function b"<<endl;}
~C(){cout<<"destructor"<<endl;}
};

int main(int argc,char **argv){
A *a=new C();
a->fun_a();
delete a;
return 0;
}


程序二
#include <iostream>

#define interface class

using namespace std;

interface A{
public:
virtual void fun_a()=0;
};

interface B{
public:
virtual void fun_b()=0;
};

class C:public A,public B{
public:
void fun_a(){cout<<"function a"<<endl;}
void fun_b(){cout<<"function b"<<endl;}
~C(){cout<<"destructor"<<endl;}
};

int main(int argc,char **argv){
B *b=new C();
b->fun_b();
delete b;
return 0;
}


[code=BatchFile]*** glibc detected *** ./a.out: free(): invalid pointer: 0x08f0600c ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6[0xb7ec23f4]
/lib/tls/i686/cmov/libc.so.6(cfree+0x96)[0xb7ec4456]
/usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0xb80a0031]
./a.out[0x80488f3]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7e69685]
./a.out[0x80487a1]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:01 843784 /home/luojianwen/programer/c/a.out
08049000-0804a000 r--p 00000000 08:01 843784 /home/luojianwen/programer/c/a.out
0804a000-0804b000 rw-p 00001000 08:01 843784 /home/luojianwen/programer/c/a.out
08f06000-08f27000 rw-p 08f06000 00:00 0 [heap]
b7d00000-b7d21000 rw-p b7d00000 00:00 0
b7d21000-b7e00000 ---p b7d21000 00:00 0
b7e52000-b7e53000 rw-p b7e52000 00:00 0
b7e53000-b7fab000 r-xp 00000000 08:01 1073300 /lib/tls/i686/cmov/libc-2.8.90.so
b7fab000-b7fad000 r--p 00158000 08:01 1073300 /lib/tls/i686/cmov/libc-2.8.90.so
b7fad000-b7fae000 rw-p 0015a000 08:01 1073300 /lib/tls/i686/cmov/libc-2.8.90.so
b7fae000-b7fb1000 rw-p b7fae000 00:00 0
b7fb1000-b7fbe000 r-xp 00000000 08:01 1073167 /lib/libgcc_s.so.1
b7fbe000-b7fbf000 r--p 0000c000 08:01 1073167 /lib/libgcc_s.so.1
b7fbf000-b7fc0000 rw-p 0000d000 08:01 1073167 /lib/libgcc_s.so.1
b7fc0000-b7fc1000 rw-p b7fc0000 00:00 0
b7fc1000-b7fe5000 r-xp 00000000 08:01 1073306 /lib/tls/i686/cmov/libm-2.8.90.so
b7fe5000-b7fe6000 r--p 00023000 08:01 1073306 /lib/tls/i686/cmov/libm-2.8.90.so
b7fe6000-b7fe7000 rw-p 00024000 08:01 1073306 /lib/tls/i686/cmov/libm-2.8.90.so
b7fe7000-b80ca000 r-xp 00000000 08:01 91052 /usr/lib/libstdc++.so.6.0.10
b80ca000-b80cb000 ---p 000e3000 08:01 91052 /usr/lib/libstdc++.so.6.0.10
b80cb000-b80cf000 r--p 000e3000 08:01 91052 /usr/lib/libstdc++.so.6.0.10
b80cf000-b80d0000 rw-p 000e7000 08:01 91052 /usr/lib/libstdc++.so.6.0.10
b80d0000-b80d6000 rw-p b80d0000 00:00 0
b80d9000-b80dc000 rw-p b80d9000 00:00 0
b80dc000-b80f6000 r-xp 00000000 08:01 1073168 /lib/ld-2.8.90.so
b80f6000-b80f7000 r-xp b80f6000 00:00 0 [vdso]
b80f7000-b80f8000 r--p 0001a000 08:01 1073168 /lib/ld-2.8.90.so
b80f8000-b80f9000 rw-p 0001b000 08:01 1073168 /lib/ld-2.8.90.so
bfae4000-bfaf9000 rw-p bffeb000 00:00 0 [stack]
Aborted[/code]
...全文
69 点赞 收藏 7
写回复
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
分析的很有道理,第二次迷失在这个阴沟里了,呵呵,谢谢
回复
fox000002 2008-11-13
lz 输出这些,看看就知道了

    cout << (A *)b <<endl;
cout << (B *)b << endl;
cout << (C *)b << endl;
回复
fox000002 2008-11-13
这里存在隐式指针类型转换的问题


A *a=new C();

// 1 new C() 返回一个 C * 类型指针,假定为 C *tmp
// 2 = 之后,进行了隐式指针类型转换,*a = (A *)((char *)tmp + delta(A))
// 这里的 delta(A) = 0, a == tmp


B *b=new C();

// 1 new C() 返回一个 C * 类型指针,假定为 C *tmp
// 2 = 之后,进行了隐式指针类型转换,*b = (B *)((char *)tmp + delta(B))
// 这里的 delta(B) != 0, b != tmp


new 和 delete 的地址不同,assert 就报错了

回复
fox000002 2008-11-13
首先,可以肯定 lz 的写法是不对的,基类的析构函数必须为 virtual

否则乱用指针是对象析构会出问题的

lz 代码应该是涉及 A, B, C 在内存中的布局的问题

这里仅仅说说个人观点,如有不对,请大虾指教

生成 C 的对象后,内存布局为
________

A
________

B
________

C
________

第一种写法,delete 的时候找到的确实是 A 类型,所以没有报错,调用了 A 的默认析构函数,但析构是有问题的

第二种的话,delete 的时候找到的还是 A 类型,但调用的是 B 的默认析构函数,不匹配,所以 assert 报错了

回复
lann64 2008-11-13
#include <iostream>

#define interface class

using namespace std;

interface A{
public:
virtual void fun_a()=0;
virtual ~A(){}
};

interface B{
public:
virtual void fun_b()=0;
virtual ~B(){}
};

class C:public A,public B{
public:
void fun_a(){cout<<"function a"<<endl;}
void fun_b(){cout<<"function b"<<endl;}
~C(){cout<<"destructor"<<endl;}
};

int main(){
B *b=new C();
b->fun_b();
delete b;
return 0;
}

在linux g++ 测试正常运行。
回复
lann64 2008-11-13
没看出什么不同来,除了析构函数不是virtual函数造成的问题外,其他好像没看出问题。
回复
phisherr 2008-11-13
难道这个和继承顺序有关?
回复
发动态
发帖子
C++ 语言
创建于2007-09-28

5.9w+

社区成员

C++ 语言相关问题讨论,技术干货分享,前沿动态等
申请成为版主
社区公告
暂无公告