2

I need to copy an object of a polymorphic class having a base pointer. I know that I can implement a virtual method for this. But what if the base class should not be abstract? Leaving the method without pure-specifier can lead to run-time bugs, if you forget reimplement it in the derived. It's uncomfortable. What is the best way to handle this?

Oleksa
  • 635
  • 5
  • 15
  • I think maybe there must be a feature that forces you to implement a method even if it's not pure. – Oleksa Jul 03 '20 at 05:44
  • Make it a pure function. then create another class declared final that inherits and implants that function. – Mellester Jul 03 '20 at 05:46
  • the virtual clone method is the best practitce then. – Mellester Jul 03 '20 at 05:51
  • @Mellester You're aware that the class should not be abstract, yes? If so, can you provide an example? – Oleksa Jul 03 '20 at 05:52
  • What's wrong with making it pure? – florestan Jul 03 '20 at 05:53
  • @florestan I must be able to instantiate the base class. – Oleksa Jul 03 '20 at 05:55
  • So my suggestion is to derive for a abstract base class and make all instantaneous classes final. However giving your question you are aware that you can declare a clone method virtual and give a implementation of that clone method for your base class and leave it up to the derived objects to implement there clone method. You might be able to static_assert yourself to safety – Mellester Jul 03 '20 at 05:59
  • Why do you have to be able to instanciate the base class exactly? It sounds like you should have a `ICopyable` interface with a single pure virtual member function, `copy`. – Kaldrr Jul 03 '20 at 06:27
  • 1
    What is the problem with just implementing the virtual copy function in your base class as well as your derived classes? – super Jul 03 '20 at 06:44
  • _"Leaving the method without pure-specifier can lead to run-time bugs, if you forget reimplement it in the derived."_ — Sorry, but this is extremely weak and silly reasoning. Simply make sure that it is reimplemented. – Daniel Langr Jul 03 '20 at 07:02
  • _"I think maybe there must be a feature that forces you to implement a method even if it's not pure."_ — What made you think so? See, e.g., [this answer](https://stackoverflow.com/a/23364632/580083). Also, [this another answer](https://stackoverflow.com/a/9478895/580083) might be relevant to your problem. – Daniel Langr Jul 03 '20 at 07:06
  • Does this answer your question? [Force all classes to implement / override a 'pure virtual' method in multi-level inheritance hierarchy](https://stackoverflow.com/questions/9477581/force-all-classes-to-implement-override-a-pure-virtual-method-in-multi-level). Especially, see the answer of @JamesKanze. – Daniel Langr Jul 03 '20 at 07:08

2 Answers2

2

There are good reasons why you should never want to instantiate a base class. If you do need to make a empty final class use the following.

class IBase 
{
    virtual void SharedCode()
    {
        1 + 1;
        /// code here
    };
    virtual void AbstractDecalration() = 0;
};

class Base final: IBase
{
     void AbstractDecalration() override;
};

Base b{};

All Future Derived classes will be able to use the SharedCode of IBase and you will have a Instantiated class of Base that is final. This is for future proofing your code base.

However I realize that is not the question you asked so here is a implementation were I use a simple check to the vtable pointer of the class to see if I have the correct class.

This is a runtime check and doesn't work across libraries use dynamic_assert if that is the case.

#include <memory>
#include <type_traits>
#include <assert.h>
class Base {
    public:
       auto clone() const
   {
      return std::unique_ptr<Base>(this->clone_impl());
   }
    private:
     virtual Base* clone_impl() const
     {
         Base b{};
         int* bVtablePtr = (int*)((int*)&b)[0];
         int* thisVtablePtr = (int*)((int*)this)[0];
         assert(bVtablePtr == thisVtablePtr);
        return new Base(*this);
    }
};

class Derived : public Base 
{
       auto clone() const
   {
      return std::unique_ptr<Derived>(this->clone_impl());
   }
    virtual Derived* clone_impl() const
    {
        return new Derived();
    }
};
class Falty : public Base{};

    int  main(){
        std::unique_ptr<Derived> good(new Derived());
        std::unique_ptr<Falty> falty(new Falty());
        good->clone(); // oke
        falty->clone(); // this function asserts at runtime
        
}

Note the private clone_impl and public unique_ptr retuning clone method. Very usefull to prevent memory leaks in your code

Mellester
  • 922
  • 7
  • 9
1

You can achieve what you want by introducing another abstract base class plus using CRPT for clone function. Then, clone will be automatically implemented in all derived classes "for free" (without manual retyping). Example:

struct Abstract
{
  virtual ~Abstract() {}
  virtual Abstract* clone() const = 0;
  virtual void say() const = 0;
};

template <typename B, typename D>
struct AbstractCloneable : B
{
  virtual B* clone() const override
  {
    return new D(static_cast<const D&>(*this));
  }
};

// original base class
struct Base : AbstractCloneable<Abstract, Base>
{
  virtual void say() const override
  {
    std::cout << "Base" << std::endl;
  }
};

// original derived class #1
struct Derived1 : AbstractCloneable<Base, Derived1>
{
  virtual void say() const override
  {
    std::cout << "Derived1" << std::endl;
  }
};

And a test program:

int main()
{
  std::unique_ptr<Abstract> ptr1 = std::make_unique<Base>();
  ptr1->say();
  std::unique_ptr<Abstract> ptr1_copy{ ptr1->clone() };
  ptr1_copy->say();

  std::unique_ptr<Abstract> ptr2 = std::make_unique<Derived1>();
  ptr2->say();
  std::unique_ptr<Abstract> ptr2_copy{ ptr2->clone() };
  ptr2_copy->say();
}

Which outputs:

Base
Base
Derived1
Derived1

Live demo: https://godbolt.org/z/3FeSTd


See this article for more details and explanations: C++: Polymorphic cloning and the CRTP (Curiously Recurring Template Pattern).

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • I like how this works all the way done the inheritance chain. is it possible to use private or protected to prevent someone from inheriting form the derived class unless the use this template. – Mellester Jul 03 '20 at 07:56
  • @Mellester Not sure at this moment and I don't have time right now to dig deeper. But this may be an interesting standalone question (if not been posted already). – Daniel Langr Jul 03 '20 at 08:13