Your confusion stems from a crucial difference between Java and C++.
In Java if you write
MyClass var = whatever;
your variable var
is a reference to the object returned by whatever
. However, in C++ this syntax means "create a new object of type MyClass
by passing the result of the expression whatever
to an appropriate constructor, and copy the resulting object into the variable var
.
In particular, your code creates a new object of type A
, named c
, and passes a temporary default-constructed object of type B
to its copy constructor (because that's the only constructor that fits). Since the newly created object is of type A
, not of type B
, obviously A
's method foo
is called.
If you want to have a reference to an object, you have to explicitly request that in C++, by adding &
to the type. However a reference to non-constant objects cannot be bound to temporaries. therefore you need to explicitly declare also the object you bind to (or alternatively, use a reference to a const object, and fix your foo
member functions to be const
, since they don't change the object anyway). So the simplest version of your code doing what you want would read:
// your original definitions of A and B assumed here
B b; // The object of type B
A& c = b; // c is now a *reference* to b
int main() { c.foo(); } // calls B::foo() thanks to polymorphism
However the better version would be const-correct, and then could use your original construction:
#include <iostream>
class A
{
public:
virtual void foo() const // note the additional const here!
{ std::cout << "foo" << std::endl; }
};
class B : public A
{
public:
void foo() const // and also const here
{ std::cout << "overridden foo" << std::endl; }
};
A const& c = B(); // Since we bind to a const reference,
// the lifetime of the temporary is extended to the
// lifetime of the reference
int main() { c.foo(); } //prints overridden foo
(note that I removed using namespace std;
because it's a bad thing to do (and your code used explicit std::
anyway, so it's just redundant).
Note however, that C++ references are still different from Java references in that they cannot be reassigned; any assignment goes to the underlying object instead. For example:
#include <iostream>
class A { public: virtual void foo() const { std::cout << "I'm an A\n"; } };
class B: public A { public: void foo() const { std::cout << "I'm a B\n"; } };
class C: public A { public: void foo() const { std::cout << "I'm a C\n"; } };
B b;
C c;
int main()
{
A& ref = b; // bind reference ref to object b
ref.foo(); // outputs "I'm a B"
ref = c; // does *not* re-bind the reference to c, but calls A::operator= (which in this case is a no-op)
ref.foo(); // again outputs "I'm a B"
}
If you want to change the object you refer to, you'll have to use pointers:
// definitions of A, B and C as above
int main()
{
A* prt = &b; // pointer ptr points to b
prt->foo(); // outputs "I'm a B"
prt = &c; // reassign ptr to point to c
prt->foo(); // outputs "I'm a C"
}