C++指针转换

gooore 2010-03-06 08:51:26
如果有两个类CA, CB, 分别有成员函数funA, funB。

有一个指向CA的指针pA, 如果强制转换一下pB=(CB*)pA, 那么pB,pA实际上指向的是同一个地址,可为什么pB->funB(),和pA->funA()都可以运行?

以前一直以为指针指向的内存应该是某个固定的变量,如果是CA的实例,那么就应该总是CA.

这是为什么呢?
...全文
221 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
uis2r 2010-03-07
  • 打赏
  • 举报
回复
学习啦,真是个学习的好地方.
na2650945 2010-03-07
  • 打赏
  • 举报
回复
引用 15 楼 demon__hunter 的回复:
1 非虚拟成员函数是静态绑定的,
2 非静态成员函数调用时编译器隐式传入this指针的值。
由于funB()为非虚拟成员函数,所以是静态绑定,实质是编译器在pb->funB();处直接翻译成调用B类中funB()的汇编代码,编译器调用funB()时是根据pb类型来确定调用哪个类的funB函数时,也就是说根据->左边对象的类型来确定调用的函数。

函数参数入栈后,this指针的值也会入栈或者存入ecx寄存器。而this指针的值可以认为是pb的值,也就是->左边对象的值。传入this值的目的是为了操作对象里的数据,通过类的声明,编译器可以确定对象内成员变量的相对于类对象起始地址的偏移,即相对this值的偏移。而成员函数调用时隐式传入的this值,编译器是不对this值进行检查,编译器只是简单生成this+偏移操作对象的汇编代码,所以->左边对象的类型正确,编译器就会找到相应的成员函数,不管传入this值是否正确,只要this+偏移访问的地址是合法的,os也不会抱怨,一旦this+偏移不合法,激活os的异常机制,程序才会宕了。
下面代码验证以下上面所说的~~~
C/C++ code
#include<iostream>
#include<windows.h>usingnamespace std;class A
{public:void fun1(){};
};class B:public A
{public:int j;void fun2()
{
cout<<j<<endl;
cout<<"fun2"<<endl;
}
};class C:public A
{public:int i;void fun3()
{
cout<<i<<endl;
cout<<"fun3"<<endl;
}
};int main()
{
A*a=new B;
((B*)a)->j=1000;
((C*)a)->fun3();int i=99;
((C*)&i)->fun3();//静态绑定this+偏移访问内存即是i对应4个字节里内存 ((B*)&i)->fun2();//((B*)0)->fun2();this+偏移访问内存是非法内存
}

学习啦。
程序已经得上升到汇编的高度。
才能开明白啦。
Julykey 2010-03-07
  • 打赏
  • 举报
回复
引用 12 楼 magic7004 的回复:
名字只是浮云,实际上名字代表的是地址。
比如 7 楼的类。
C/C++ codeclass CA
{int a;public :
CA(int n):a(n){}void showa()
{
cout<<"This is CA::showa() The value a ="<<a<<endl;
}
};
如果pa = new CA;其实就是为a分配内存,然后调用CA的构造函数。
pa->showa(); 是调用CA的第二个public函数,实际上就是执行CA的代码段offset(showa)地址的指令。
showa()中访问变量a,其实就是访问pa+offset(a)的地址。

pb = (CB*) pa; 其实就是pb指向pa的地址,并且认为pb是CB类型的。
所以pb->showb(); 其实就是执行CB的代码段offset(showb)地址的指令。
showb()中访问变量b,其实就是访问pb+offset(b)的地址。

但是很不幸的是,根据两个类的定义,offset(a) = offset(b),且a和b都是int类型;
所以代码可以执行,pb->showb执行的代码是CB的,但使用的变量(pb+offset(b))则是CA的构造函数初始化的。

分析的很有道理!

class CA
{
int a;
public :
CA(int n):a(n){}
void showa()
{
cout<<"This is CA::showa() The value a = "<<a<<endl;
}
};

class CB
{
double b;
public :
CB(double n):b(n){}
void showb()
{
cout<<"This is CB::showb() The value b = "<<b<<endl;
}
};

void main()
{
CA a(3);
CB b(5.0);

CA *pA = &a;
CB *pB = &b;

pA->showa();
pB->showb();

pB = (CB*)pA;

pA->showa();
pB->showb();
system("pause");
}

此时,最后一个函数输出的结果为一个随机数。这说明,虽然将pA的地址赋给pB(此时所指为同一块内存pA),但是pB仍然按照自己的结构去理解pA。此时offset(b)和offset(a)已经不相同了,取不到a了,所以offset(b)只能取到一个随机数了。
机智的呆呆 2010-03-06
  • 打赏
  • 举报
回复
1 非虚拟成员函数是静态绑定的,
2 非静态成员函数调用时编译器隐式传入this指针的值。
由于funB()为非虚拟成员函数,所以是静态绑定,实质是编译器在pb->funB();处直接翻译成调用B类中funB()的汇编代码,编译器调用funB()时是根据pb类型来确定调用哪个类的funB函数时,也就是说根据->左边对象的类型来确定调用的函数。

函数参数入栈后,this指针的值也会入栈或者存入ecx寄存器。而this指针的值可以认为是pb的值,也就是->左边对象的值。传入this值的目的是为了操作对象里的数据,通过类的声明,编译器可以确定对象内成员变量的相对于类对象起始地址的偏移,即相对this值的偏移。而成员函数调用时隐式传入的this值,编译器是不对this值进行检查,编译器只是简单生成this+偏移操作对象的汇编代码,所以->左边对象的类型正确,编译器就会找到相应的成员函数,不管传入this值是否正确,只要this+偏移访问的地址是合法的,os也不会抱怨,一旦this+偏移不合法,激活os的异常机制,程序才会宕了。
下面代码验证以下上面所说的~~~

#include<iostream>
#include<windows.h>
using namespace std;

class A
{
public:
void fun1(){};
};
class B:public A
{
public:
int j;
void fun2()
{
cout<<j<<endl;
cout<<"fun2"<<endl;
}
};

class C:public A
{
public:
int i;
void fun3()
{
cout<<i<<endl;
cout<<"fun3"<<endl;
}
};

int main()
{
A *a = new B;
((B*)a)->j=1000;
((C*)a)->fun3();

int i=99;
((C*)&i)->fun3();//静态绑定this+偏移访问内存即是i对应4个字节里内存
((B*)&i)->fun2();
//((B*)0)->fun2();this+偏移访问内存是非法内存

}

2010-03-06
  • 打赏
  • 举报
回复
不考虑虚函数的情况下,大多数成员函数的实现是这样的:

a->fun(para1, para2);
其实是
fun(&a, para1, para2);

套用这个模型,剩下的自己想象~
magic7004 2010-03-06
  • 打赏
  • 举报
回复
引用 11 楼 tianwen2976 的回复:
我认为将指针pA强制转化为指向CB类型时,;pA所指向的地址是不变的。

如果类中没有虚函数,确实是不变的。如果有虚函数,就不一定了
magic7004 2010-03-06
  • 打赏
  • 举报
回复
名字只是浮云,实际上名字代表的是地址。
比如 7 楼的类。
class CA
{
int a;
public :
CA(int n):a(n){}
void showa()
{
cout<<"This is CA::showa() The value a = "<<a<<endl;
}
};

如果pa = new CA;其实就是为a分配内存,然后调用CA的构造函数。
pa->showa(); 是调用CA的第二个public函数,实际上就是执行CA的代码段offset(showa)地址的指令。
showa()中访问变量a,其实就是访问pa+offset(a)的地址。

pb = (CB*) pa; 其实就是pb指向pa的地址,并且认为pb是CB类型的。
所以pb->showb(); 其实就是执行CB的代码段offset(showb)地址的指令。
showb()中访问变量b,其实就是访问pb+offset(b)的地址。

但是很不幸的是,根据两个类的定义,offset(a) = offset(b),且a和b都是int类型;
所以代码可以执行,pb->showb执行的代码是CB的,但使用的变量(pb+offset(b))则是CA的构造函数初始化的。
tianwen2976 2010-03-06
  • 打赏
  • 举报
回复
我认为将指针pA强制转化为指向CB类型时,;pA所指向的地址是不变的。
giant1st 2010-03-06
  • 打赏
  • 举报
回复
引用楼主 gooore 的回复:
如果有两个类CA, CB, 分别有成员函数funA, funB。

有一个指向CA的指针pA, 如果强制转换一下pB=(CB*)pA, 那么pB,pA实际上指向的是同一个地址,可为什么pB->funB(),和pA->funA()都可以运行?

以前一直以为指针指向的内存应该是某个固定的变量,如果是CA的实例,那么就应该总是CA.

这是为什么呢?


都可以运行,并不一定是 正确的运行啊!

如果CA 和 CB的类型极为相似,比如只是成员函数是都仅仅是一条输出语句“cout<<”之类的,那就有可能都能运行啊!
gooore 2010-03-06
  • 打赏
  • 举报
回复
那funA()在什么地方呢?

越来越不太明白了,期待解惑。

引用 2 楼 yyg990441 的回复:
因为调用成员函数pB->funB();实际上是执行funB(pB);将pB作为this指针

所以
pB,pA实际上指向的是同一个地址,可为什么pB->funB()《==》funB(pB);
和pA->funA()《==》funA(pA);
na2650945 2010-03-06
  • 打赏
  • 举报
回复
引用 6 楼 yyg990441 的回复:
5楼的,你这样用不会出错的,解释请看2楼。
但是showa()或者showb()里面如果使用了类的成员变量,就会出错了!

对。
差this指针。
kuillldan 2010-03-06
  • 打赏
  • 举报
回复
引用 6 楼 yyg990441 的回复:
5楼的,你这样用不会出错的,解释请看2楼。
但是showa()或者showb()里面如果使用了类的成员变量,就会出错了!


试了一下,调用pb->showb()输出的果然是a的值3
class CA
{
int a;
public :
CA(int n):a(n){}
void showa()
{
cout<<"This is CA::showa() The value a = "<<a<<endl;
}
};

class CB
{
int b;
public :
CB(int n):b(n){}
void showb()
{
cout<<"This is CB::showb() The value b = "<<b<<endl;
}
};

void main()
{
CA a(3);
CB b(5);

CA *pA = &a;
CB *pB = &b;

pA->showa();
pB->showb();

pB = (CB*)pA;

pA->showa();
pB->showb();
}

输出结果:
This is CA::showa() The value a = 3
This is CB::showb() The value b = 5
This is CA::showa() The value a = 3
This is CB::showb() The value b = 3
请按任意键继续. . .

受教了=_=
yyg990441 2010-03-06
  • 打赏
  • 举报
回复
5楼的,你这样用不会出错的,解释请看2楼。
但是showa()或者showb()里面如果使用了类的成员变量,就会出错了!
kuillldan 2010-03-06
  • 打赏
  • 举报
回复
以下代码编译运行均没有错误
class CA
{
public :
void showa()
{
cout<<"This is CA::showa()"<<endl;
}
};

class CB
{
public :
void showb()
{
cout<<"This is CB::showb()"<<endl;
}
};

void main()
{
CA a;
CB b;

CA *pA = &a;
CB *pB = &b;

pA->showa();
pB->showb();

pB = (CB*)pA;

pA->showa();
pB->showb();
}


输出结果为:
This is CA::showa()
This is CB::showb()
This is CA::showa()
This is CB::showb()
请按任意键继续. . .

请高人解释
herman~~ 2010-03-06
  • 打赏
  • 举报
回复
pB=(CB*)pA

CA 和 CB 两个类如果没任何关系,本身这样转换就编译不过吧
yyg990441 2010-03-06
  • 打赏
  • 举报
回复
BTW,
pB->funB()《==》funB(pB);

是编译器执行的,我们自己不能这样写代码,以上只是伪代码
yyg990441 2010-03-06
  • 打赏
  • 举报
回复
因为调用成员函数pB->funB();实际上是执行funB(pB);将pB作为this指针

所以
pB,pA实际上指向的是同一个地址,可为什么pB->funB()《==》funB(pB);
和pA->funA()《==》funA(pA);
dubiousway 2010-03-06
  • 打赏
  • 举报
回复
show 你的程序


sdfsadfdsaffffffffffffffffff

64,647

社区成员

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

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