Reputation: 693
I'm struggling with a seemingly simple task in C++. I'm trying to achieve basic OOP polymorphism.
Consider this Java example:
interface Bob
{
void foo();
}
class ActualBob implements Bob
{
void foo()
{
/* I like foo things */
}
}
class Jane
{
Bob bob;
}
Jane can have any Bob, easypeasy:
Jane jane = new Jane();
jane.bob = new ActualBob();
jane.bob.foo(); // actualbob things
Now, in C++ this seems somewhat more involved.... What do I have to type to get the above behaviour?
In other words, I would like to have member variables of an abstract base class, but would like to do actual-implementation things with them.
class Bob
{
public:
virtual void foo() = 0;
}
class ActualBob : public Bob
{
public:
void foo(){/* I like foo things */}
}
class Jane
{
public:
Bob bob;
}
Taking shortcuts here, but I'd like to do, in C++:
jane.bob.foo(); // does ActualBob things
Also, can I have a std::vector<Bob>
which contains ActualBob
instances?
Upvotes: 2
Views: 251
Reputation: 12212
You nearly have it. The problem is that it seems in Java that you don't have to use anything in order to create a polymorphic object, while actually in Java all that variables are references. Java references are C++ pointers with less capabilities.
In C++, when you type Bob bob;
, you're actually creating an object of the precise type on the left. When you use a pointer instead, then at the right you can have a Bob object (which would be impossible in this case, since the class Bob has a pure virtual method), or any other subclass.
So, in your code:
class Jane
{
public:
Jane()
{ bob = new ActualBob(); }
Bob * bob;
}
We have, however, run into a different, dangerous scenario without warning. Java has a garbage collector, basically meaning that any object which is not referenced is deleted. This is not done automatically in C++, so we have to do it manually, using the delete
operator. Or maybe a smart pointer in this case: they behave in C++ doing the deletion of the object automatically. We have std::unique_ptr<>
available, doing exactly that. There are indeed other scenarios, such as copying Jane objects, which are somewhat managed by a smart pointer. So instead of talking about rules (destructors, copy constructors, operator assignment, and family), let's use one of them:
class Jane
{
public:
Jane()
{ bob.reset( new ActualBob() ); }
unique_ptr<Bob> bob;
}
The unique_ptr<>
smart pointer captures the use of ->
(technically, it has the arrow operator overloaded), so its use is syntactically familiar to the use of a regular pointer. Now:
Jane jane = new Jane();
jane.bob->foo();
Also, can I have a
std::vector<Bob>
which contains ActualBob instances?
Sure, but have to change your code in the same way as above: you would have to use std::vector<Bob *>
, and free the objects pointed by the members yourself. Or, you could use again a smart pointer, as in: std::vector<unique_ptr<Bob>>
.
Hope this helps.
Upvotes: 1
Reputation: 726809
Your code would be fine if Bob
were non-polymorphic:
class Jane
{
public:
Bob bob;
};
However, trying to store a polymorphich object in bob
would lead to object slicing, which drops polymorphism. In order to preserve it, use a pointer or a reference to Bob
.
Depending on the ownership of Bob
you could use a different kind of pointer, or even a reference. For example, if Bob
is allocated dynamically, and could be shared among multiple instances of Jane
, using std::shared_ptr<Bob>
would be appropriate:
class Jane
{
public:
Jane(Bob* bPtr) : bob(bPtr) {}
std::shared_ptr<Bob> bob;
};
Upvotes: 1
Reputation: 8401
Use pointers, smart pointers, like this:
class Bob
{
public:
virtual void foo() = 0;
}
class ActualBob : public Bob
{
public:
void foo() override {/* I like foo things */}
}
class Jane
{
public:
std::unique_ptr<Bob> bob;
}
Taking shortcuts here, but I'd like to do, in C++:
jane.bob.foo(); // does ActualBob things
You can do this:
jane.bob->foo(); // does ActualBob things, if `bob` is a pointer to ActualBob
Also, can I have a
std::vector<Bob>
which containsActualBob
instances?
Yes, with some modifications, you can have a std::vector<Bob*>
(but you must free memory on your own) or, better, some smart pointers, like std::vector<std::unique_ptr<Bob>>
.
Upvotes: 6