关于downcast,该如何处理
关于downcast
简单问题,请指教,
代码:
class A
{
int a;
public:
A(int aa){a=aa;}
void printa(){printf("a=%d\n", a);}
};
class B : public A
{
int b;
public:
B(int bb):A(bb){b=bb;}
void printb(){printf("b=%d\n", b);}
};
int main()
{
A *pa = new A(10);
B *pb = (B*)pa;
pb->printb();
return 0;
}
结果是打印b=0,我的问题是,为什么pb可以调用到printb函数,虽然pb是B的指针,但pb指向基类对象,应该完全没有这个函数的实现才对啊。
最好能发个链接介绍一下来龙去脉,谢谢大家!
------解决方案--------------------
C++是静态类型语言, 编译器只知道pb是B的指针, 没法知道它运行的时候会指向什么对象.
只能按照pb的类型去编译可执行代码.
至于执行效果, 就是把你New出来的A后面的某个内存区域当作b成员变量来输出.
------解决方案--------------------
pb是B类型的指针,当然可以调用printfb函数。
编译器只管语法错误,才不管你运行错误。所以即便你写出下面的代码也能通过编译:
B *pb = (B*)pa;只是保证下面的pb->printb();能通过编译,这个转换不会对pa指向的内存有任何改变。
至于你这样转话后printfb到底应该输出什么,我简单分析一下:
首先你要明白C++调用成员函数使用的是__thiscall调用约定。
这个调用约定在调用成员函数前现将对象地址mov到ecx寄存器,在函数内部访问ecx的偏移就能访问成员变量。
如果你不熟悉汇编的话,再给你说形象一点:
可以等价于
我在release下面也是输出b=0。debug下面输出的是b=-33686019,也验证了我的分析。
------解决方案--------------------
你没有搞清楚类和对像的概念民,只要定义了类,类的成员函数实现是一直都存在的.
只有成员变量才和对像有关,你的例子,只有b值是不存在,但函数printb()是存在的,这个函数入口地址已经确定。
this->printb()在编译器中实际是这样的调用的:
printb(this),也就是对像的指针只是函数的一个参数,而函数不是对像指针的附属品。
你运行一下下面的例子就知道了。
如果想看更详细的原理,可以看一下<深入c++模型>,候捷译的,经典书。
简单问题,请指教,
代码:
class A
{
int a;
public:
A(int aa){a=aa;}
void printa(){printf("a=%d\n", a);}
};
class B : public A
{
int b;
public:
B(int bb):A(bb){b=bb;}
void printb(){printf("b=%d\n", b);}
};
int main()
{
A *pa = new A(10);
B *pb = (B*)pa;
pb->printb();
return 0;
}
结果是打印b=0,我的问题是,为什么pb可以调用到printb函数,虽然pb是B的指针,但pb指向基类对象,应该完全没有这个函数的实现才对啊。
最好能发个链接介绍一下来龙去脉,谢谢大家!
------解决方案--------------------
C++是静态类型语言, 编译器只知道pb是B的指针, 没法知道它运行的时候会指向什么对象.
只能按照pb的类型去编译可执行代码.
至于执行效果, 就是把你New出来的A后面的某个内存区域当作b成员变量来输出.
------解决方案--------------------
pb是B类型的指针,当然可以调用printfb函数。
编译器只管语法错误,才不管你运行错误。所以即便你写出下面的代码也能通过编译:
B* pb = (B*)0;
pb->printb();
B *pb = (B*)pa;只是保证下面的pb->printb();能通过编译,这个转换不会对pa指向的内存有任何改变。
至于你这样转话后printfb到底应该输出什么,我简单分析一下:
首先你要明白C++调用成员函数使用的是__thiscall调用约定。
这个调用约定在调用成员函数前现将对象地址mov到ecx寄存器,在函数内部访问ecx的偏移就能访问成员变量。
如果你不熟悉汇编的话,再给你说形象一点:
void printb(){printf("b=%d\n", b);}
可以等价于
void printb(void* pThis)
{
//最后编译成汇编才不管pThis是什么类型。
//只要传进来一个指针就按照后面的指令执行
//成员变量b在类B中偏移4个字节,前4个字节是基类的a。
//取b的值就相当于下面这两行代码
int* p = (int*)((char*)pThis + 4);
int b = *p;
//所以如果传B类的指针没问题,如果传A类的指针,实际上指向类A的后4个字节。
//这4个字节是未知的值
printf("b=%d\n", b);
}
我在release下面也是输出b=0。debug下面输出的是b=-33686019,也验证了我的分析。
------解决方案--------------------
你没有搞清楚类和对像的概念民,只要定义了类,类的成员函数实现是一直都存在的.
只有成员变量才和对像有关,你的例子,只有b值是不存在,但函数printb()是存在的,这个函数入口地址已经确定。
this->printb()在编译器中实际是这样的调用的:
printb(this),也就是对像的指针只是函数的一个参数,而函数不是对像指针的附属品。
你运行一下下面的例子就知道了。
class A
{
public:
void printa(){printf("printa()");}
};
int main()
{
A *pa =NULL;//这是一个空指针
pa->printa();//通过这种方式调用成员函数完全没有问题,只要这个函数不访问成员变量。
return 0;
}
如果想看更详细的原理,可以看一下<深入c++模型>,候捷译的,经典书。