最近在查 CWnd::GetSafeHwnd()函数时, 顺带发现了一个关于 CWnd::GetSafeHwnd()的实现过程 https://www.debugease.com/vc/2113218.html 的讨论, 其中讨论过程涉及到了空指针调用成员函数的问题, 恰巧之前工作项目中也有偶遇到相关的知识, 因此在此总结一下相关知识.
总结到的相关知识
空对象指针 (NULL) 可以正常调用成员函数, 并正常返回值
空对象指针 (NULL) 调用成员函数时, 如果访问对象的成员变量, 会崩溃
空对象指针 (NULL) 调用类的虚函数会崩溃
对于包含虚函数的类, 其每个对象指针都含有一个隐藏的成员变量, 虚函数表指针 vfptr
实验工具
类声明:
- class CStringHelper
- {
- public:
- CStringHelper();
- virtual ~CStringHelper();
- int DoNothing();
- void SetStringFlag();
- virtual int DoVirtualFunc();
- protected:
- int m_nStringFlag = 1;
- static int ms_nCommVar;
- };
类实现:
- #include "StringHelper.h"
- int CStringHelper::ms_nCommVar = 100;
- CStringHelper::CStringHelper()
- {
- }
- CStringHelper::~CStringHelper()
- {
- }
- int CStringHelper::DoNothing()
- {
- int k = 0;
- ++ms_nCommVar;
- return ms_nCommVar;
- }
- void CStringHelper::SetStringFlag()
- {
- int x = DoNothing();
- m_nStringFlag = 2;
- }
- int CStringHelper::DoVirtualFunc()
- {
- int y = DoNothing();
- return y;
- }
空对象指针 (NULL) 可以正常调用成员函数, 并正常返回值
实验过程
- CStringHelper *pStringHelper = NULL;
- pStringHelper->DoNothing();
- pStringHelper->SetStringFlag();
实验结果
调用 DoNothing 正常
调用 SetStringFlag 过程中, 调用 DoNothin 正常返回, 执行到 m_nStringFlag = 2; 语句时崩溃
理论支撑
调用普通成员函数时(非静态成员函数), 有一个隐藏的参数是 this 指针, 类的普通成员函数的地址在程序开始执行时已经是一个固定的地址了. 只有访问类的非静态成员变量时才会使用到 this 指针(对于成员变量 m_nStringFlag 其实是 this->m_nStringFlag). 只有当访问成员变量时, 且传入的 this 指针为空时才会崩溃.
空对象指针 (NULL) 调用成员函数时, 如果访问对象的成员变量, 会崩溃.
同上
空对象指针 (NULL) 调用类的虚函数会崩溃
实验过程
- CStringHelper *pStringHelper = NULL;
- pStringHelper->DoVirtualFunc();
实验结果
执行到
pStringHelper->DoVirtualFunc();
语句时崩溃
对于包含虚函数的类, 其每个对象指针都含有一个隐藏的成员变量, 虚函数表指针 vfptr
事实支撑
对于拥有虚函数的每个对象, 都有一个默认的隐藏成员变量 -- 虚函数表指针 vfptr
从 VS 的自动变量中可以看出:
而且, 相同的类产生的对象的虚函数表指针 vfptr 都是相同的(可以从实验结果观察出该事实)
对于没有成员变量, 有虚函数的类, 因为该对象函数一个虚函数表指针, 在 32 位操作系统下, 输出其 sizeof, 为 4:
- class CEmptyClass
- {
- public:
- CEmptyClass();
- virtual ~CEmptyClass();
- };
顺带一提, 如果没有虚函数, sizeof 得到的值为 1.
来源: http://www.bubuko.com/infodetail-3198906.html