Это - легальный/четко определенный C++ для вызова нестатического метода, который не получает доступ к участникам через нулевого указателя?

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

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++ методы являются просто функциями, которые неявно передаются указатель для 'этого', и 'это' может быть проверено для ПУСТОГО УКАЗАТЕЛЯ точно так же, как любой другой указатель. Очевидно, этот код является запутывающей и плохой практикой даже при том, что это не отказывает; это довольно сбивало бы с толку, чтобы ступить через код в отладчик, видеть, что Нулевой указатель собирается обратиться к методу это, и затем не посмотрите ожидаемый катастрофический отказ. Мой вопрос: это нарушает стандарт C++ для вызова SomeFooPtr->bar() где SomeFooPtr == NULL?

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

7
задан Joseph Garvin 15 July 2010 в 15:55
поделиться

5 ответов

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

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, еще до ввода функции.

13
ответ дан 6 December 2019 в 07:49
поделиться

Этот тест не работает, даже если разыменование не было 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();
}

выводит здесь «безопасно».

7
ответ дан 6 December 2019 в 07:49
поделиться

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

1
ответ дан 6 December 2019 в 07:49
поделиться

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

1
ответ дан 6 December 2019 в 07:49
поделиться

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

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

1
ответ дан 6 December 2019 в 07:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: