4

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

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

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

4

5 回答 5

12

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

正如 Matthieu M. 在评论中指出的那样,如果您在代码中执行以下操作:

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

这会导致未定义的行为,这很糟糕

于 2013-01-08T14:59:11.023 回答
6

正如已经指出的,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()
}
于 2013-01-08T15:19:05.627 回答
1

就标准而言,该代码是不合法的,它在实践中被使用(这是不好的做法)。

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

于 2013-01-08T15:01:07.170 回答
1

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

如果您想将检查保存在某处,您可以将它放在一个智能指针类中,如果持有的指针为 NULL,该类可以采取适当的操作。如果“适当的操作”由所持有的类型唯一确定,则可以使用特征类来指定它。

这样,您的 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;
    }
};

不幸的是,我看不到不投掷的方法。没有办法拦截所有方法名称的函数调用,否则我只会*this从.operator->operator()

于 2013-01-08T15:10:34.803 回答
0

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

可以使用类成员访问语法 (5.2.5, 13.3.1.1) 为其类类型的对象或从其类类型派生的类的对象(第 10 条)调用非静态成员函数。

其次我们看5.2.5类成员访问,2.:

表达式 E1->E2 被转换为等价形式 (*(E1)).E2;5.2.5 的其余部分将仅解决第一个选项(点)。

因此,如果 E1 是 a nullptr,则 *E1 将不被允许。所以至少是我的猜测。

于 2013-01-08T15:45:59.723 回答