Несколько основных вопросов в переопределении - PullRequest
2 голосов
/ 26 мая 2010

У меня мало проблем с базовым, и я был бы благодарен, если бы кто-нибудь смог это исправить.

1 : что это значит, когда я говорю "base" b = новый производный; Зачем идти на это? Мы очень хорошо по отдельности можем создавать объекты для базы классов и класс производный, а затем вызвать функционирует соответственно. я знаю это эта база * b = новый производный; является называется нарезкой объектов, но почему и когда можно пойти на это?

2 : я знаю почему не рекомендуется конвертировать объект базового класса для производного объект класса (потому что базовый класс не в курсе производного класса члены и методы). Я даже читал в другие потоки StackOverflow, если это будет так, то мы должны изменить / повторно посетить наш дизайн. Я все это понимаю, однако я просто любопытно, есть ли способ сделать это?

    class base
    {
    public:
        void f(){cout << "In Base";}
    };

    class derived:public base
    {
    public:
        void f(){cout << "In Derived";}
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
        base b1, b2;
        derived d1, d2;
        b2 = d1;
        d2 = reinterpret_cast<derived*>(b1); //gives error C2440
        b1.f(); // Prints In Base
        d1.f(); // Prints In Derived
        b2.f(); // Prints In Base
        d1.base::f(); //Prints In Base
        d2.f();
        getch();
        return 0;
    }

3: В случае моего приведенного выше примера, могу ли я как-нибудь вызвать базовый класс f (), используя объект производного класса? Я использовал d1.base () :: f (), я просто хочу знать, есть ли способ без использования оператора разрешения области видимости?

Большое спасибо за то, что уделили мне время!

Ответы [ 5 ]

5 голосов
/ 26 мая 2010

1. Это не нарезка объектов:

base *b = new derived;

Это присвоение указателя типа base экземпляру derived.

Один (очень распространенный) пример этого в действии - обратные вызовы. Учтите это:

class Observer
{
public:
    virtual void OnEvent() = 0;
};

class Subject
{
public:
    Subject() : observer(0) {}
    void SetObserver(Observer* o) { observer = o; }
    void FireEvent() { if(observer != 0) observer->OnEvent(); }
private:
    Observer* observer;
};

class MyObserver : public Observer
{
public:
    void OnEvent() { ::printf("Hi there!\n"); }
};

int main()
{
    Subject s;
    MyObserver o;
    s.SetObserver(&o);
    s.FireEvent();
    return 0;
}

Это должен быть ожидаемый результат:

Привет!

Обратите внимание, что здесь происходит. Мы передаем указатель на экземпляр от MyObserver до SetObserver(), хотя функция принимает только указатели типа Observer. Это работает, потому что MyObserver (публично) происходит от Observer. В этом случае мы говорим, что MyObserver является Observer.

Тип Observer определяет чисто виртуальную функцию (=0 означает, что функция является чистой; она должна быть реализована производными классами). Ключевое слово virtual сообщает компилятору, что вызов функции должен привести к выполнению самой производной функции. Функция OnEvent() в MyObserver является наиболее производной, поэтому эта версия вызывается, хотя мы вызываем OnEvent() для указателя типа Observer.

Мы сталкиваемся с проблемой выполнения всего этого, потому что в этом коде Subject не нужно знать точный тип своего наблюдателя - наблюдатели просто должны получить из Observer, а экземпляр Subject вызовет реализация самого производного типа OnEvent(). Это позволяет отделить код - Subject не зависит от MyObserver, а MyObserver не зависит от Subject.

2. Нет ничего плохого в приведении указателя от базового к производному типу per se . Следующее является законным и гарантированно работает:

class Base {};
class Derived : public Base {};

int main()
{
    Derived d;
    Base* bp = &d;
    Derived* dp = static_cast<Derived*>(bp);
    return 0;
}

Однако строка перед оператором return в этом фрагменте не определена:

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

int main()
{
    Derived1 d1;
    Base* bp = &d1;
    Derived2* d2p = static_cast<Derived2*>(bp); // WTF?!
    return 0;
}

Значение указателя d2p не имеет смысла, и попытка доступа к чему-либо в нем, безусловно, вызовет сбой, потому что указатель bp на самом деле не указывает на экземпляр Derived2, он указывает на Derived1 экземпляр. Компиляторы не могут поймать это во время компиляции, потому что оба Derived1 и Derived2 наследуются от Base, поэтому приведение успешно компилируется. Это главная опасность при приведении от базового к производному типу - до времени выполнения вы не узнаете, действительно ли приведение даст значимые результаты.

Конечно, если вы не используете dynamic_cast<>(), но приведение влечет штраф за время выполнения. static_cast<>() включает максимум арифметику указателя. reinterpret_cast<>() заставляет указатель принимать другой (потенциально не связанный) тип без выполнения какой-либо арифметики указателя. Это делает reinterpret_cast<>() одним из наиболее опасных приведений и должно использоваться только при необходимости, особенно если static_cast<>() может выполнить работу.

3. Примите во внимание следующее:

class Base
{
public:
    void Foobar() { ::printf("In Base!\n"); }
};

class Derived : public Base
{
public:
    void Foobar() { ::printf("In Derived!\n"); }
};

int main()
{
    Derived d;
    Derived* dp = &d;
    Base* bp = dp;
    dp->Foobar();
    bp->Foobar();
    return 0;
}

Если функция Foobar() не виртуальная, вы получите такой вывод:

In Derived!
In Base!

В противном случае, если функция Foobar() является виртуальной, вы получите следующий вывод:

In Derived!
In Derived!

Чтобы гарантировать, что вызов виртуальной функции Foobar() вызывает базовую реализацию через базовый указатель, необходимо использовать оператор разрешения области действия:

// Prints "In Base!", even if bp actually points
// to an instance of Derived overriding Foobar().
bp->Base::Foobar();
0 голосов
/ 26 мая 2010
base *b = new derived;

(1) У вас есть указатель базового класса на объект производного класса. Это используется при реализации полиморфизма, когда у вас есть несколько производных классов, которые наследуются от общего базового класса. Затем вы можете вызвать функцию правильных производных классов в соответствии с ее типом через указатель базового класса, при условии, что функция является виртуальной.


class base
{
    public:
        virtual void f(){ cout &lt&lt "In Base" &lt&lt endl; }
};

class derived:public base
{
    public:
        void f(){ cout &lt&lt "In Derived" &lt&lt endl; }
};

class derived2: public derived
{
public:
    void f() { cout &lt&lt "In Derived2" &lt&lt endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    derived d1;
    derived2 d2;
    base* b;
    b = &d1;
    b->f();   // calls derived::f();
    b = &d2;
    b->f();   // calls derived2::f();
    return 0;
}

И это не называется нарезкой объектов. Срезы объектов - это когда вы назначаете объект производного класса объекту базового класса (а не указателям), в котором члены объекта производного класса нарезаются и не включаются. Из-за этого вы не можете получить доступ к этим нарезанным элементам из объекта базового класса.

(2) Другие операторы в порядке, за исключением строки, использующей reinterpret_cast.

(3) Вам придется использовать оператор разрешения области, если вы используете объекты или переопределить функцию производного класса для вызова версии базового класса (как упоминалось в других ответах). Однако вы можете использовать указатели:

derived *d = static_cast( &b1 );
d->f();
0 голосов
/ 26 мая 2010

Вопрос 1: Зачем вы это делаете: вы, вероятно, не сделали бы это напрямую. Случай, когда это произойдет, может быть в том случае, если у вас есть, например, функция, которая возвращает «некоторую базу *», и вам все равно, какой тип.

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

Примечание: вы не объявили свою функцию виртуальной, поэтому на самом деле вы не получите ожидаемого поведения.

0 голосов
/ 26 мая 2010
base *b = new derived

Создание указателя базового класса, который указывает на объект производного класса. Это позволяет создавать отношения is-a . Производный класс является базой . Пожалуйста, обратитесь наследование для более подробной информации.

2 . d2 = reinterpret_cast<derived*>(b1); Вы пытаетесь преобразовать объект базового класса в объект производного класса. Но использовалась операция приведения. Это не правильно.

Если вы планируете вызывать метод базового класса, используйте указатели базового класса.

Base* ptr = &b1;
ptr->f();

Я рекомендую вам обратиться FAQ Lite .

0 голосов
/ 26 мая 2010

Да и нет. Вы можете пойти class derived: public base { public: void f() { base::f(); } };

Хотя я не слишком уверен в глобальном масштабе.

...