0

I have a case where I have a container of pointers to a base class and some of those pointers actually point to objects of a derived class. I need to create copies of each of the objects in the container without slicing the derived "portions" of the objects off during the copying process.

A common way to do this is by implementing something like a "virtual Parent clone()" method. This requires every class in the hierarchy to implement its own clone() method using its own type. This requires that future yet-unwritten child classes implement behavior expected by the parent class, but with no way to enforce doing so. Instead, I wrote an "adapter" class that the derived classes can inherit from instead of directly inheriting from the base class. By doing this, I can enforce consistency in yet-unwritten child classes.

The adapter class (intermediate-level in the hierarchy, between parent and child) does need to have knowledge of the child class in order to call "new()" for the proper child class type. This "feels" like it somewhat violates the traditional object-oriented model by making the parent have knowledge of classes that inherit from it. However, this may be justified from a generic programming perspective. It does seem to compile and work.

I am looking for any critiques, things to watch out for, etc. Is this code 100% portable and standards-compliant? Is there a better design pattern that I could be using instead? Is this adapter useful to others for general purposes? Thanks,

Sean

#include <cstdlib>
#include <iostream>
#include <stdio.h>

//-----------------------------------------

//Don't need to forward-declare bar; just needs to be defined before first instantiation

template<typename A, typename B>
class foo
{
public:
    foo()
    {
        printf("foo::foo() : constructed a foo.\n");
    }

    virtual foo<A, B>* clone()
    {
        printf("foo::clone() : making a clone...\n");
        return new foo<A, B>;
    }

    virtual void DoSomething()
    {
        printf("foo::DoSomething() : something...\n");
    }

    A myA;
    B myB;
};

template<typename A, typename B, typename ChildClass>
class fooAdapter : public foo<A, B>
{
public:
    fooAdapter()
    {
        printf("fooAdapter::fooAdapter() : constructed a fooAdapter.\n");
    }

    foo<A, B>* clone()
    //or directly take in foo<>'s complete type rather than parametric per-parameter
    {
        return new ChildClass( *((ChildClass*) this) );
    }
};

//-----------------------------------------

class bar : public fooAdapter<int, float, bar>
{
public:
    bar()
    {
        printf("bar::bar() : constructed a bar.\n");
    }

    bar(const bar& myBar)
    {
        printf("bar::bar(bar) : copy-constructing a bar.\n");   
    }

    virtual void DoSomething()
    {
        printf("bar::DoSomething() : something.\n");
    };
    //don't need to include clone() in bar
};

//-----------------------------------------

int main(int argc, char *argv[])
{
    printf("About to construct myBar.\n");
    bar myBar;
    myBar.myA = 2;
    myBar.myB = 1.5;

    printf("\nAbout to clone myBar to fooTest.\n");
    foo<int, float>* fooTest = myBar.clone();
    fooTest->DoSomething();

    printf("\nAbout to construct fooTest2 from fooTest.\n");
    foo<int, float>* fooTest2 = new bar( *((bar*) fooTest) );

    return EXIT_SUCCESS;
}
Sean
  • 1
  • 1

1 Answers1

0

Stop using dumb pointers.

Write either an augmented any with a cast-to-base operation or a value ptr and a make value function that constructs the value ptr's value directly.

Both know the dynamic type of the object, and as they refuse to take pointers themselves are unlikely to be created polymorphically.

template<class Base>
struct poly_any:private std::any{
  Base* operator->(){ return to_base(*this); }
  template<class X, std::enable_if<std::is_base_of<Base, std::decay_t<X>>{}, bool> =true>
  poly_any(X&& x):
    std::any(std::forward<X>(x)),
    to_base([](std::any& self)->Base*{
      return std::any_cast<std::decay_t<X>*>(&self);
    })
  {}
  poly_any(poly_any const&)=default;
  poly_any(poly_any &&)=default;
  poly_any&operator=(poly_any const&)=default;
  poly_any&operator=(poly_any &&)=default;
private:
  using to_base_t = Base*(*)(std::any&);
  to_base_t to_base=nullptr;
};

this poly_any<Base> represents a polymorphic vallue type of any type derived from Base. It needs some work, but that is the meat of it.

We don't rewuire anyone to write clone, we use any to type erase copying and custom type erase cast-to-base.

You can still slice this, but it happens the first time you put something into the poly_any.

std::any can be written if you lack it, or you can use boost::any. There may be typos.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524