奇怪问题(将某对象实例指向NULL,然后又访问这个实例中的方法)

zmxjh 2003-10-13 02:24:33
我在一本书中看到如下代码:

class Controller
{
public:
void print()
{
std::cout<<"good";
}
};

int main(int argc, _TCHAR* argv[])
{
int a = 100;
void *lpCreate = static_cast<void *>(&a);
Controller *pCtrl = static_cast<Controller *>(lpCreate);
pCtrl->print();
return 0;
}
在VC7.0的Debug和Release中都可以显示结果为good???而不会现任何异常,是为什么?

以上程序简化自<<C++ In Action>>一书.
...全文
37 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
Wolf0403 2003-10-15
  • 打赏
  • 举报
回复
呵呵。一个类对象就是一块内存,没别的了。一个证明:
struct S
{
void F()
{
cout << "i is " << i << endl;
}
int i;
};

int main()
{
int a = 2;
void * pv = static_cast<void *>(&a);
S* s = static_cast<S*>(pv);
s->F();
}
打印结果:
i is 2
就是 a 的值。
Jinhao 2003-10-14
  • 打赏
  • 举报
回复
我来猜测一下,因为我没看过这本书。代码也不详细。
关键是在于这几句
CreateData const * create = reinterpret_cast<CreateData const *> (lParam);
pCtrl = static_cast<Controller *> (create->GetCreationData ());

Controller 的对象也许已经在Win::Procedure的外部创建了,而这个对象的指针是通过lParam以参数的形式传递给Win::Procedure再进行处理

作者这样写的目的可能是突出某种设计思想吧
Jinhao 2003-10-14
  • 打赏
  • 举报
回复
当然,因为一个int的大小是4字节,而Controller的对象大小是8字节,当进行访问成员数据_i或_j时(其中一个,至于是哪个视编译器而定),就发生错误。
因为_i或_j他们其中有一个是在int a的内存里,所以另外一个必定会在int a这4个字节之外,这样就发生了错误。只要你不访问_j和_i就不会发生错误!

[ a ] | 未知内存 | int a 的内存布局
[ _j ] | [ _i ] | Controller 对象的内存布局
4KB | 4KB |
而当你调用下面两个函数时
pCtrl->SetInt(12);
pCtrl->print();
就会访问a后面的未知4kb的内存,所以出错拉
zmxjh 2003-10-14
  • 打赏
  • 举报
回复
我将Controller改成如下形式时,程序死掉了:

class Controller
{
public:
Controller()
{
std::cout<<"ctro"<<'\n';
}
void print()
{
std::cout<<_i<<'\n';

}

void Set(int i)
{
_i = i;
_j = i;
}
private:
int _i;
int _j;
};

故意让Controller的内存布局比int a大,上面几位回答都很正确,16号有分,现在正在着手研究作者为何这样做.
zmxjh 2003-10-14
  • 打赏
  • 举报
回复
Controller绝对没有在Procedure的外部创建,我可以保证,作者的本来目的是来展示用C++来封装Win API,对于作者为什么采用如此trick来实现,我实在想不通.

16结贴,希望有更多的朋友关注.

以下是原文,供大家参考:
http://www.relisoft.com/book/win/2control.html
Darkay_Lee 2003-10-14
  • 打赏
  • 举报
回复
.^_^。
这个问题嘛,了解对象的内存布局就比较容易理解了。第一个问题 Jinhao(辣子鸡丁)已经讲得很明白了。第二个问题如果你不将类看成是特别的东西,它就是一块内存,那么它和int a;在内存里面都是一样的玩意儿。对象和a有相同的地址,而i的就放在对象地址的0偏移的位置。既然对象和a的地址一样,那么i所在的地址就是a所在的地址,要访问这个地址当然是合法的啦。

再次提醒:不要拿这些技巧来炫耀,没有什么值得炫耀的。没有一个转移的程序员写这样的代码(除非你的程序已经限定在某个特定平台的特定编译器的特定应用的特殊情况下)
aflyinghorse 2003-10-13
  • 打赏
  • 举报
回复
#include <iostream>
class Controller
{
public:
void print()
{
std::cout<<"good";
}
};
int main(int argc, _TCHAR* argv[])
{
Controller *pCtrl;
pCtrl->print();
getchar();
return 0;
}
这段程序可显示结果good ,Controller 对象没有建立,但仍可访问非虚函数
但不能访问虚函数,因为此时没有vptr
zmxjh 2003-10-13
  • 打赏
  • 举报
回复
我不知道<<C++ In Action>>作者为什么会设计成这样,不知楼上几位怎么看以下例子:

LRESULT CALLBACK Win::Procedure (HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
Controller * pCtrl;
/*这里有一些对pCtrl的一些其它操作.................*/

switch (message)
{
case WM_NCCREATE:
{
CreateData const * create =
reinterpret_cast<CreateData const *> (lParam);
pCtrl = static_cast<Controller *> (create->GetCreationData ());
pCtrl->SetWindowHandle (hwnd);
}
break;
case WM_DESTROY:
// We're no longer on screen
pCtrl->OnDestroy ();
return 0;
}
return ::DefWindowProc (hwnd, message, wParam, lParam);
}

class CreateData: public CREATESTRUCT
{
public:
void * GetCreationData () const { return lpCreateParams; }
int GetHeight () const { return cy; }
int GetWidth () const { return cx; }
int GetX () const { return x; }
int GetY () const { return y; }
char const * GetWndName () const { return lpszName; }
};

class Controller
{
friend LRESULT CALLBACK Procedure (HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam);

void SetWindowHandle (HWND hwnd) { _h = hwnd; }
public:
virtual ~Controller () {}
virtual bool OnDestroy ()
{ return false; }
virtual bool OnMouseMove (int x, int y, KeyState kState)
{ return false; }
protected:
HWND _h;
};



Jinhao 2003-10-13
  • 打赏
  • 举报
回复
上面"这点你可以看看C的函数模型"是多余的。
Jinhao 2003-10-13
  • 打赏
  • 举报
回复
首先,这个代码并不奇怪,class Controller里没有任何成员数据,只有一个void print()
所以,即使是在pCtrl这个指针所指向的内存上没有构建Controller的实例,它也会安全的把那一字节的内存上的对象看作Controller的实例

即使没有构建Controller实例print()也是存在的);//参见《Inside C++ Object Module》
而pCtrl->print();在编译时被转换为 print(*pCtrl);
我们一般对print();的认识,其实被语义所束缚,都认为他会存在于类的实例中,其实并不是这样
class A
{
int a;
public:
void fun(){cout<<"good";}
};

int main()
{
A a;
cout<<sizeof(a);
}
可看出输出结果为4,这个A的实例大小是4字节,而一个int型对象的大小也是4字节,从结果可知,在类的实例里并不存放函数的相关信息。这点你可以看看C的函数模型,
虚函数就不是这样了
Jinhao 2003-10-13
  • 打赏
  • 举报
回复
楼上兄弟对的,pCtrl已经把a的内存看成Controller的_i了,因为
void *lpCreate = static_cast<void *>(&a);
Controller *pCtrl = static_cast<Controller *>(lpCreate);
likangnian0128 2003-10-13
  • 打赏
  • 举报
回复
如果pCtrl 没有初实例化,那么_i并不会分配空间.在这里却看起来都很正常.
————————————————————————————————————————————

_i使用的是a的空间。所以pCtrl->SetInt(12);后,a的值也变成12了。
zmxjh 2003-10-13
  • 打赏
  • 举报
回复
以这看来,pCtrl并不指向一个Controller实例,但结果却完全正常,很奇怪.
zmxjh 2003-10-13
  • 打赏
  • 举报
回复
如果pCtrl 没有初实例化,那么_i并不会分配空间.在这里却看起来都很正常.
zmxjh 2003-10-13
  • 打赏
  • 举报
回复
我昨晚睡在床上时,也想到了Jinhao所说的,但<<C++ In Action>>中有一个更进一步的例子,
我把它简化成:

class Controller
{
public:
void print()
{
std::cout<<_i;
}
void SetInt(int i)
{
_i = i;
}
private
int _i;
};

int main(int argc, _TCHAR* argv[])
{
int a = 100;
void *lpCreate = static_cast<void *>(&a);
Controller *pCtrl = static_cast<Controller *>(lpCreate);
pCtrl->SetInt(12);
pCtrl->print();
return 0;
}
结果仍然正确???

64,686

社区成员

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

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