I have this A class with a heap member ‘name’:
class A {
protected:
char *name;
public:
// Constructor
A() {
name = new char[10];
strcpy(name, "Undefined");
}
// Assignment Operator
virtual A &operator=(const A &rhs) {
if (this == &rhs) {
return *this;
}
delete [] name;
name = new char[10];
strcpy(name, rhs.name);
return *this;
}
// Destructor
virtual ~A() {
delete [] name;
}
};
I have the subclass B with additional variable ‘b’:
class B : public A {
int b;
// Constructor
B():A(),b(0){}
// Assignment Operator
virtual B& operator=(const B& rhs) {
if (this == &rhs) {
return *this;
}
delete [] name;
name = new char[10];
strcpy(name, rhs.name);
b = rhs.b;
return *this;
}
// Destructor
virtual ~B() {
delete [] name;
}
};
I have the subclass C with additional member ‘char* name’:
class C : public A {
int* c;
// Constructor
C():A() {
c = new int[10];
for(int i = 0 ; i < 10 ; i++) {
c[i] = 0;
}
}
// Assignment Operator
virtual C& operator=(const C& rhs) {
if (this == &rhs) {
return *this;
}
delete [] name;
name = new char[10];
strcpy(name, rhs.name);
delete [] c;
c = new int[10];
for (int i = 0 ; i < 10 ; i++) {
c[i] = rhs.c[i];
}
return *this;
}
// Destructor
virtual ~C() {
delete [] name;
delete [] c;
}
};
I wonder if this is the correct way to implement operator= and destructors for B and C.
Is there any way to call A’s operator= or destructor in B or C, so I don’t write assignment for all members again and again.
- A is base class with a heap variable
- B is a class derived from A, with additional ‘int b’
- C is a class derived from A, with additional ‘char* name’
11
To respond to the question as asked, yes, a derived class operator=()
can call a base class operator=()
. For example;
B& operator=(const B& rhs) {
A::operator=(rhs);
b = rhs.b;
return *this;
}
(and similarly for C
).
However, this isn’t a particularly good approach. In the following, I’m going to address some concerns with your approach that you did NOT ask about.
-
It is not necessary that any of your
operator=()
be virtual. -
It would be better to not declare or define
B::operator=()
at all. Doing that allowedB::operator=()
to be implicitly generated (e.g. by your compiler) and the implicitly generated version calls the base class version, and copy/assigns all member by value. The only reason to roll your own is if the implicitly-generatedoperator=()
is not suitable for your derived class.
Additionally, it is not necessary to do the if (this == &rhs)
in A::operator=()
. Instead, it can be defined as
A &operator=(const A &rhs) {
char *newname = new char [std::strlen(rhs.name)];
strcpy(newname, rhs.name);
delete [] name;
name = newname;
return *this;
}
Apart from avoiding the this == &rhs
test (which will produce different behaviour if A
has an operator&()
), this approach is also except safe – if the new
expression throws, the object is unchanged.
The only downside of this is that, temporarily, there is a minor additional memory usage (name
, rhs.name
, and newname
all consume memory, before name
is released and reassigned).
Using this approach, C::operator=()
would be implemented to call A::operator=()
and to allocate/copy/release c
.
Furthermore, the destructors of B
and C
should generally not release A
s members. When B
and C
are destructed, A
s constructor is called implicitly (so your approach will result in A::name
being destroyed twice, which results in undefined behaviour.
Lastly, you can use standard containers (std::string
, or std::vector<char>
, and avoid the need for manual memory allocation (new
and delete
) entirely. Look up “rule of zero C++” for more information.
1