私有派生析构函数

时间:2017-05-10 11:05:03

标签: c++ oop virtual-destructor scope-resolution

当我尝试以多态方式删除派生对象时(即:基类具有公共虚拟析构函数),为什么仍然会调用派生类私有析构函数?为什么范围分辨率私有无效。

class Base
{
protected:
    Base() { cout << "Base constructor.\n"; }
public:
    virtual ~Base() { cout << "Base destructor.\n"; }
};

class Derived :public Base
{
public:
    Derived() { cout << "Derived constructor.\n"; }
private:
   ~Derived() { cout << "Derived destructor.\n"; }
};

int main()
{
    Base *p = new Derived();
    delete p;
}

输出:

Base constructor.
Derived constructor.
Derived destructor.
Base destructor.

5 个答案:

答案 0 :(得分:3)

因为析构函数是按构造函数的相反顺序调用的,所以总是会调用虚析构函数。

如果要调用虚函数,

private无关。

正如我指出的那样:

Why would a virtual function be private?

ISO C ++ 1998标准版明确指出:

  

§10.3[...]在确定覆盖时不考虑访问控制(第11条)。

有点哲学背景:

进一步说明这是STL为iostreams所做的事情:Non-Virtual Interface的定义,即所有公共函数(除了析构函数)都是非虚拟的,所有虚函数都是protectedprivate。公共函数调用虚拟保护或私有功能。这为整个层次结构提供了一个非常明确的切入点。

答案 1 :(得分:1)

确实如此,但您没有直接致电~Derived()。如果您要使用

Derived *p = new Derived();
delete p;

然后你就会收到错误。但是当您通过多态性间接访问~Derived()时(例如通过调用~Base()),则访问说明符private不适用。

根据[class.access.virt#1]

  

虚函数的访问规则(Clause [class.access])由其声明确定,不受稍后覆盖它的函数规则的影响。 [实施例:

class B {
public:
  virtual int f();
};

class D : public B {
private:
  int f();
};

void f() {
  D d;
  B* pb = &d;
  D* pd = &d;

  pb->f();                      // OK: B​::​f() is public, D​::​f() is invoked
  pd->f();                      // error: D​::​f() is private
}
     

- 结束示例]

再次在footnote 111 in [class.virtual]

  

在确定覆盖时不考虑访问控制。

答案 2 :(得分:0)

您使用虚拟析构函数,因为您希望调用继承中的所有析构函数。这正是你得到的。私有不适用,因为你没有明确地调用析构函数。

如果您的析构函数不是虚拟的,那么只会调用<?php class Login_model extends CI_Model { function login($username, $password) { $this->db->select('id, username, password'); $this->db->from('login'); $this->db->where('username', $username); $this->db->where('password', $password); $this->db->limit(1); $query = $this->db->get(); if($query->num_row()==1) { return $query->result(); } else { return false; } } } ?> 。通常,当你有多态时,这不是你想要的。

答案 3 :(得分:0)

你可以通过指向B的指针来调用析构函数,因为它已经在那里公开了。它是指针的静态类型,在这种情况下用于确定访问。

[class.access.virt/1]

  

虚函数的访问规则(Clause [class.access])是   由其声明确定,不受a规则的影响   稍后覆盖它的函数。 [实施例:

class B {
public:
  virtual int f();
};

class D : public B {
private:
  int f();
};

void f() {
  D d;
  B* pb = &d;
  D* pd = &d;

  pb->f();                      // OK: B​::​f() is public, D​::​f() is invoked
  pd->f();                      // error: D​::​f() is private
}
     

- 结束示例]

这使虚拟功能与Liskov Substitution Principle

保持一致

答案 4 :(得分:0)

您可以通过基类指针删除派生类。在虚拟析构函数的帮助下,您可以在派生类中启动删除。因为该类可以访问其私有成员,所以它会调用私有析构函数。之后,基类调用公共析构函数。

请注意,您调用删除而不是直接调用de destructor Derived :: ~Derived()!