1.起因

今天写代码的时候遇到一个bug,一个类中的set函数在设置一个POD类型的时候出现了异常,直接段错误退出了。

小tips,POD类型指的是内置类型。比如INT、DOUBLE这类;

想了好久,都没发现这里的问题到底是因为什么。后来才知道,原来空的对象指针,也能被解引用访问到函数!

2.示例

下面是关于这个情况的示例代码

#include <iostream>
using namespace std;

class mytest
{
public:
    mytest() = default;
    mytest(int a):_a(0){}

    void set_int(int a)
    {
        cout << "set int to " << a << endl;
        _a = a;
    }

    void print(int a)
    {
        cout << "just a print " << a << endl;
    }

private:
    int _a = 0;
};


int main()
{
    mytest* ptr = nullptr;
    ptr->print(20);
    ptr->set_int(10);

    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

运行这个程序,会出现段错误。但你会发现这个段错误并不是因为我们的ptr-> 里面出现的,而是成功进入了set_int函数,执行了打印,最终对成员变量_a赋值的时候出现的!

对象指针为空,代表压根不存在一个实际的对象,也没办法对不存在的成员变量操作。

> ./test
just a print 20
set int to 10
[1]    280362 segmentation fault  ./test
  • 1.
  • 2.
  • 3.
  • 4.

当代码很多的时候,就会因为忽略这个特性(其实我当时是压根不知道)而误以为错误出现在set_int函数中。

没有想到是外层的对象指针为空导致的。

3.为什么?

在编译过程中,对象就已经绑定了函数地址。一个类的所有对象,使用的函数地址都是一样的。当编译ptr->set_int(10) 的时候,函数的地址就已经和这个指针绑定了,调用它等价于直接调用 set_int 函数。

如果这个函数中没有需要用到成员变量的地方,也就不需要解引用this指针,是不会出错的。比如上方代码中的print函数,就没有出现异常。

但如果函数内访问了内置成员,那么就会出现解引用空指针导致的段错误!