liveBook · Manning

Superclass

As explained in 100 C++ Mistakes and How to Avoid Them

A base class is a C++ class from which another class inherits members and behavior.

A superclass, also called a base class, is a class from which a derived class inherits members and behavior. An object of the derived class combines the data parts of both classes, so operations must maintain every part of the hierarchy. section 8.7

Treat a superclass as part of the derived object's correctness boundary:

  • Call the superclass assignment operator when implementing assignment in a derived class; otherwise, private superclass data is left unchanged. section 8.7
  • Give a polymorphic superclass a virtual destructor so deleting a derived object through a superclass pointer destroys both parts. section 8.8
  • Design a superclass with its future derived classes in mind, because errors higher in the hierarchy affect every subtype. section 8.8

Assignment that silently preserves the wrong name

A Student derives from Person. If Student::operator= copies only gpa, assigning Thelma to Louise produces two students named Thelma and leaves Louise's inherited name unchanged. Calling Person::operator=(o) before copying gpa updates both class parts. section 8.7

Listing 8.13 A derived class copying its data but disregarding its base class
class Person {
private:
    std::string name;
public:
    Person(const std::string& name) : name(name) {}
    const std::string& getName() { return name; }
};
 
class Student : public Person {
private:
    double gpa;
public:
    Student(const std::string& name, double gpa) : Person(name), gpa(gpa) {}
    Student& operator=(const Student& o) {
        if (this == &o)
            return *this;
        gpa = o.gpa;                                         #1
        return *this;
    }
    double getGpa() const { return gpa; }
};
 
int main() {
    Student thelma("Thelma", 3.85);
    Student louise("Louise", 3.75);
 
    louise = thelma;
    std::cout << thelma.getName() << ' ' << thelma.getGpa() << '\n';
    std::cout << louise.getName() << ' ' << louise.getGpa() << '\n';
}

Inspect the faulty derived assignment operator and its output before comparing it with the corrected superclass assignment. section 8.7

Listing 8.14 The derived class properly considering its base class
class Person {
private:
    std::string name;
public:
    Person(const std::string& name) : name(name) {}
    const std::string& getName() { return name; }
};
 
class Student : public Person {
private:
    double gpa;
public:
    Student(const std::string& name, double gpa) : Person(name), gpa(gpa) {}
    Student& operator=(const Student& o) {
        if (this == &o)
            return *this;
        Person::operator=(o);                                #1
        gpa = o.gpa;                                         #2
        return *this;
    }
    double getGpa() const { return gpa; }
};
 
int main() {
    Student thelma("Thelma", 3.85);
    Student louise("Louise", 3.75);
 
    louise = thelma;
    std::cout << thelma.getName() << ' ' << thelma.getGpa() << '\n';
    std::cout << louise.getName() << ' ' << louise.getGpa() << '\n';
}

Compare the corrected assignment operator, which explicitly delegates inherited state to Person before copying Student state. section 8.7

Polymorphic deletion

Shape is a superclass of Square and declares virtual area(). Deleting a Square through Shape* is undefined when Shape lacks a virtual destructor; making the destructor virtual produces ~Square followed by ~Shape. section 8.8

Listing 8.15 Base class without a virtual destructor
class Point {
private:
    double x;
    double y;
public:
    Point(double x, double y) : x(x), y(y) {}
};
 
class Shape {
private:
    Point* location;
public:
    Shape(double x, double y) : location(new Point(x, y)) {}
    ~Shape() { delete location;  std::cout << "~Shape\n"; }    #1
    virtual double area() const = 0;                           #2
};
 
class Square : public Shape {
private:
    double* side;
public:
    Square(double side, double x, double y) : Shape(x, y), 
        side(new double(side)) {}
    ~Square() { delete side;  std::cout << "~Square\n"; }
    double area() const { return *side * *side; }  // looks weird
};
 
int main() {
    Shape* s = new Square(2.5, 0, 0);  // place at origin
    std::cout << s->area() << '\n';
    delete s;                                                  #3
    return 0;
}

Examine the failure caused by deleting a dynamically allocated Square through a Shape pointer without a virtual superclass destructor. section 8.8

Listing 8.16 A base class with virtual destructor
class Point {
private:
    double x;
    double y;
public:
    Point(double x, double y) : x(x), y(y) {}
};
 
class Shape {
private:
    Point* location;
public:
    Shape(double x, double y) : location(new Point(x, y)) {}
    virtual ~Shape() { delete location;                     #1
        std::cout << "~Shape\n"; }    
    virtual double area() const = 0;
};
 
class Square : public Shape {
private:
    double* side;
public:
    Square(double side, double x, double y) : Shape(x, y),
        side(new double(side)) {}
    ~Square() { delete side;  std::cout << "~Square\n"; }
    double area() const { return *side * *side; }  // still looks weird
};
 
int main() {
    Shape* s = new Square(2.5, 0, 0);  // place at origin
    std::cout << s->area() << '\n';
    delete s;
    return 0;
} 

Compare the safer Shape definition, where a virtual destructor enables derived and superclass cleanup. section 8.8

How 100 C++ Mistakes and How to Avoid Them covers it

How deep it goes
Explains composite objects, assignment delegation, virtual destructors, and deletion consequences through contrasting C++ listings.
Examples
Demonstrates Person and Student assignment, plus Shape and Square deletion through a superclass pointer.
What it assumes
Assumes basic C++ classes and pointers while introducing inheritance, polymorphism, and superclass responsibilities through examples.
What you can do after
Apply inheritance safely by delegating superclass assignment and declaring virtual destructors for polymorphic superclasses.
How it connects
Connects superclass to inheritance, derived classes, polymorphism, copy assignment, constructors, destructors, and class invariants.

Also explained in

Compare how 10 books explain superclass →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 C++ Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.