1

I'm facing the following situation:

 class Base{
      virtual void Stuff() = 0;
 };

 class ConcreteA : public Base{
      void Stuff() {};
 };

 class ConcreteB : public Base{
      void Stuff() {};
 };

 class Context {
      Base exec() {
           ConcreteA conc_a();
           return a;
      }
 };

Of course the compiler gives me an error since Base is abstract. However, I'd need that exec() return a Base type. I know that I can make the compiler happy using pointers or references, but since the object ConcreteA is created in exec(), returning by value is the best way to avoid dangling references or pointer with undefined ownership.

Is there a way to avoid using pointers or references to handle this kind of situations?

gcswoosh
  • 1,279
  • 1
  • 15
  • 31

1 Answers1

3

This looks like a perfect, simple use case for std::unique_ptr. Here is a C++14 example:

#include <memory>
#include <iostream>

class Base{
public:
      virtual ~Base() {}
      virtual void Stuff() = 0;
};

class ConcreteA : public Base{
      void Stuff() { std::cout << "ConcreteA::Stuff\n"; };
};

class ConcreteB : public Base{
      void Stuff() { std::cout << "ConcreteB::Stuff\n";};
};

class Context {
public:
      std::unique_ptr<Base> exec() {
           return std::make_unique<ConcreteA>();
      }
};

int main()
{
    Context c;
    auto base_ptr = c.exec();
    base_ptr->Stuff();
}

Memory is automatically deleted when base_ptr in main goes out of scope.

Note that I also made the Base destructor virtual. Perhaps you just left it out in your code example for the sake of brevity, but its importance should be stressed, and I think it should be kept even in short example code.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • It might be worth drawing attention to the importance of adding the virtual destructor. – Mike Seymour Mar 13 '15 at 17:26
  • @MikeSeymour: Right. I added something to the answer. – Christian Hackl Mar 13 '15 at 17:28
  • @Mike Seymour What do you mean exactly? The unique_ptr should be automatically deleted when Context goes out of scope, am I wrong? – gcswoosh Mar 13 '15 at 17:30
  • 1
    @Gabrielecswoosh: The `unique_ptr` will delete the `ConcreteA` object via a pointer to `Base`. That gives undefined behaviour unless `Base` has a virtual destructor. – Mike Seymour Mar 13 '15 at 17:32
  • 1
    @Gabrielecswoosh: As Mike correctly says, it is undefined behaviour to delete (or in the case of `std::unique_ptr`, let someone else automatically delete) an object of derived type through a base pointer if the base destructor is not virtual. Try to make `Base::~Base` non-virtual and add `~ConcreteA() { std::cout << "~ConcreteA\n"; }`. You will very likely see one possible outcome of undefined behaviour, but the program could really do anything it wants. – Christian Hackl Mar 13 '15 at 17:34
  • What if after calling `exec` I need the concrete type in the main function? without the unique_ptr the substitution is made by the compiler, but not in this case – gcswoosh Mar 13 '15 at 17:52
  • @Gabrielecswoosh: Not sure what you mean. The compiler will not substitute a base type for a derived type. You'd need a `dynamic_cast` for that. But that's often already a sign of class-design errors. Why return `Base` if you will specifically need `ConcreteA` later on? – Christian Hackl Mar 13 '15 at 17:55
  • @Christian Hackl Because actually I have an AsbtractContext class. ContextA that produces ConcreteA and ContextB that produces ConcreteB. Afterwards, other classes will handle ContextA and other ContextB, so they should be able to access to the methods of ConcreteA and B. – gcswoosh Mar 13 '15 at 18:04
  • @Gabrielecswoosh: Are you talking about covariant return types? I don't think you need them in your example. Just have all methods return `std::unique_ptr`. As I said, I don't see the problem. As long as you don't need `dynamic_cast`, everything should be pretty straight-forward, unless I completely misunderstand the problem. – Christian Hackl Mar 13 '15 at 18:07
  • Ok thanks, I will re-think the architecture! Actually I'm using an Abstract Factory pattern. And in this case probably the dynamic_cast is the only way – gcswoosh Mar 13 '15 at 18:19
  • @Gabrielecswoosh: Refactoring may be a good idea, but for reference, have a look at http://stackoverflow.com/questions/21174593/downcasting-unique-ptrbase-to-unique-ptrderived. It discusses the question of how to cast a `std::unique_ptr`. – Christian Hackl Mar 13 '15 at 18:28