c++在成员函数中测试this指针是否合法?

Is it legal C++ to test the this-pointer in a member function?

本文关键字:是否 指针 this 测试 成员 函数 c++      更新时间:2023-10-16

我有一个涉及不同类类型对象的应用程序。对象由指针引用。空指针表示关联对象不存在。目前调用代码很麻烦,因为每次使用指向对象的指针时,它都会测试指针值是否为空,并在为空时采取一些适当的操作。因为在不存在的情况下采取的默认操作取决于对象的类型,所以我更愿意将其编码在对象本身的类中,而不是在调用程序中。这将导致如下结构:

class C
{ ... 
  void member_func() //non-virtual !
  { if (this) { do something with the object ... }
    else { take some default action }
  }
  ...
};

显然成员函数不能是虚函数,因为当对象不存在时查找表不存在,虚函数调用将失败。但是对于非虚成员函数,这段代码合法吗?对于我尝试过的编译器,它似乎可以正确地工作,但我担心可能的不可移植性。在标准中,我找不到一个明确允许或明确禁止这种结构的条款。

this在成员函数中永远不会为null,因此您执行的检查是无用的。

正如Matthieu M.在注释中指出的,如果你在代码中做了这样的事情:

C* c = 0; 
c->member();

这会导致未定义行为,这是不好的

如前所述,this永远不能是空指针。如果是,那么您已经调用了未定义的行为。相反,您可以创建一组重载函数,如下所示:

void DoTheThing(C* cp)
{
    if (cp)
        cp->member_func();
    else
    {
        // take some default action
    }
}
void DoTheThing(B* bp)
{
    if (bp)
        bp->some_other_member_func();
    else
    {
        // take some default action
    }
}

如果你想调用的函数在每个类中都有相同的名称,那么你可以在每个类中创建一个静态函数,执行该类的默认操作(所有函数都具有相同的名称),并创建一个模板:

template<typname T>
void DoTheThing(T* tp)
{
    if (tp)
        tp->member_func();
    else
        T::default_action()
}

标准而言,代码是不合法的,但是它在实践中被使用(是坏的实践)。

实际上,IIRC MFC在内部使用这些检查

检查 this == NULL是否无问题。通过NULL对象指针调用方法是。

如果你想把检查放在某个地方,你可以把它放在一个智能指针类中,如果持有的指针为NULL,它可以采取适当的行动。如果"适当的操作"是由持有的类型唯一决定的,你可以使用trait类来指定它。

这样你的NULL检查和他们的逻辑是保持在一起的,而不是混合到调用者或方法代码。


// specialize this to provide behaviour per held type
template <typename T> struct MaybeNullDefaultAction {
    void null_call() { throw std::runtime_error("call through NULL pointer"); }
}
template <typename T> class MaybeNull: MaybeNullDefaultAction<T> {
    T *ptr;
public:
    explicit MaybeNull(T *p) : ptr(p) {}
    T* operator-> () {
        if (!ptr)
            null_call();
        // null_call should throw to avoid returning NULL here
        return ptr;
    }
};

不幸的是,我看不到没有抛出的方法。没有办法拦截所有方法名称的函数调用,否则我就从operator->返回*this,并在operator()中完成工作。

我认为这是不允许的。你要求参考标准。我相信首先感兴趣的是9.3.1非静态成员函数,1.:

非静态成员函数可以为其类的对象调用类型,或从其类派生的类(第10条)的对象类型,使用类成员访问语法(5.2.5,13.3.1.1)。
其次,让我们看一下5.2.5类成员访问,2.:

表达式E1->E2被转换成等价形式(*(E1))。E2;5.2.5的剩余部分将只处理第一个选项(点)。

所以如果E1是nullptr, *E1将不被允许。