虚拟析构函数不在Base和Derived Class中调用

时间:2014-12-27 15:02:02

标签: c++ inheritance c++11 destructor virtual-functions

以下代码涉及4个类。

  1. 基类是Person Class,有两个派生类Student和Lecturer。每个人都支持两个函数:toString()和type()。 Type()返回该类的名称,而toString()则打印实例的信息(Student或Lecturer)。

  2. Person是一个抽象类,但Student和Lecturer都是具体的类。

  3. 我已实现上述两项功能。

    1. 许多讲师将分享指向同一SalaryTable的指针,可能有其他SalaryTables(支持相同的功能),并且讲师不拥有SalaryTable。此外,他们每个人都有一些额外的功能。
    2. 任何人都可以单独解释第3点吗?据我所知,我使用了Lecturer构造函数中给出的SalaryTable指针,并将其分配给我在Lecturer.h中添加的SalaryTable指针(salaryTable_)。然后我使用salaryTable _-> annualSalary(grade_)返回薪水。在~Lectuere()的析构函数中,我删除了salaryTable _。

      这是正确的方法吗?当我这样做时,只调用~Salary()析构函数,并且不调用Base类析构函数(~Person())和派生类析构函数(~School()& ~Lecturer())。谁能解释一下我在哪里错了?

      的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_ */
      

      注意:请注意,我无法更改.h文件。

      我因摧毁指针而收到很多意见。但我的终极问题是:为什么除了SalaryTable类之外的所有其他类都没有被销毁。我通过在所有类的析构函数中打印stmt来验证它。任何人都可以对此有所了解。

      &#34;还添加了main.cpp文件,我也无法对其进行修改。&#34;

4 个答案:

答案 0 :(得分:0)

  

任何人都可以单独解释第3点吗?据我所知,我使用了Lecturer构造函数中给出的SalaryTable指针,并将其分配给我在Lecturer.h中添加的SalaryTable指针(salaryTable_)。然后我使用salaryTable _-&gt; annualSalary(grade_)返回薪水。在~Lectuere()的析构函数中,我删除了salaryTable _。

您的方法存在问题:第三个主题指出讲师不拥有薪资表,并且可以在几位讲师之间共享。在这样的构造函数中删除意味着表的唯一拥有,但事实并非如此。

这可能取决于程序逻辑的其余部分,但这里最好的方法之一是制作表的共享智能指针,而不是原始指针。您的声明将更改为以下内容:

    // ...
    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_;

并且没有必要明确删除它们。


考虑您的最新编辑:如果不能更改类的接口,则必须采取其他措施来管理薪资表和人员实体。你必须确保工资表是在讲师之前创建的(我想这已经发生了),并且在讲师之后它们就被销毁了。智能指针也可以在主程序中使用,但根本不需要它们。

  

我因摧毁指针而收到很多意见。但我的终极问题是:为什么除了SalaryTable类之外的所有其他类都没有被销毁。我通过在所有类的析构函数中打印stmt来验证它。任何人都可以对此有所了解。

嗯,这只表示这些对象没有得到妥善处理,如上段所述。现在您提供了代码,我们可以确定给定的表是堆栈分配的,除了析构函数之外不需要额外的内存管理。请注意,只有在st超出范围时才会调用析构函数,因此它应该在if块的末尾被销毁。

答案 1 :(得分:0)

这不是正确的方法:薪水表是在讲师的构造函数中给你的。如果你在讲师的析构函数中删除它,那么共享同一个表的所有其他讲师都会受到影响。

正确的方法是让创建表格的来电者在没有需要的情况下将其破坏。

最好的方法是将共享工资表的原始指针替换为共享指针,然后可以自动跟踪其使用情况并在不需要长时间的情况下销毁该对象

答案 2 :(得分:0)

您必须决定谁拥有SalaryTable每个Lecturer在构造时收到指针的人。 有两种类似的选择。

(1)该表由另一个类/代码显式拥有(未在您的问题中显示),然后Lecturer应该保持观察指针(或引用)到表(const SalaryTable*constSalaryTable&或类似)从未用于delete 代码布局必须保证SalaryTable之前不会被销毁任何Lecturer观察它。

(2)您可以使用std::shared_ptr确保后者,但这需要付出一定的代价,这也意味着SalaryTable没有所有者,但是所有代码的所有部分都保留了shared_ptr它共同拥有所有权。

对我来说(1)看起来更合乎逻辑:SalaryTable是一个独立于任何Lecturer的基本对象,应该在任何Lecturer之前创建并在任何{{Lecturer之后销毁1}}。如果您没有相关经验,我还建议您避免使用shared_ptr

答案 3 :(得分:0)

SalaryTable实例被销毁,因为它是一个本地自动变量。

其他对象不会被破坏,因为它们是使用new动态分配的,没有相应的delete调用(也没有智能指针)。

就这么简单。


顺便说一句,正如其他答案中所提到的,你不应该让一个对象delete不属于它。

每位讲师都只有一个指向工资表的非拥有指针。

这意味着只要引用讲师对象存在,代码就必须确保引用的工资表存在。通常这是通过智能指针完成的。由于您无法更改标题,因此您必须更加手动确保标题。