2013-10-10 5 views
7

У меня есть фрагмент кода ниже:Override поле член в производных классах

#include <iostream> 

using namespace std; 

class Base { 
public: 
    Base() : b(0) {} 
    int get(); 
    virtual void sayhello() { cout << "Hello from Base with b: " << b << endl; } 
private: 
    int b; 
}; 

int Base::get() {sayhello(); return b;} 

class Derived : public Base { 
public: 
    Derived(double b_):b(b_){} 
    void sayhello() { cout << "Hello from Derived with b: " << b << endl; } 
private: 
    double b; 
}; 

int main() { 
    Derived d(10.0); 
    Base b = d; 

    cout << "Derived b: " << d.get() << endl; 
    cout << "Base b: " << b.get() << endl; 
} 

Запустите скомпилированный исполняемый файл, и я нахожу результат из моих ожиданий на моем LLVM-г ++ 4.2 машины. Выход на моей коробке как

Hello from Derived with b: 10 
Derived b: 0 
Hello from Base with b: 0 
Base b: 0 

То, что я хочу сделать в коде, чтобы переопределить член поля (b) в Derived классе. Поскольку я думаю, что Base и Derived необходимо получить к этому полю, я определяю функцию-член get в Base, таким образом Derived может наследовать его. Затем я пытаюсь получить поле участника из разных объектов.

Результат показывает, что я до сих пор получить оригинальный b в Base по d.get() вместо того, что в Derived, что то, что я ожидал, что код делать. Что-то не так с кодом (или моим пониманием)? Указано ли это поведение в спецификации? Каков правильный способ переопределить поле члена и правильно определить его getter и setter?

+0

Почему этот результат не соответствует вашим ожиданиям? Вы скрываете 'B :: b' от' D', если он не квалифицирован. Но скрытый, инициализированный элемент B :: b 'D' копируется так, как он должен быть установлен по умолчанию copy-ctor' B', как и должно быть. Ваши базовые классы не просто начинают использовать свои не скрытые производные переменные класса класса с помощью своего рода осмоса. Они даже не знают, что они там. – WhozCraig

ответ

9

Новый b, добавленный в производный класс, не переопределяет b базы. Это просто шкуры это.

Итак, в производном классе у вас есть два b, и виртуальный метод печатает соответствующие b.

+0

Спасибо. Я не могу объяснить, почему я все еще получаю '0', то есть значение' b' из 'Base', когда я использую' d.get() '. –

+4

Потому что 'get()' не является виртуальным и возвращает 'b' в классе' Base'. Он ничего не знает о классе 'Derived', поэтому он, очевидно, не может вернуть значение члена' Derived :: b'. Только 'sayhello()' является виртуальным, поэтому это единственный метод, который может возвращать 'b' из' Derived'. –

+0

Я должен переопределить 'get' в' Derived' также? –

7

Вы не можете просто переопределить член поля, а также Base::get компилируется, переменная b разрешен к Base::b поэтому этот метод всегда будет использовать это значение, а не значение из другого поля с тем же именем в производном классе ,

Обычный способ переопределения атрибута - переопределить способ доступа к нему, то есть переопределить accessors (getter и setter).

Вы можете достичь что-то подобное, украшая сорбент, но тип возвращаемого геттер всегда будет таким же:

class Base { 
public: 
    Base() : b(0) {} 
    int get(); 
    virtual void sayhello() { cout << "Hello from Base with b: " << b << endl; } 
protected: 
    virtual int getB() {return b;} 
private: 
    int b; 
}; 

int Base::get() {sayhello(); return getB();} 

class Derived : public Base { 
public: 
    Derived(double b_):b(b_){} 
    void sayhello() { cout << "Hello from Derived with b: " << b << endl; } 
protected: 
    int getB() override {return b;} // conversion from double to int 
private: 
    double b; 
}; 
+0

Спасибо. В моем случае код геттера прост и «многоразовый» (просто верните базовые данные). Любой подход я могу повторно использовать код из «Базы»? –

+0

@Summer_More_More_Tea Что вы имеете в виду? Вы не хотите переопределять получателя? Тогда это будет невозможно напрямую. Если вы хотите, чтобы в геттере всегда выполнялось какое-то дополнительное поведение, используйте базовый (защищенный?) Виртуальный приемник, который вы вызываете в своем украшенном геттере. Я редактирую свой пост с примером – Geoffroy

1

Я не уверен, что я правильно вас понимаю, но его «переопределить» вам значит «заменить», вы бы использовать шаблон:

#include <iostream> 
using namespace std; 

template< typename T > 
class Base { 
public: 
    Base() : b(0) {} 
    Base(T b_) : b(b_) {} 
    T get(); 
    virtual void sayhello() { cout << "Hello from Base with b: " << b << endl; } 
protected: 
    T b; 
}; 

template< typename T > 
T Base<T>::get() {sayhello(); return b;} 

class Derived : public Base<double> { 
public: 
    Derived(double b_):Base(b_){} 
    void sayhello() { cout << "Hello from Derived with b: " << this->b << endl; } 
}; 

int main() { 
    Derived d(10.0); 
    Base<double>* b = &d; 

    cout << "Derived b: " << d.get() << endl; 
    cout << "Base b: " << b->get() << endl; 
} 

вы код в main также пытающийся Base b = d;, которая привела бы к нарезке, вышеуказанные исправлениям, что и делает, что вы не accidentially использовать Base<int> вместо Base<double>.

Live example

+0

Спасибо, это действительно решение. Я не могу объяснить, в моем исходном коде, почему я все еще получаю 'b' из' Base' с 'd.get()'. –

+0

@Summer_More_More_Tea В вашем коде 'd.get()' вызывает не-виртуальный 'int Base :: get()', который явно обращается к базе Base :: b', которая является 'int', которая была инициализирована' 0'. –

+0

Даже создание 'get' как' virtual' не может решить проблему. Вы должны переопределить 'get' в производном классе. – deepmax

2

вы должны переписать Derived :: CTOR следующим образом:

Derived(double _b) 
:Base(_b) 
{} 

И удалить подал b в производном классе. Вместо этого отметьте b в классе Base как защищенный.

EDIT
Игнорирование все это я нашел проблему в коде:

Base b = d; 

Вы копирование производный объект базы. Он копирует только базовые поля. Если вы хотите полиморфизм, попробуйте сделать следующее:

Base *b = &d; 
b->get() 
+0

Спасибо. Может быть, я не очень хорошо себя проявил. Я хочу переопределить 'b' в' Derived' с другим типом, т. Е. Изменить реализацию базовой структуры данных. Я понимаю ваш ответ, однако, 'b' здесь по-прежнему имеет тип' int'. –

Смежные вопросы