Преобразование указателя базового класса в указатель производного класса - PullRequest
1 голос
/ 27 июня 2019

Как показано в следующем коде, я пытаюсь преобразовать указатель базового класса в указатель производного класса. Я ожидаю ошибки компилятора из следующего кода, но не сообщает об ошибке. Также функция «SomeMethod_2» печатает значение 10.

#include <iostream>

using namespace std;

class Base {
public:
    Base() {
        cout << "Base class constructor\n";
    }

};

class Derived : public Base
{
public:
    int Val;
    Derived() {
        cout << "Derived class constructor\n";
    }
    void SomeMethod(void)
    {
        cout << "SomeMethod\n";

    }
    void SomeMethod_1(void)
    {
        Val = 10;
    }
    void SomeMethod_2(void)
    {
        cout << Val;
    }
};


int main()
{
    Base* BaseObj = new Base();

    Derived* DerivedObj = (Derived*) BaseObj;

    DerivedObj->SomeMethod();    # Expecting compiler error
    DerivedObj->SomeMethod_1();
    DerivedObj->SomeMethod_2();
    return 0;
}

Ответы [ 3 ]

2 голосов
/ 27 июня 2019

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

Использование указателя, который не указывает на объект этого типа, в большинстве случаев является неопределенным поведением. Неопределенное поведение - только это - нет никаких требований относительно того, что могло бы произойти, таким образом, вы МОЖЕТЕ получить ошибку компилятора или ошибку времени выполнения. Или вам может показаться, что это «работает» (что бы это ни значило), или что-то совершенно неожиданное.

Что касается того, что на самом деле происходит в вашем конкретном случае, функции, скорее всего, используют смежную память, которая не принадлежит *BaseObj для хранения int, но впоследствии это не имело значения. Если вы запомнили delete объект, тем не менее, есть вероятность, что вы столкнетесь с проблемой в этот момент, поскольку перезаписанная память могла использоваться функциями управления кучей памяти.

Обратите внимание, что если Base имеет хотя бы одну виртуальную функцию (например, деструктор), и если вы используете более безопасный dynamic_cast вместо наименее безопасного приведения в стиле C, то результатом приведения будет нулевое указатель, поскольку dynamic_cast проверяет, действительно ли объект относится к этому типу.

class Base {
public:
    Base() {
        cout << "Base class constructor\n";
    }
    virtual ~Base() = default;
};

// ...

int main()
{
    Base* BaseObj = new Base();

    Derived* DerivedObj = dynamic_cast<Derived*>(BaseObj);

    if (DerivedObj) {
        DerivedObj->SomeMethod();
        DerivedObj->SomeMethod_1();
        DerivedObj->SomeMethod_2();
    } else {
        std::cout << "Not a Derived\n";
    }

    delete BaseObj; // Don't forget to match every "new" with a "delete".
    return 0;
}
1 голос
/ 27 июня 2019
Derived* DerivedObj ....
DerivedObj->SomeMethod();    # Expecting compiler error

Я ожидаю ошибки компилятора

Нет причин ожидать ошибки компилятора здесь.DerivedObj - это указатель на Derived, который имеет функцию-член SomeMethod, и поэтому вызов функции правильно сформирован, и компилятор должен успешно скомпилировать его.

Теперь, будет ли поведение косвенногочерез указатель DerivedObj и правильность определения функции-члена зависит от того, является ли указатель действительным.В этом случае оно недопустимо, и поэтому поведение программы не определено.

Также функция «SomeMethod_2» печатает значение 10.

Это примернеопределенного поведения.

1 голос
/ 27 июня 2019

Вы только что выполнили приведение c * в стиле Base * к Derived *. Функция SomeMethod определена в классе Derived, поэтому ее вызов допустим через Derived *, а не во время компиляции. Функции-члены не являются частью экземпляра класса, скорее они помещены в сегмент кода, и вызов функции-члена для конкретного экземпляра возможен при скрытом этом аргументе. Если у вас нет элементов данных, даже нарезки не происходит. Всегда полезно после приведения указателя проверить, является ли то, к чему он приведен, действительным указателем. И, конечно же, в c ++ мы используем dynamic_cast для такого варианта использования, как этот.

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