21

I have a code snippet below:

#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;
}

Run the compiled executable and I find the result is out of my expectation on my llvm-g++ 4.2 machine. The output on my box is as

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

What I want to do in the code is to override a member field (b) in Derived class. Since I think both Base and Derived need to access this field, I define a get member function in Base, thus Derived can inherit it. Then I try to get the member field from different objects.

The result shows that I still get original b in Base by d.get() instead of that in Derived, which is what I expected the code to do. Anything wrong with the code (or my understanding)? Is this behavior specified in the specification? What is the right way to override a member field and properly define its getter and setter?

Summer_More_More_Tea
  • 12,740
  • 12
  • 51
  • 83
  • Why would this result be out of your expectations? You're hiding `B::b` from `D` unless it is qualified. But the hidden, initialized B::b member of `D` is copied as it should be into place by the default copy-ctor of `B`, as it should be. Your base classes don't just starting using their non-hidden derived class member variables by some sort of osmosis. They aren't even aware they're there. – WhozCraig Oct 10 '13 at 08:38

4 Answers4

24

The new b added in the derived class doesn't override base's b. It just hides it.

So, in the derived class you have two b and the virtual method prints corresponding b.

masoud
  • 55,379
  • 16
  • 141
  • 208
11

You can't simply override a member field, and as Base::get is compiled, the b variable is resolved to Base::b so this method will always use this value and not a value from another field with the same name in a derived class.

The usual way to override an attribute is to override the way you access it, i.e. override the accessors (getter and setter).

You can achieve something like that by decorating the getter, but the getter return type will always be the same:

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;
};
Jaffa
  • 12,442
  • 4
  • 49
  • 101
  • Thanks. In my case, the getter code is simple and "reusable" (just return the underlying data). Any approach I can reuse code from `Base`? – Summer_More_More_Tea Oct 10 '13 at 09:00
  • @Summer_More_More_Tea what do you mean? You don't want to override the getter? Then it won't be possible directly. If you want some additional behavior to always be done in the getter, the use an underlying (protected?) virtual getter which you call in your decorated getter. I edit my post with an example – Jaffa Oct 10 '13 at 09:04
3

I'm not sure I understand you correctly, but it by "override" you mean "replace", you'd use a template:

#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;
}

You code in main was also attempting Base b = d; which would lead to slicing, the above fixes that and makes sure you don't accidentially use Base<int> instead of Base<double>.

Live example

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • Thanks, that is indeed a solution. What I cannot explain is, in my original code, why I still get `b` from `Base` with `d.get()`. – Summer_More_More_Tea Oct 10 '13 at 08:47
  • @Summer_More_More_Tea In your code `d.get()` calls the non-virtual `int Base::get()` which obviously accesses `Base::b` which is an `int` that was initialized with `0`. – Daniel Frey Oct 10 '13 at 08:50
  • Even making `get` as `virtual` can not solve the problem. You should override `get` in the derived class. – masoud Oct 10 '13 at 08:58
  • @MM. In the OPs code the return type of `get()` would be different, so you can't simply override it. Or you'd have to accept that the derived `double` is converted to `int`. – Daniel Frey Oct 10 '13 at 09:18
2

you should rewrite your Derived::ctor as follows:

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

And remove filed b in Derived class. Instead mark b in the Base class as protected.

EDIT
Disregard all of this I've found a problem in your code:

Base b = d;

You're copying derived object to base. It copies only base fields. If you want polymorphism try next:

Base *b = &d;
b->get()
zabulus
  • 2,373
  • 3
  • 15
  • 27
  • Thanks. Maybe I did not express myself well. I want to redefine `b` in `Derived` with another type, i.e. change the implementation of the underlying data structure. I understand your answer, however, `b` here is still of type `int`. – Summer_More_More_Tea Oct 10 '13 at 08:52