7

This is the code:

#include <string>
#include <vector>
#include <iostream>
using namespace std;
class A {
public:
  virtual const string f() const { return "A"; }
};
class B : public A {
public:
  const string f() const { return "B"; }
};
int main(int ac, char** av) {
  vector<A> v;
  v.push_back(B());
  cout << v.at(0).f() << endl;
  return 0;
}

Expected result is B, but it's A. As I understand object slicing is taking place. How to avoid it? Shall I store pointers in vector instead of object instances? Is this the only choice?

yegor256
  • 102,010
  • 123
  • 446
  • 597
  • Object slicing, although happening here, is not what causes the wrong result, it is the function invocation by member operator (.) Virtual functions need to be invoked with the member-by-pointer operator (->) (except when using references) – Ozan Jun 04 '10 at 11:24
  • 6
    @Ozan: nonsense. Calling a virtual member function from another member function (virtual or not) will also result in a virtual call. – MSalters Jun 04 '10 at 11:26
  • Yes, I just wanted to point out to Vincenzo that using the member operator, not object slicing, was the culprit. It is a common mistake when java/c# people write c++ – Ozan Jun 04 '10 at 13:31
  • 3
    @Oznan: NO. Using the member operator is *not* the problem here. If OP stored pointers instead of values, and called using the dot operator like this: `cout << (*v.at(0)).f() << endl;` it would still call the virtual method and produce `B` – John Dibling Jun 04 '10 at 14:25

3 Answers3

13

You need to store pointers. If these refer to dynamically allocated objects, use smart pointers.

sbi
  • 219,715
  • 46
  • 258
  • 445
13

Ordered from most simple, to most complex (but most nice).

Solution 1:

vector<B> v;
v.push_back(B());
cout << v.at(0).f() << endl;

Solution 2:

vector<A*> v;
v.push_back(new B());
cout << v.at(0)->f() << endl;
while(!v.empty()) { delete v.back(); v.pop_back(); }

Solution 3:

vector<boost::shared_ptr<A>> v;
v.push_back(boost::make_shared<B>());
cout << v.at(0)->f() << endl;

If you don't want slicing to happen, you need to take into account the fact that different objects may have different sizes -- ergo you need a solution that can work with variable sizes -- that makes storing on the heap a must.

sbi
  • 219,715
  • 46
  • 258
  • 445
Kornel Kisielewicz
  • 55,802
  • 15
  • 111
  • 149
  • 2
    `vector> v;` is really bad idea. Never store `auto_ptr` in a vector, as its copy ctor modifies the passed object. See this:http://www.gotw.ca/publications/using_auto_ptr_effectively.htm for the reason. – Naveen Jun 04 '10 at 11:08
  • I'm sure it's actually _forbidden_ to do that. `std::auto_ptr` doesn't meet the requirements for a container element. – sbi Jun 04 '10 at 11:17
  • @sbi, duh, I never use `auto_ptr` and mixed it up with `scoped_array` -_-, corrected – Kornel Kisielewicz Jun 04 '10 at 11:19
  • hey!! i was also searching for the same solution..i like the solution the most:-) – srinuvenu Jul 15 '11 at 14:21
3

Well, in your code you could use a vector of B. Note that virtual functions will only be dispatched properly if called via a pointer or a reference. However, assuming you really want your vector to contain both A and B objects, you need to make it vector of A pointers, and create the A and B objects dynamically.