【C++】空指针调用成员函数及访问成员变量

2023-05-16

最近在review代码的时候发现,使用了空指针调用成员函数,并且成员函数内部有使用到成员变量,居然没有出错。很是奇怪,就用一篇博客把关于空指针调用成员函数相关的内容总结起来。


空指针调用成员函数

调用普通成员函数

如果空指针调用普通成员函数,看该函数体中是否使用到了this指针(是否访问非静态成员变量)。如果使用到了this指针,程序会崩溃;如果没有使用到this指针,程序不会崩溃。当然,如果访问静态成员变量,是不会使用到this指针的

例如:

#include <iostream>

class A {
  public:
    A() { a_ = 1; }
    ~A() { }

    int GetValueA() {
      std::cout << "GetValueA()" << std::endl;
      return a_;
    }
    void fun() {
      std::cout << "fun()" << std::endl;
    }

    int a_;
};

int main() {
  A *a = nullptr;
  a->fun();
  a->GetValueA();

  return 0;
}

运行这段代码,调用fun()函数正常,调用GetValueA()函数出错。运行结果为:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11
yngzmiao@yngzmiao-virtual-machine:~/test$ ./main 
fun()
GetValueA()
段错误 (核心已转储)

运行GetValueA()函数,cout语句没有使用到this指针,没有问题顺利打印。但是紧接着return语句使用到了成员变量a_,需要使用到this指针,因此直接出错。

调用虚函数

如果空指针调用虚函数,程序会崩溃,无论函数体中是否使用this指针

例如:

#include <iostream>

class A {
  public:
    A() { a_ = 1; }
    ~A() { }

    virtual void fun_v() {
      std::cout << "fun_v()" << std::endl;
    }

    int a_;
};

int main() {
  A *a = nullptr;
  a->fun_v();

  return 0;
}

运行这段代码,调用fun_V()函数出错。运行结果为:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11
yngzmiao@yngzmiao-virtual-machine:~/test$ ./main 
段错误 (核心已转储)

为什么空指针调用虚函数就直接崩溃呢?

这是因为一个类一旦拥有虚函数,那么它就拥有虚函数表。该类的对象指针为实现多态,会有一个指向该虚函数表的指针(即虚表指针vfptr)。当空指针调用虚函数时,会因为使用到虚表指针而直接出错。

调用静态成员函数

如果空指针调用静态成员函数,由于静态成员函数内部没有this指针,也不能调用非静态成员变量,因此程序没有问题

“特例”

如果空指针调用普通成员函数,而且该普通成员函数中有非静态成员变量的出现,一定会出错么?可以看一下下例:

#include <iostream>

class A {
  public:
    A() { a_ = 1; }
    ~A() { }

    int &GetRefA() {
      std::cout << "GetRefA()" << std::endl;
      return a_;
    }

    int a_;
};

int main() {
  A *a = nullptr;
  a->GetRefA();

  return 0;
}

运行这段代码,其结果为:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11
yngzmiao@yngzmiao-virtual-machine:~/test$ ./main 
GetRefA()

运行结果居然没有错误!和上文空指针调用普通成员函数相比,唯一的不同就是返回的是一个引用。修改成引用之后,居然就没有错误了。

是不是上文讲解的内容有误呢?并不是。既然是引用,我们可以进行输出或者赋值:

std::cout << a->GetRefA() << std::endl;

a->GetRefA() = 2;

上述的两种方式都是有错误的。仔细看来,其实并不违背上文的讲解。由于返回的是引用,尽管使用了this指针,但是并没有对该指针指向的内容进行读取或者修改操作。一旦需要对内容进行读取或者修改,就会出错。因此,也不算是特例。


C++的静态绑定

为什么对this指针的读取和修改,会引起空指针的不同表现?这需要对C++的静态绑定讲起。

对于非虚成员函数,C++这门语言是静态绑定的

a->fun()

这语句的意图是:调用对象a的fun()成员函数。

如果这句话在Java或Python等动态绑定的语言之中,编译器生成的代码大概是:找到a的fun()成员函数,调用它(注意,这里的找到是程序运行的时候才找的,这也是所谓动态绑定的含义:运行时才绑定这个函数名与其对应的实际代码。有些地方也称这种机制为迟绑定,晚绑定)。

但是对于C++。为了保证程序的运行时效率,C++的设计者认为凡是编译时能确定的事情,就不要拖到运行时再查找了。所以C++的编译器看到这句话会这么干:

  1. 查找a的类型,发现它有一个非虚的成员函数叫fun(编译器干的);
  2. 该函数找到了,在这里生成一个函数调用,直接调A::fun(a)。

所以到了运行时,由于fun()函数里面,若没有任何需要解引用a指针的代码,所以真实情况下也不会出错。这里对成员函数的解析,和查找其对应的代码的工作都是在编译阶段完成而非运行时完成的,这就是所谓的静态绑定,也叫早绑定。

更为简单粗暴地理解就是:

a->fun();

fun(A* this);

对于上面的两种方式,本质上是没有区别的。类的成员函数隐藏了一个默认实参:this指针

如果在该函数内部需要用到this指针,此时this指针有视空指针,当然出错。如果没有使用到,只是传了一个空指针而已,没有问题。

对于虚函数,C++这门语言是静态绑定或者静态绑定的。如果使用了多态,就是动态绑定的;如果没有使用多态,就是静态绑定。如果是动态绑定,肯定是需要this指针的;如果是静态绑定,就和上文的解释一致。


总结

空指针调用成员函数,会分一下几种情况:

  • 如果调用编译器确定函数(普通成员函数、静态成员函数),该成员函数中需要对this指针指向的内容进行读取或者修改,出错;反之无错
  • 如果调用运行期确定函数(使用多态的虚函数),出错

相关阅读

  • 是否可以使用空对象指针调用成员函数及访问成员变量
  • C++中的静态绑定和动态绑定
  • 为什么C++调用空指针对象的成员函数可以运行通过?
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++】空指针调用成员函数及访问成员变量 的相关文章

随机推荐

  • idea maven打包编译报错 java.lang.AssertionError: input.getType

    今天使用idea打包编译maven项目 xff0c 出现如下报错 构建报错时 xff0c 最先显示的是这个报错 查了一圈下来 xff0c 我的配置是没有问题的 Failed to execute goal org apache maven
  • gitee仓库人员上限如何一键删人

    我碰到情况是 xff0c 自己的gitee码云 xff0c 新建了很多仓库 xff0c 拉了谁都不知道 今天做了一个项目 xff0c 新建了仓库 xff0c 发了邀请链接 xff0c 想拉一下研发进仓库 xff0c 结果就提示人员上限了 我
  • docker启动pg出现报错Connection matched pg_hba.conf line 89: “local all postgres peer“

    今天在公司使用docker启动pg xff0c 查看日志出现以下报错 provided user name postgres and authenticated user name root do not match Peer authen
  • Deep Learning 最优化方法之Adam

    本文是Deep Learning 之 最优化方法系列文章的Adam方法 主要参考Deep Learning 一书 整个优化系列文章列表 xff1a Deep Learning 之 最优化方法 Deep Learning 最优化方法之SGD
  • 【CMake】CMakeLists.txt的超傻瓜手把手教程(附实例源码)

    新手写CMakeLists txt简直就是实力劝退 xff0c 各种命令让很多人头大 xff0c 如何写一个最基础的CMakeLists txt呢 xff1f 本文从一个实例出发 xff0c 教你编写的基本流程 CMakeLists txt
  • 【Gradle】Groovy的语法详解(下篇)

    上文介绍了Groovy的一般省略规则 xff0c 以及字符串 列表 Map的使用方式 xff0c 然而在Groovy中 xff0c 闭包是其中的一大杀器 xff0c 可以对代码的结构和逻辑带来的优化显而易见 本博文主要对Groovy闭包的使
  • 【OpenCV】OpenCV图像/视频的读取与写入(C++版)

    使用C 43 43 开发图像处理算法时 xff0c 最基础的就是利用OpenCV完成图像文件的输入 输出以及自动内存管理 重点 所以 xff0c 只要需要掌握一些简单的OpenCV的操作即可 本博文就对这些基础内容进行讲解 图像操作 图像读
  • 【OpenCV】OpenCV常用函数(C++版)

    俗话说 xff1a 好记性不如烂笔头 在使用OpenCV的过程中 xff0c 时常会用到很多函数 xff0c 而且往往可能会一时记不起这个函数的具体参数怎么设置 xff0c 故在此将常用函数做一汇总 图像缩放与放大 对图像的各项操作中 xf
  • 【Python】python曲线拟合

    python作为一款可以简单方便地进行科学计算的语言 xff0c 进行曲线拟合自然是必备的功能之一了 本文就如何进行曲线拟合进行讲解 本文需要进行拟合的数据为 xff1a x 61 np arange 1 31 1 y 61 np arra
  • 【C++】NULL和nullptr的关联与差别

    在写代码的过程中 xff0c 有时候需要将指针赋值为空指针 xff0c 以防止野指针 在C中 xff0c 都是使用NULL来实现的 xff1b 在C 43 43 中 xff0c 除了NULL之外 xff0c 还提供了nullptr来进行定义
  • 【C++】C++11可变参数模板(函数模板、类模板)

    在C 43 43 11之前 xff0c 类模板和函数模板只能含有固定数量的模板参数 C 43 43 11增强了模板功能 xff0c 允许模板定义中包含0到任意个模板参数 xff0c 这就是可变参数模板 可变参数模板的加入使得C 43 43
  • 【C++】C++11统一初始化(initializer_list<T>源码分析)

    C 43 43 11之前的初始化语法很乱 xff0c 有四种初始化方式 xff0c 而且每种之前甚至不能相互转换 让人有种剪不断 xff0c 理还乱的感觉 因此 xff0c C 43 43 11添加了统一初始化的方式 xff0c 本文将对统
  • 【C++】右值引用、移动语义、完美转发(上篇)

    在C 43 43 11 xff0c 引入了右值引用的概念 xff0c 在此基础上的移动语义在STL容器中使用非常广泛 简单来说 xff0c move语义使得你可以用廉价的move赋值替代昂贵的copy赋值 xff0c 完美转发使得可以将传来
  • 【C++】右值引用、移动语义、完美转发(下篇)

    上篇中 xff0c 主要讲解了右值引用和移动语义的具体定义和用法 在C 43 43 11中几乎所有的容器都实现了移动语义 xff0c 以方便性能优化 本文以C 43 43 11容器中的insert方法为例 xff0c 详细讲解在容器中移动语
  • AI==喜茶??

    2017年7月10日 xff0c 上海 xff0c 雨 刚从某CV方向的公司下班 xff0c 骑着小黄车朝着浦东某郊区租了一个月的床位行驶着 xff0c 雨打在脸上 xff0c 有点生疼 我不禁在思考 xff0c 这一切到底为了什么 xff
  • 【C++】unique_ptr独占型智能指针详解

    指针是C C 43 43 区别于其他语言的最强大的语法特性 xff0c 借助指针 xff0c C C 43 43 可以直接操纵内存内容 但是 xff0c 指针的引入也带来了一些使用上的困难 xff0c 这要求程序员自己必须手动地对分配申请的
  • 【C++】shared_ptr共享型智能指针详解

    指针是C C 43 43 区别于其他语言的最强大的语法特性 xff0c 借助指针 xff0c C C 43 43 可以直接操纵内存内容 但是 xff0c 指针的引入也带来了一些使用上的困难 xff0c 这要求程序员自己必须手动地对分配申请的
  • 【C++】weak_ptr弱引用智能指针详解

    weak ptr这个指针天生一副小弟的模样 xff0c 也是在C 43 43 11的时候引入的标准库 xff0c 它的出现完全是为了弥补它老大shared ptr天生有缺陷的问题 相比于上一代的智能指针auto ptr来说 xff0c 新进
  • 【C++】模板声明与定义不分离

    一般在写C 43 43 相关代码的时候 xff0c 我们总习惯于将类声明和类实现进行分离 也就是说 xff0c 类的声明一般写在 h文件中 xff0c 而它的实现一般写在 cpp文件中 但是 xff0c 在模板类中 xff0c 这个习惯却要
  • 【C++】空指针调用成员函数及访问成员变量

    最近在review代码的时候发现 xff0c 使用了空指针调用成员函数 xff0c 并且成员函数内部有使用到成员变量 xff0c 居然没有出错 很是奇怪 xff0c 就用一篇博客把关于空指针调用成员函数相关的内容总结起来 空指针调用成员函数