Polymorphism, Clone Pattern, Confusion++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <iostream>

template<typename T>
struct Cloneable
{
	virtual T *Clone() const
	{
		return new T(*dynamic_cast<const T*>(this));
	}
protected:
	Cloneable(){}
	Cloneable(const Cloneable &){}
	virtual Cloneable &operator=(const Cloneable &){ return*this; }
	virtual ~Cloneable(){}
};

struct A : Cloneable<A>
{
	A() : x(1) {}
	A(const A &f) : x(f.x) {}
	A(int v) : x(v) {}
	virtual A &operator=(const A &f)
	{
		x = f.x;
		return*this;
	}

	virtual void Print() const
	{
		std::cout << "x=" << x << std::endl;
	}

	virtual ~A(){}
private:
	int x;
};

struct B : A, Cloneable<B>
{
	B() : q(2) {}
	B(const B &f) : A(f), q(f.q) {}
	B(int v) : A(v-1), q(v) {}
	virtual B &operator=(const B &f)
	{
		A::operator=(f);
		q = f.q;
		return*this;
	}
	virtual A &operator=(const A &f)
	{
		A::operator=(f);
		const B *b = dynamic_cast<const B*>(&f);
		if(b)
		{
			q = b->q;
		}
		return*this;
	}

	using Cloneable<B>::Clone;
	virtual void Print() const
	{
		std::cout << "q=" << q << ", ";
		A::Print();
	}

	~B(){}
private:
	int q;
};

int main()
{
	B b1 (7), b2, *b3 (0);
	A &a1 (b1), &a2 (b2), *a3 (0);

	b1.Print();
	b2.Print();

	a1.Print();
	a2.Print();

	b3 = b1.Clone();
	b3->Print();
	delete b3;

	a3 = a1.Clone();
	a3->Print();
	delete a3;

	b2 = b1;
	b2.Print();

	a3 = new B(9);
	a2 = *a3;
	a2.Print();
	b2.Print();
}












































































q=7, x=6
q=2, x=1

q=7, x=6
q=2, x=1


q=7, x=6



x=6



q=7, x=6



q=9, x=8
q=9, x=8


Am I doing everything 'correctly'? This is as confusing as it gets. I'm thinking of abandoning the whole Cloneable<> class and just doing it manually...what does everyone here think?

I am also concerned about the operator= stuff. I didn't make a class C, but if I did, would it be correct to do it the same as in B but call B::operator= instead of A's?

And by the way, I am aware of the memory leaks in main - I am more concerned about the rest of the code.
Last edited on
I think the base class has to be an abstract class ( you can do this by making the destructor pure virtual but providing a body ouside the class declaration) - otherwise someone maybe tempted to create a variable of the base class which wouldn't really make sense.

[[Although I did notice later that you made the constructors protected...]]

Last edited on
Oops, I did have a pure virtual member function at one point but I removed it.

http://ideone.com/sOWLk - This is what I was expecting to work just fine. Apparently one base class can't implement pure virtual member functions of another base class. Any ideas?

By the way, making the destructor pure virtual is a bad idea because the base class needs to have data members that it can destruct ;)
You seem to be wanting to automate the override of Clone(), which I don't believe you can do.


By the way, making the destructor pure virtual is a bad idea because the base class needs to have data members that it can destruct ;)


http://www.gotw.ca/gotw/031.htm
Polymorphic T* clone( const T* ) ; with automated override of cloneable::clone() via CRTP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <typeinfo>
#include <type_traits>

struct cloneable
{
    virtual ~cloneable() {}
    virtual cloneable* clone() const = 0 ;
};

template< typename T > struct cloneable_copy_construct_impl : cloneable
{
    virtual cloneable* clone() const override
    {
        std::cout << "cloneable_copy_construct_impl::clone of "
                  << typeid(T).name() << '\n' ;
        return new T( *dynamic_cast<const T*>(this) ) ;
    }
};

struct A : cloneable_copy_construct_impl<A> {} ; // synthesizerd clone implementation

struct B // not cloneable
{
    B() = default ;
    B( const B& ) { std::cout << "B::copy_constructor\n" ; }
} ;

struct C : cloneable // implements its own clone
{
   C* clone() const override { std::cout << "C::clone\n" ; return new C() ; }
};

template< typename T >
typename std::enable_if< std::is_base_of<cloneable,T>::value, T* >::type
  clone( const T* p ) { return p ? dynamic_cast<T*>( p->clone() ) : nullptr ; }

template< typename T >
typename std::enable_if< !std::is_base_of<cloneable,T>::value, T* >::type
 clone( const T* p ) { return p ? new T(*p) : nullptr ; }

int main()
{
    A* p1 = new A() ;
    A* p2 = clone(p1) ;

    B* p3 = new B() ;
    B* p4 = clone(p3) ;

    C* p5 = new C() ;
    C* p6 = clone(p5) ;

    double* p7 = new double(234.5678) ;
    double* p8 = clone(p7) ;
}
Last edited on
I think you might be screwed here by making A abstract - it throws a spanner in the whole
Clone function in the Cloneable base class.
> I think you might be screwed here by making A abstract

Think again.

What is the direct base class of A?
Last edited on
My reply was somewhat out of sync - I was replying to the original post by LB
Well, I read about using the templated clone class to auto-implement the clone pattern from some wiki somewhere. It said it worked in any case but obviously not abstract.

I'll just stick to the manual implementing style, this is unnecessary for such a simple copypasteedit problem.
Topic archived. No new replies allowed.