c++objectoopc++17superclass

How to call operator= or destructor of superclass?


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.


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.

    1. It is not necessary that any of your operator=() be virtual.

    2. It would be better to not declare or define B::operator=() at all. That allows B::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-generated operator=() 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) + 1];
        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 exception 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 (such as 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.