I'm currently investigating the interplay between polymorphic types and assignment operations. My main concern is whether or not someone might try assigning the value of a base class to an object of a derived class, which would cause problems.
From this answer I learned that the assignment operator of the base class is always hidden by the implicitely defined assignment operator of the derived class. So for assignment to a simple variable, incorrect types will cause compiler errors. However, this is not true if the assignment occurs via a reference:
class A { public: int a; };
class B : public A { public: int b; };
int main() {
A a; a.a = 1;
B b; b.a = 2; b.b = 3;
// b = a; // good: won't compile
A& c = b;
c = a; // bad: inconcistent assignment
return b.a*10 + b.b; // returns 13
}
This form of assignment would likely lead to inconcistent object state, however there is no compiler warning and the code looks non-evil to me at first glance.
Is there any established idiom to detect such issues?
I guess I only can hope for run-time detection, throwing an exception if I find such an invalid assignment. The best approach I can think of just now is a user-defined assigment operator in the base class, which uses run-time type information to ensure that this
is actually a pointer to an instance of base, not to a derived class, and then does a manual member-by-member copy. This sounds like a lot of overhead, and severely impact code readability. Is there something easier?
Edit: Since the applicability of some approaches seems to depend on what I want to do, here are some details.
I have two mathematical concepts, say ring and field. Every field is a ring, but not conversely. There are several implementations for each, and they share common base classes, namely AbstractRing
and AbstractField
, the latter derived from the former. Now I try to implement easy-to-write by-reference semantics based on std::shared_ptr
. So my Ring
class contains a std::shared_ptr<AbstractRing>
holding its implementation, and a bunch of methods forwarding to that. I'd like to write Field
as inheriting from Ring
so I don't have to repeat those methods. The methods specific to a field would simply cast the pointer to AbstractField
, and I'd like to do that cast statically. I can ensure that the pointer is actually an AbstractField
at construction, but I'm worried that someone will assign a Ring
to a Ring&
which is actually a Field
, thus breaking my assumed invariant about the contained shared pointer.
Field f
, it would feel natural to writef.getZeroElement()
but unnatural to writef.ring.getZeroElement()
. Since math is hard enough to read without taking peculiarities of the programming language into account, I'd try very hard to accommodate the former syntax, but not having to implementgetZeroElement() { return this->ring.getZeroElement(); }
as a method of Field would be good. – Barnard