Даункинг базового типа - PullRequest
4 голосов
/ 11 мая 2011

Является ли в C ++ неопределенным поведением, если объект базового класса создается как базовый объект , а затем понижается до производного объекта?

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

Но что, если класс Derived просто предоставляетдополнительные функции-члены, но не включают в себя дополнительные данные члена?Например:

class Base
{
    public:
    int x;
};

class Derived : public Base
{
    public:
    void foo();    
};

int main()
{
    Base b;
    Derived* d = static_cast<Derived*>(&b);
    d->foo(); // <--- Is this undefined behavior?
}

Эта программа вызывает неопределенное поведение?

Ответы [ 4 ]

7 голосов
/ 11 мая 2011

Да, это все еще неопределенное поведение, потому что вы лжете компилятору о реальном типе d.

См. Стандарт 5.2.9 / 8:

Значение типа «указатель на cv1 B», где B - тип класса, может быть преобразован в значение типа «Указатель на cv2 D», где D - класс выведено (пункт 10) из B, если действительный стандартное преобразование из «указателя на От D ”до“ указатель на B ”(4.10), cv2 - это та же квалификация, что и большая квалификация, чем, cv1, и B не является виртуальным базовым классом D. Значение нулевого указателя (4.10) преобразуется в значение нулевого указателя тип назначения. Если значение тип «указатель на cv1 B» указывает на B это на самом деле подобъект объект типа D, результирующий указатель указывает на вмещающий объект типа D. В противном случае результат Состав не определен.

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

4 голосов
/ 11 мая 2011

Стандарт С ++ 03, пар. 5.2.9.8 излагает это (выделение мое):

Значение типа «указатель на cv1 B», где B - тип класса, может быть преобразован в значение типа «Указатель на cv2 D», где D - класс выведено (пункт 10) из B, если действительный стандартное преобразование из «указателя на От D ”до“ указатель на B ”(4.10), cv2 - это та же квалификация cv, что и или больше cv-квалификации, чем, cv1, и B не является виртуальным базовым классом D. Значение нулевого указателя (4.10) преобразуется в значение нулевого указателя тип назначения. Если значение тип «указатель на cv1 B» указывает на B это на самом деле подобъект объект типа D, результирующий указатель указывает на вмещающий объект типа D. В противном случае результат Состав не определен.

1 голос
/ 11 мая 2011

Да, это совершенно неопределенное поведение. Вот почему при унынии вы должны отдавать предпочтение dynamic_cast, если вы не очень уверены.

0 голосов
/ 11 мая 2011

Учитывая имеющуюся ментальную модель реализации C ++ в терминах сгенерированного машинного кода, я бы сказал, что если вызываемый метод не является виртуальным, а производный класс не представляет виртуальные методы, когда базовый класс не имеет ни одного, а множественное наследованиене участвует в этом обмане, и и, и ... он должен работать так, как вы ожидаете, если код метода действительно только обращается к элементам, определенным в базовом объекте.

Однако это все еще очевидно UB в C ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...