Является ли законным / четко определенным C ++ для вызова нестатического метода, который не обращается к членам через нулевой указатель? - PullRequest
7 голосов
/ 15 июля 2010

Недавно я наткнулся на следующий код:

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 собирается вызвать метод, а затем не видеть ожидаемого сбоя.Мой вопрос: нарушает ли он стандарт C ++ для вызова SomeFooPtr->bar(), где SomeFooPtr == NULL?

Мне приходит в голову, что это может не произойти, потому что определенный пользователем оператор-> возвращает указатель, что означает, что даже еслиэтот указатель равен NULL, он определенно не был разыменован (разыменование NULL-указателя, я уверен, рассматривается стандартом как недопустимое или неопределенное).С другой стороны, семантика необработанных указателей не обязательно должна соответствовать семантике пользовательских указателей - возможно, оператор-> на них считается разыменованием, даже если компилятор не сгенерирует его.

Ответы [ 5 ]

13 голосов
/ 15 июля 2010

Это, вероятно, будет работать на большинстве систем, но это неопределенное поведение.Quoth the Standard:

5.2.5.3

Если E1 имеет тип «указатель на класс X», то выражение E1->E2 преобразуется в эквивалентную форму (*(E1)).E2 [...]

И:

5.2.5.1

Постфиксное выражение, за которым следует точка . или стрелка ->, за которым следует ключевое слово template (14.8.1), за которым следует id-выражение , является выражением постфикса.Постфиксное выражение перед точкой или стрелкой оценивается; 58) [...]

58) Эта оценка происходит, даже если результат не нужен для определениязначение всего постфиксного выражения, например, если id-выражение обозначает статический член.

Оценка *x, где x - нулевой указатель, приводит к неопределенному поведению, поэтому вашеслучай UB, еще до того, как функция была введена.

7 голосов
/ 15 июля 2010

Этот тест не пройден, даже если разыменование не было UB.Он ломается, когда в игру вступают корректировки this для множественного наследования:

#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();
}

печатает здесь «безопасно».

1 голос
/ 15 июля 2010

Это (теперь все вместе) неопределенное поведение . Однако для многих компиляторов это будет работать с дополнительным ограничением, согласно которому метод должен быть не виртуальным.

1 голос
/ 15 июля 2010

Это UB. Хороший способ сделать его аварийным - использовать его как базовый класс производного класса, который использует множественное наследование. YMMV.

1 голос
/ 15 июля 2010

Неважно, если это законно, это сбивает с толку читателя. В реализации, где работает этот код, доступ к vtable осуществляется по типу, а не по объекту.

Более того, я ожидаю, что этот код был добавлен для покрытия ошибки конструктора, что могло бы замаскировать множество проблем в других местах. Отказ конструктора должен обрабатываться правильно, а не с неприятным клочком, как в примере.

...