spinalwrap
spinalwrap

Reputation: 693

C++ equivalent for simple Java polymorphism

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

Answers (3)

Baltasarq
Baltasarq

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

Sergey Kalinichenko
Sergey Kalinichenko

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

vladon
vladon

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 contains ActualBob 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

Related Questions