I have this A class with a heap member ‘a’:
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’
>Solution :
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 As members. When B and C are destructed, As 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.