1

I am building some kind of factory method which returns a DerivedClass as a BaseClass in the following way:

BaseClass Factory() 
{
    return DerivedClass();
}

Am I right, that the move constructor of the BaseClass is used when I call that method:

BaseClass object = Factory();

Since I cannot make the move constructor of the BaseClass virtual, is there an other way to force the move constructor of the DerivedClass to be taken?

Edit1: additional information - I already thought about pointers. But the thing is, I want to export the factory function in a DLL and I wanted to make it as easy as possible for the user. Standard pointers could result in memory leaks and on the other hand not everybody is familiar with smart pointers.

Edit2: As I learnd the real question was: Does polymorphism also work with return by value?
And the answer is NO.

RomCoo
  • 1,868
  • 2
  • 23
  • 36
  • 1
    Most of these problems go away if you were to `new DerivedClass` and return it as as a `BaseClass*`. It's near impossible to do class inheritance any other way. – selbie Apr 13 '19 at 22:21

3 Answers3

6

I think you souldn't do:

BaseClass Factory() 

But rather

// You'll need to delete
BaseClass* Factory() {
    return new Derived();
}

// You will not need to delete
unique_ptr<BaseClass> Factory() {
    // return new Derived();
    // As the comments point out: prefer use of make_unique
    return std::make_unique<Derived>();
}

Otherwise, you're slicing your object.

Polymorphism works only in pointers and references.

Mirko
  • 1,043
  • 6
  • 12
  • 2
    I would argue to make `std::make_unique` the default in the answer. There's no downside to it. – Quimby Apr 13 '19 at 22:32
  • so it is not possible with return by value? – RomCoo Apr 13 '19 at 22:39
  • @RomCoo What should be a result of that? `BaseClass` is not a `DerivedClass`. Where should the extra member variable be stored? C++ doesn't hide the value and reference types like java or C#. If you want values, write `T x;` If you want reference semantics, write `T* ptr;` and manage the memory or use `std::` to do it for you., – Quimby Apr 13 '19 at 22:52
  • @RomCoo C++ doesn't make you pay for what you don't use. The size of the object is statically determined by the compiler. When you return by value, it will copy sizeof(Base), not knowing what kind what sizeof(Derived) is. When you return a pointer, it will not touch the actual object, but transfer the pointer. – Mirko Apr 13 '19 at 23:00
  • @Quimby it should if you can. I use it whenever I can, but I'm used to avoiding it because I compile a lot of legacy code (C++98). I don't know how many people has this problem (for better of for worse, new should work always). – Mirko Apr 13 '19 at 23:02
2

In C++, you don't return a derived object as a base class object. That looks like a c# way. You should use pointers.

I am assuming this is something you want.

BaseClass* Factory() 
{
    return new DerivedClass();
}

Also I never heard about the concept of 'move constructor'.

Tony
  • 632
  • 1
  • 4
  • 18
  • 1
    [Move constructors](https://en.cppreference.com/w/cpp/language/move_constructor) have been an integral part of C++ object semantics since C++11. – Nick Mertin Apr 13 '19 at 22:25
  • Wow.... Learned new concept. Just learned that C++ has separate such scenario from copy constructor. – Tony Apr 13 '19 at 22:34
1

A plain variable, field, return type, etc. of a base class in C++ cannot contain values of a subclass. This is different from other object-oriented languages such as Java, and it has to do with how the objects are actually represented in memory - storing a polymorphic value requires a pointer or reference. (In Java and similar languages, all object-containing variables are actually pointers to the object which itself is stored elsewhere, so this implementation detail is hidden from the programmer.)

The solution is to use a pointer to allow you to use polymorphism. The naive version of this is to make the function return BaseClass *, and change the return expression to new DerivedClass(), however this leaves you easily open to memory leak bugs, as you must manually ensure that the object is destroyed by the caller when it is no longer needed (via the delete operator). A pereferable solution is to use std::shared_ptr<BaseClass> or std::unique<BaseClass> as the return type, which will ensure that the object is destroyed when a reference to it no longer exists.

Nick Mertin
  • 1,149
  • 12
  • 27