Reputation: 3877
I am pretty new to C++ and I am coming from Java/C#.
I know that in Java and C# you can make a class, have another class inherit from it, and override its function. You can then make list of the parent class and insert to this list objects of the child class. After that you can use it's functions that were overridden.
Example:
public class Parent
{
public virtual void test()
{
Console.WriteLine("test");
}
}
public class Child : Parent
{
public override void test()
{
Console.WriteLine("test2");
}
}
Usage:
List<Parent> tests = new List<Parent>();
tests.Add(new Child());
tests[0].test();
Output:
test2
In C++, when I do this with std::vector
, it calls the parent's member function, rather than that of the child.
How can I do the above in C++?
Upvotes: 3
Views: 5442
Reputation: 224179
I feel like there's two problems here for you. One is a syntactical problem, which others have already addressed. However, it seems that you also have an underlying problem of trying to write Java/C# code in C++. This will lead to misery no matter what the syntactical problems are, so I try to address this here.
In c++ when I do this with vector it calls that parent's function. How can I do the example above in C++?
Java and C# use the object-oriented paradigm for everything. C++ is different in that C++ is a multi-paradigm language. It supports (more or less) Structured, OO, Generic, Functional and whatnot Programming paradigms. You can freely mix and mingle paradigms, and C++ shines brightest where you do that.
The part of the standard library that derives from the STL, that is: containers, algorithms, iterators, are not OO at all. They are applying Generic Programming. One of the attributes of that is that the containers usually (there are exceptions, but not within the standard library itself) store values, rather than references. Polymorphism, however, at least runtime polymorphism, only operates on references (or, syntactically, pointers, which are, semantically, references, too).
If you have a std::vector<base_class> vc
, this will store actual values, rather than references to objects somewhere on the heap. If you put an object into such a container, the object will actually be copied into the container. If you put in a derived_class
object, then that is subjected to slicing. That is, only the base_class
part of it will be copied into the container, all the derived_class
parts will be disregarded. You then end up with an actual base_class
object in the container, rather than, as in Java and C#, a base class reference to a derived class object somewhere on the heap.
That is why invoking a member function on that object will end up in the base class: there is no derived class object to invoke a function on.
In C++, if you want to employ OOP, you will usually have to dynamically allocate derived class objects (i.e., new derived_class()
) and assign them to base class pointers. The problem with this is that C++ does not have garbage collection, so you must keep track of those pointers, and all the copies made from it, and explicitly delete the object just before the last pointer gets destroyed. That is very error-prone to do manually, which is why nowadays everybody lets smart pointers do this automatically.
So what you want is std::vector<smart_ptr<base_class>>
and put in new derived_class()
objects. What the symbolic smart_ptr
refers to depends on your needs. If you plan to store pointers to those objects nowhere but in that container, std::unique_ptr
(std::tr1::unique_ptr
if your compiler only supports C++03, or boost::unique_ptr
if it doesn't even support that) would be ideal. If you freely pass around such pointers, and them to keep track of when the last goes out of scope for themselves, std::shared_ptr
would be better.
Now, all this said, I feel the need to add: You might not need to do this the OO way at all. There might be a much better design if you could just let go of the rigid OO thinking Java and C# have imprisoned you in.
If you employ polymorphism just so you can pass containers with different content to the same algorithms, then employing Generic Programming might be much better:
template<typename FwdIt>
void do_something(FwdIt begin, FwdIt end)
{
while(begin != end)
if(begin->foo() == bar()) // whatever
begin->baz(); // whatever
}
std::vector<some_class> vs;
std::vector<other_class> vo;
std::deque<other_class> do;
// ...
do_something(vs.begin(), vs.end());
do_something(vo.begin(), vo.end());
do_something(do.begin(), do.end());
This works for all types (here it's some_class
) that have a foo()
member not taking any arguments and returning something comparable with whatever bar()
returns, and have a baz()
member, not taking any arguments either. (If you try to use some type that doesn't have those, the compiler will bark at you.)
Upvotes: 8
Reputation: 3741
It looks like the boost::ptr_container
library would be very helpful for you.
It works in the same manner as a vector of (smart) pointers but it has the added benefit of having syntax designed for use as such.
So for example you can do the following:
typedef boost::ptr_vector<AbstractClass> PolyVector;
PolyVector polyVect;
polyVect.push_back( std::unique_ptr( new ChildClassA() ) );
polyVect.push_back( std::unique_ptr( new ChildClassB() ) );
polyVect.push_back( std::unique_ptr( new ChildClassC() ) );
BOOST_FOREACH( PolyVector::value_type item, polyVect)
item.memberFunction( x );
Which will call the derived class implementation of the virtual memberFunction
.
Upvotes: 1
Reputation: 154047
Unlike Java or C#, C++ uses value semantics by default. An
std::vector<Parent>
contains actual objects of Parent
type, and not
pointers or references. When you insert into the vector, the object you
are inserting is copied, and it is copied into an object of Parent
type. (Objects can't change type.) This is called slicing.
If you want to use polymorphism in C++, it is necessary that you specify explicitly that you want reference semantics. Both pointers and references provide reference semantics, and it's possible to define "smart pointers"—classes which behave like a pointer to some other class. Since references don't support the copy/assignment semantics required by the standard containers, they can't be used to instantiate a container, so if the container is to hold polymorphic objects, it must be defined to contain pointers. So:
std::vector<ValueType> v;
v.push_back( ValueType() ); // no new
but
std::vector<BaseType*> v;
v.push_back( new DerivedType() ); // dynamic allocation.
Because of slicing, polymorphism and copy/assignment don't work well together, and it is usual to block copy/assignment in classes designed to be the base of a hierarchy.
Also, if you are going to manage objects through pointers to the base class, the destructor should be virtual:
class Parent
{
public:
virtual ~Parent() {}
// ...
};
Otherwise, you'll run into undefined behavior when you go to delete the object (through a pointer to its base).
Upvotes: 3
Reputation: 32538
In C++, this would look something like the following:
MyList<Parent*>* tests = new MyList<Parent*>();
tests->Add(new Child());
tests->test();
In order to invoke polymorphic functionality in C++ where you would call the child's function and not the parents, you have to use either pointers or references that point or reference the parent class, and the class methods themselves need to be declared as virtual
in the parent and child class declarations.
Keep in mind that using raw pointers like this could cause some serious memory leaks if you do not compensate for the fact that the MyList
object "owns" (or should own) the pointers being passed to it. If ownership is ambiguous, you need to be extra careful, or use something like std::shared_ptr<T>
. For instance, if you decided to use a STL container like std::vector
with raw-pointers, then the container will not "own" the memory allocated to each pointer, and when the container is destroyed, it will not free the memory being pointed to by each of it's members, resulting in a nasty memory leak.
BTW, this is a very important point about C++ ... unlike C#/Java, C++ uses explicit, not implicit pointers. Therefore if you declare an object such that it is not a pointer (i.e., it's a static, or "automatic" variable on the stack), then if you copy a derived class instance object into a parent instance, you will end up "slicing" the parent portion of the derived object off, and just copying the parent portion of the derived object. That's not what you want. You want polymorphic behavior, and therefore you must use pointers or references to the parent class-type.
For instance, here is a working example of polymorphic behavior:
#include <iostream>
//polymorphic base
struct test
{
virtual void print() { std::cout << "I'm the parent" << std::endl; }
};
//derived type
struct derived : public test
{
virtual void print() { std::cout << "I'm the derived" << std::endl; }
};
int main()
{
test* a = new test;
test* b = new derived;
a->print();
b->print(); //calls derived::print through polymorphic behavior
return 0;
}
Upvotes: 0
Reputation: 1153
test()
should be made virtual
in your Parent
class to make sure that the Child
class's test()
is being invoked.
Upvotes: 1
Reputation: 1
C++ doesn't have an override
keyword. Just redeclare your overridden method as virtual
.
Upvotes: 0