简体   繁体   中英

Virtual Destructor Not called in Base as well as Derived Class

The following code involves 4 classes.

  1. The bases class is the Person Class and there are two Derived classes Student and Lecturer. Each person supports two functions: toString() and type(). Type() returns the name of that class whereas toString() prints the information of the instance (Student or Lecturer).

  2. Person is an abstract class but both Student and Lecturer are concrete classes.

I've implemented the above two functionalities.

  1. Many lecturers will share the pointer to the same SalaryTable, that there could be other SalaryTables (supporting the same functionality) and that the lecturers do not own the SalaryTable. Also, each one of them has some extra functions.

Can anyone explain me the 3rd point alone? As far as I know, I made use of the SalaryTable Pointer given in the Lecturer constructor and assigned it to the SalaryTable pointer (salaryTable_) which I've added in Lecturer.h. Then I return the salary using salaryTable_->annualSalary(grade_). And in the destructor of ~Lectuere(), I do a delete salaryTable_.

Is it the right way to do so? When I do it, only the ~Salary() destructor is getting called and both the Base class destructor (~Person()) and the derived class destructors (~Student() & ~Lecturer()) are not called. Can anyone explain me where am I wrong please?

main.cpp

int main(int argc, char* argv[]) {

    if (argc == 1) {
        SalaryTable st;
        Person* arr[2];
        arr[0] = new Student("Apolo",5);
        arr[1] = new Lecturer("Zeus","CO7100",33,&st);

        for (unsigned int i=0 ; i<2 ; ++i) {
            if (arr[i]->type() == "Student") {
                Student* s=dynamic_cast<Student*>(arr[i]);
                s->addMCF("blah blah");
                s->addMCF("");
                s->addMCF("Something else");
            }
        }
        for (unsigned int i=0 ; i<2 ; ++i) {
            cout << *arr[i] << endl;
        }
    }
}

SalaryTable.h

#ifndef SALARYTABLE_H_
#define SALARYTABLE_H_

class SalaryTable {
public:
    SalaryTable();
    ~SalaryTable();

    unsigned int annualSalary(unsigned int grade) const;
};

#endif /* SALARYTABLE_H_ */

Person.h

#ifndef PERSON_H_
#define PERSON_H_

#include <string>
#include <iosfwd>
#include <vector>

#include "SalaryTable.h"

using std::vector;
using std::string;

class Person {
public:
    Person() = delete;
    Person(const Person&) = delete;
    Person(Person&&) = delete;

    Person(const char* name);
    Person(const std::string& name);
    virtual ~Person();

    // Return the name of the Person
    // Should be supported by all Persons.
    std::string name() const;

    virtual std::string toString() const=0;

    virtual std::string type() const=0;

    friend std::ostream& operator<<(std::ostream&, const Person&);
private:
    std::string name_;
};

Student.h

class Student: public Person {
public:
    Student() = delete;
    Student(const Student&) = delete;
    Student(Student&&) = delete;

    Student(const char* name, unsigned int studentId);
    Student(const std::string& name, unsigned int studentId);
    virtual ~Student();

    void addMCF(const std::string&);
    std::string MCF(unsigned int);

    unsigned int id() const;

    std::string toString() const;
    std::string type() const;

private:
    unsigned int studentId_;
    vector<string> vec_;
};

Lecturer.h

class Lecturer: public Person {
public:
    Lecturer() = delete;
    Lecturer(const Lecturer&) = delete;
    Lecturer(Lecturer&&) = delete;

    Lecturer(const char* name, const char* teaches, unsigned int grade,
            SalaryTable*);
    Lecturer(const std::string& name, const std::string& teaches,
            unsigned int grade, SalaryTable*);
    virtual ~Lecturer();

    void increaseGrade();
    unsigned int salary() const;

    void changeModule(const std::string& newModule);
    std::string teaches() const;

    std::string toString() const;
    std::string type() const;

private:
    string teaches_;
    string module_;
    unsigned int grade_;
    SalaryTable& salaryTable_;

};

#endif /* PERSON_H_ */

Note: A kind note, I cannot make changes to the .h files.

I've received many opinions for destroying the pointer. But my ultimate question: Why all other classes except the SalaryTable class is not getting destroyed. I verified it by printing a stmt in the destructor of all the classes. Can anyone throw some light on it.

"Added the main.cpp file also and I cannot modify it too."

Can anyone explain me the 3rd point alone? As far as I know, I made use of the SalaryTable Pointer given in the Lecturer constructor and assigned it to the SalaryTable pointer (salaryTable_) which I've added in Lecturer.h. Then I return the salary using salaryTable_->annualSalary(grade_). And in the destructor of ~Lectuere(), I do a delete salaryTable_.

There is a problem with your approach: the third topic states that lecturers do not own the salary table, and it can be shared among several lecturers. Deleting in the constructor like that implies unique ownage of the table, which is not the case.

This may depend on the rest of the program's logic, but one of the best approaches here is to make shared smart pointers to the tables, rather than raw pointers. Your declarations would change to something like this:

    // ...
    Lecturer(const char* name, const char* teaches, unsigned int grade,
        std::shared_ptr<SalaryTable>);
    Lecturer(const std::string& name, const std::string& teaches,
        unsigned int grade, std::shared_ptr<SalaryTable>);        
    // ...
    std::shared_ptr<SalaryTable> salaryTable_;

And there would be no need to delete them explicitly.


Considering your latest edit: if the interfaces of the classes can't be changed, then other measures must be taken on managing the salary tables and person entities. You must make sure that the salary tables are created before the lecturers are (I suppose this already happens), and that they are destroyed after the lecturers are. Smart pointers could also be used in the main program, but they may not be needed at all.

I've received many opinions for destroying the pointer. But my ultimate question: Why all other classes except the SalaryTable class is not getting destroyed. I verified it by printing a stmt in the destructor of all the classes. Can anyone throw some light on it.

Well, that only means these object are not being properly handled, as proposed in the paragraph above. Now that you provided the code, we can identify that the given table is stack-allocated, which requires no additional memory management besides its destructor. Do note that the destructor will only be invoked once st goes out of scope, so it should be destroyed at the end of the if block.

This is not the right way: the salary table was given to you in the lecturer's constructor. If you delete it in the lecturer's destructor, all other lecturers sharing the same table will suffer.

The right way would be to let the caller who created the table destry it once it's no longuet needed.

The best way wiul be to replace the raw pointers to the shared salary table to shared pointers, which can then automatically keep track of its usage and destroy the object when it's no longuer needed

You must decide who owns the SalaryTable that each Lecturer receives a pointer to at construction. There are two similar options.

(1) the table is explicitly owned by another class/code (not shown in your question), then the Lecturer should keep an observing pointer (or reference) to the table (a const SalaryTable*const or SalaryTable& or similar) which is never used to delete and the code layout must guarantee that a SalaryTable is not destroyed before any Lecturer observing it.

(2) you can ensure this latter using std::shared_ptr , but this comes at some price and also means that there is no owner to the SalaryTable , but that all those parts of code which keep a shared_ptr to it jointly keep ownership.

To me (1) appears the more logical: a SalaryTable is a fundamental object that exists indepedent of any Lecturer and should be created before any Lecturer and destroyed after any Lecturer . I also suggest to avoid shared_ptr if you don't have some experience with it.

The SalaryTable instance is destroyed because it's a local automatic variable.

The other objects are not destroyed because they're allocated dynamically with new , with no corresponding delete calls (and no smart pointers to do that either).

It's that simple.


By the way, as mentioned in other answers, you should not let an object delete something that it doesn't own.

Each lecturer simply has a non-owning pointer to a salary table.

This means that the code must ensure that the referred salary table exists as long as the referring lecturer object exists. Usually this is done via smart pointers. Since you can't change the headers, you'll have to ensure it more manually.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM