【问题标题】:Is it legal/well-defined C++ to call a non-static method that doesn't access members through a null pointer?调用不通过空指针访问成员的非静态方法是否合法/定义明确的 C++?
【发布时间】:2011-03-16 12:05:12
【问题描述】:

我最近遇到了以下代码:

class Foo
{
public:
    void bar();
    // .. other stuff
};

void Foo::bar()
{
    if(!this) {
        // .. do some stuff without accessing any data members
        return;
    }

    // .. do normal actions using data members
}

代码可以编译,因为在 C++ 中,方法只是隐式传递“this”指针的函数,并且可以像任何其他指针一样检查“this”是否为 NULL。显然,即使它没有崩溃,这段代码也是令人困惑和不好的做法;在调试器中单步执行代码会很混乱,看到一个 NULL 指针即将调用一个方法,然后看不到预期的崩溃。我的问题是:调用 SomeFooPtr->bar() where SomeFooPtr == NULL 是否违反 C++ 标准?

在我看来,这可能不是因为用户定义的 operator-> 返回了一个指针,这意味着即使该指针为 NULL,它也绝对没有被取消引用(我确定取消引用 NULL 指针被标准为非法或未定义)。另一方面,原始指针的语义不一定必须与用户定义指针的语义相匹配——也许它们上的 operator-> 被认为是取消引用,即使编译器不会生成一个。

【问题讨论】:

标签: c++ pointers null methods standards


【解决方案1】:

它是否合法并不重要,它会让读者感到困惑。在这段代码工作的实现中,vtable 是按类型访问的,当然不是按对象访问的。

此外,我希望这段代码用于覆盖构造函数失败,这将掩盖其他地方的各种问题。构造函数失败应该被正确处理,而不是像示例中那样令人讨厌的混乱。

【讨论】:

    【解决方案2】:

    这是(现在全部)未定义的行为。但是,对于许多编译器来说,它可以工作,但附加的限制是方法必须是非虚拟的。

    【讨论】:

      【解决方案3】:

      它是UB。使其崩溃的一个好方法是将其用作使用多重继承的派生类的基类。 YMMV。

      【讨论】:

        【解决方案4】:

        这可能适用于大多数系统,但它是未定义的行为。标准:

        5.2.5.3

        如果E1 的类型为“指向X 类的指针”,则表达式E1->E2 将转换为等效形式(*(E1)).E2 [...]

        还有:

        5.2.5.1

        后缀表达式后跟一个点 . 或箭头 ->,可选地后跟关键字 template (14.8.1),然后是 id-expression,是一个后缀表达式。计算点或箭头之前的后缀表达式;58) [...]

        58) 即使不需要结果来确定整个后缀表达式的值,也会发生这种评估,例如,如果 id-expression 表示静态成员。

        评估*x 其中x 是一个空指针会导致未定义的行为,所以你的显然是UB 的情况,甚至在输入函数之前。

        【讨论】:

        • 谢谢,这正是我想要的。
        • 抱歉编辑混乱。首先,我忽略了标准中的某些内容,然后我误读了您的问题。现在应该没问题了。
        • 你能举一个A.template B形式的表达式的例子吗?这个语法在哪里相关?
        • @Philip:假设您正在一个模板类中编写代码,该类采用 T。然后在该类中创建一个 some_other_class 的对象。然后事实证明 some_other_class 有一个模板化的成员函数 foo。 some_other_class x; x.template foo();.基本上,当您将模板参数提供给另一个模板时,如果后者模板本身具有模板化成员,则在访问它时必须将额外的模板关键字放在那里,以免混淆解析器。至少我认为。我现在是凭直觉做的,所以我可能描述得并不完全正确。
        • @Philipp:真巧,我几分钟前才知道这种语法!这里:stackoverflow.com/questions/3257890/…
        【解决方案5】:

        即使取消引用不是 UB,此测试也会中断。当多重继承的这种调整进入游戏时,它就会中断:

        #include <stdio.h>
        class B
        {
            int value;
            public:
            void foo()
            {
                if (!this)
                    printf("this==0\n");
                else 
                    printf("safe\n");
            }
        };
        class A { public: int anotherValue; };
        class Z : public A,public B {};
        
        int main()
        {
            Z *z=0;
            z->foo();
        }
        

        在这里打印“安全”。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-04-13
          • 2018-03-13
          • 2013-08-25
          • 2011-12-15
          • 2020-10-01
          • 1970-01-01
          • 2020-03-13
          相关资源
          最近更新 更多