Polymorphism and operator=

I'm very troubled by the behavior of calling operator= with polymorphism. Very unfortunately, it follows the same pattern as all other virtual functions...
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
#include <iostream>

#define DB(a) std::cout << a << std::endl
#define Pause std::cin.sync(); std::cin.ignore()

class Enemy
{
protected:
	unsigned int health;
	signed int x, y;
public:
	Enemy() : health(1), x(0), y(0) {DB(1);}
        Enemy(const Enemy &from) : health(from.health), x(from.x), y(from.y) {DB(2);}
	virtual Enemy &operator=(const Enemy &from)
	{DB(3);
		health = from.health;
		x = from.x;
		y = from.y;

		return(*this);
	}

	unsigned int Health() const
	{
		return(health);
	}
	signed int X() const
	{
		return(x);
	}
	signed int Y() const
	{
		return(y);
	}
	virtual void Attack() = 0;
	virtual void Damage(unsigned int amount)
	{
		health = (amount <= health) ? (health - amount) : 0;
	}
	virtual ~Enemy(){DB(4);};
};

class Goomba: public Enemy
{
	bool direction;
public:
	Goomba() : direction(false) {DB(5);}
	Goomba(const Goomba &from) : direction(from.direction) {DB(6);}
	virtual Goomba &operator=(const Goomba &from)
	{DB(7);
		direction = from.direction;

		this->Enemy::operator=(from);

		return(*this);
	}

	virtual void Attack(){}

	virtual ~Goomba(){DB(8);}
};

int main()
{
	Enemy &e1 = Goomba(); Pause;
	Enemy &e2 = Goomba(); Pause;

	Goomba a; Pause;
	Goomba b; Pause;

	e1 = a; Pause;
	b = a; Pause;
}

struct end{~end(){Pause;}}e;

1
5

1
5

1
5

1
5

3

7
3

8
4
8
4
8
4
8
4


I solved it partially on line 53, but what about the fourth output? How can this be fixed or avoided? I was originally under the impression that the operator= function got special treatment like constructors and destructors, but now I am left with dozens of memory leaks/corruptions...is it a design flaw that I need to do this at all? How can I make sure that all operator= functions along the inheritance tree are called in these cases?
Last edited on
I will have to check tomorrow @ work, but I am very troubled by this too!! I was under the same impression as you. These are my classes for my own test:

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
template<class T>
void Display(const T &val)
{
    std::cout << val << std::endl;
}

class Base
{
public:
    Base() { Display("Base:  Default constructor."); }
    virtual ~Base() { Display("Base:  Destructor."); }
    virtual Base& operator=(const Base& op2)
    {
        Display("Base:  Operator =().");
        return *this;
    }
};

class Derived : public Base
{
public:
    Derived() : Base() { Display("Derived:  Default constructor."); }
    virtual ~Derived() { Display("Derived:  Destructor."); }
	virtual Base& operator=(const Base &op2)
	{
		Display("Derived:  Fix for Operator =().");
		//With this "solution" we are eternally bound to RTTI.
		operator=(dynamic_cast<const Derived&>(op2));
		return *this;
	}
    Derived& operator=(const Derived& op2)
    {
        Base::operator=(op2);
        Display("Derived:  Operator =().");
        return *this;
    }
};


As you can see, it sucks. The only way to ensure proper operation is to use a dynamic_cast<>() in the override, which bounds this method to always depend on RTTI, meaning no optimization by removing RTTI ever again. Plus it is a lot of effort: Override the base operator, then create the new operator, and make sure the base implementation is called.
virtual Base& operator=(const Base &op2)

Are you sure you can't use this as a Derived* in here? Sure the function is taking and returning Base&, but it's part of Derived here, isn't it? (I haven't checked this though so I could be wrong).
The code running as operand 1 (or left-hand side operand) is for sure of type Derived. The problem comes with operand 2 (or right-hand side operand). What guarantees me that it is of type Derived too? The only safe way out I could see was a dynamic_cast. :-(

Honestly, I'm in shock!
The problem is, if I have two Base references to derived types, and I do BaseRef1 = BaseRef2, how can I make all operator= fire down the inheritance tree based on the derived type referenced?
closed account (DSLq5Di1)
Scott Meyers More Effective C++ addresses this problem, see:-
http://ptgmedia.pearsoncmg.com/images/020163371x/items/item33.html
Is sloppy9 my hero? I guess I'm one read away to find out... :-) Sounds promising.
Ah I see now. It seems you want to be able to do:
1
2
3
Base& ref1 = Derv1;
Base& ref2 = Derv2;
ref1 = ref2; //works like Derv1 = Derv2 


I can't really see a way to make that work since you would only know one of the reference types from dynamic binding...
Well, I read the post, sloppy9. It is very enlightening, no doubt. But it is also an eye-opener. I just can't shake the feeling that this is the first-ever BUG in C++. Probably most of you disagree in calling it a bug, and it is not that I am categorically stating this is a BUG, but it sure feels like it.

I understood the reasoning of using non-leaf abstract classes for sure, but that may very well be super impractical in large object hierarchies, and I really don't see me repeating blocks of member variables just to keep bases data-less. I know, there are ways (use a struct, leave fields in base but assign in concrete, etc.).

Sooo, there. I feel beaten. :-(
Yeah, I need all the data in the entire inheritance tree to be copied, between the uppermost base classes (including multiple inheritance) to the fully derived type.

@Sloppy9: Thanks! I'll be using the dynamic_cast method, as as firedraco said, I would like to be able to assign through references/pointers.

Thanks everyone!
My feeling is that operator=() isn't really appropriate for polymorphic entities. They are by definition different types so it doesn't make sense to make one subtype equal to another subtype. They are by definition not equal, therefore we need more of a cast than an assignment.

So if I have:

Base<-Der1;

Base<-Der2;

Base& b1 = Der1;
Base& b2 = Der2;

Then doing b1 = b2 doesn't really make sense. What might make more sense is:

Der2& d2 = (Der2) b1; // Need to know the types and do proper conversion

The only time the assignment makes sense is when b1 and b2 refer to the same derived type. But the whole point of polymorphism is that the programmer doesn't need to know the derived type.

So my feeling is just don't do this.

Hi , all i sorry for my lack of knowledge but i am not able to understand the problem .. compleatly .. i will be thinking over it ..
thanks all of you .. .
Agree with Galik, operator= is not appropriate for polymorphic types.

IMHO smart pointers solve a lot of the problems. I have something like

1
2
3
4
5
// shorten name
typedef boost::shared_ptr<Animal> AnimalPtr;

// create object - stuff bare pointer into management straight away
AnimalPtr mywombat(new Wombat());


Now you can generate fling around mywombat, assign it to other AnimalPtr's etc

This implements reference semantics, i.e.

 
AnimalPtr w = mywombat;


means that both mywombat and w point to the same Wombat

If you need to make deep copies of polymorphic objects than you need a clone() function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal
{
...
public:
  virtual AnimalPtr clone() const = 0;
};

class Wombat : public Animal
{
...
public:
  AnimalPtr clone() const
    { return AnimalPtr(new Wombat(*this)); }
};
 


Last edited on
I've used the clone pattern before and it doesn't work for my rather complex and vexing situation.

I'll probably redesign if I keep running into problems like this - I think I started on the wrong foot anyway.
^Actually since Enemy is an abstract class you don't need to be able to assign to Derived from Base anyway. Just like the link said.
I am with you now .. thanks Sloppy for the artical thanks all of you for conversation ..
Topic archived. No new replies allowed.