是否可以使用空对象指针调用成员函数及访问成员变量

最近在查CWnd::GetSafeHwnd()函数时,顺带发现了一个关于CWnd::GetSafeHwnd()的实现过程的讨论,其中讨论过程涉及到了空指针调用成员函数的问题,恰巧之前工作项目中也有偶遇到相关的知识,因此在此总结一下相关知识。

总结到的相关知识

  1. 空对象指针(NULL)可以正常调用成员函数, 并正常返回值
  2. 空对象指针(NULL)调用成员函数时,如果访问对象的成员变量,会崩溃
  3. 空对象指针(NULL)调用类的虚函数会崩溃
  4. 对于包含虚函数的类,其每个对象指针都含有一个隐藏的成员变量,虚函数表指针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。

posted @ 2019-09-15 14:20  HelloWooo  阅读(580)  评论(0编辑  收藏