Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/144.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 何时使用虚拟析构函数?_C++_Polymorphism_Shared Ptr_Virtual Destructor - Fatal编程技术网

C++ 何时使用虚拟析构函数?

C++ 何时使用虚拟析构函数?,c++,polymorphism,shared-ptr,virtual-destructor,C++,Polymorphism,Shared Ptr,Virtual Destructor,我对大多数OOP理论都有很好的理解,但有一件事让我很困惑,那就是虚拟析构函数 我认为析构函数总是被调用,不管是什么,对于链中的每个对象 什么时候要使它们成为虚拟的?为什么?只要类是多态的,就让析构函数成为虚拟的。当您可能通过指向基类的指针删除派生类的实例时,虚拟析构函数很有用: class Base { // some virtual methods }; class Derived : public Base { ~Derived() { // Do

我对大多数OOP理论都有很好的理解,但有一件事让我很困惑,那就是虚拟析构函数

我认为析构函数总是被调用,不管是什么,对于链中的每个对象


什么时候要使它们成为虚拟的?为什么?

只要类是多态的,就让析构函数成为虚拟的。

当您可能通过指向基类的指针删除派生类的实例时,虚拟析构函数很有用:

class Base 
{
    // some virtual methods
};

class Derived : public Base
{
    ~Derived()
    {
        // Do some important cleanup
    }
};
在这里,您会注意到我没有将Base的析构函数声明为
virtual
。现在,让我们看一下以下代码片段:

Base *b = new Derived();
// use b
delete b; // Here's the problem!
由于Base的析构函数不是虚拟的,并且
b
是指向派生的
对象的
Base*
delete b
具有:

[在
中删除b
],如果 要删除的对象与其动态类型(静态)不同 类型应为待处理对象的动态类型的基类 已删除且静态类型应具有虚拟析构函数或 行为未定义

在大多数实现中,对析构函数的调用将像任何非虚拟代码一样进行解析,这意味着将调用基类的析构函数,而不是派生类的析构函数,从而导致资源泄漏

总而言之,当基类的析构函数要进行多态操作时,一定要使它们成为虚拟的

如果要防止通过基类指针删除实例,可以使基类析构函数受保护且非虚拟;这样,编译器就不会让您对基类指针调用
delete


您可以在中了解有关虚拟性和虚拟基类析构函数的更多信息。

在多态基类中声明虚拟析构函数。这是Scott Meyers'中的第7项。Meyers继续总结说,如果一个类有任何虚函数,它应该有一个虚析构函数,而且,未设计为基类或未设计为多态使用的类不应声明虚拟析构函数。

还应注意,在没有虚拟析构函数时删除基类指针将导致未定义的行为。我最近学到了一些东西:


<>我已经使用C++多年了,我仍然设法挂上自己。

< P>我喜欢思考接口和接口的实现。在C++语言中,接口是纯虚拟类。析构函数是接口的一部分,应该实现。因此析构函数应该是纯虚拟的。构造函数呢?构造函数实际上不是接口的一部分,因为对象总是显式实例化的

不可能使用虚拟构造函数,但可以使用虚拟析构函数。 让我们做个实验

#include <iostream>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){
        cout << "Base Destructor called\n";
    }
};

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

int main()
{
    Base *b = new Derived1();
    delete b;
}
派生对象的构造遵循构造规则,但是当我们删除“b”指针(基指针)时,我们发现只调用了基析构函数。但这决不能发生。要做适当的事情,我们必须使基本析构函数为虚拟的。 现在,让我们看看下面发生了什么:

#include <iostream>

using namespace std;

class Base
{ 
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    virtual ~Base(){
        cout << "Base Destructor called\n";
    }
};

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

int main()
{
    Base *b = new Derived1();
    delete b;
}
因此,基指针的销毁(在派生对象上进行分配!)遵循销毁规则,即首先销毁派生对象,然后销毁基指针。
另一方面,没有什么比得上虚拟构造函数

什么是虚拟析构函数或如何使用虚拟析构函数

#include "stdafx.h"
#include<iostream>

using namespace std;
// program to convert the lower to upper orlower
class convertch
{
public:
//void convertch(){};
virtual char* convertChar() = 0;
virtual ~convertch(){}; // defined the virtual destructor

};
class MakeLower :public convertch
{
public:
MakeLower(char *passLetter)
{
tolower = true;
Letter = new char[30];
strcpy(Letter, passLetter);
}
virtual ~MakeLower()
{
cout<< "called ~MakeLower()"<<"\n";
      delete[] Letter;
}
char* convertChar()
{
size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] + 32;

}

return Letter;
}

private:
char *Letter;
bool tolower;

};
class MakeUpper : public convertch
{
public:
MakeUpper(char *passLetter)
{
Letter = new char[30];
toupper = true;
strcpy(Letter, passLetter);
}
char* convertChar()
{

size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] - 32;
}
return Letter;
}
virtual ~MakeUpper()
{
      cout<< "called ~MakeUpper()"<<"\n";
delete Letter;
}
private:
char *Letter;
bool toupper;
};


int _tmain(int argc, _TCHAR* argv[])
{

convertch *makeupper = new MakeUpper("hai");

cout<< "Eneterd : hai = " <<makeupper->convertChar()<<" \n";

delete makeupper;
convertch *makelower = new MakeLower("HAI");;
cout<<"Eneterd : HAI = " <<makelower->convertChar()<<"\n ";


delete makelower;
return 0;
}
类析构函数是一个与~前面的类同名的函数,它将重新分配该类分配的内存。为什么我们需要虚拟析构函数

#include "stdafx.h"
#include<iostream>

using namespace std;
// program to convert the lower to upper orlower
class convertch
{
public:
//void convertch(){};
virtual char* convertChar() = 0;
virtual ~convertch(){}; // defined the virtual destructor

};
class MakeLower :public convertch
{
public:
MakeLower(char *passLetter)
{
tolower = true;
Letter = new char[30];
strcpy(Letter, passLetter);
}
virtual ~MakeLower()
{
cout<< "called ~MakeLower()"<<"\n";
      delete[] Letter;
}
char* convertChar()
{
size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] + 32;

}

return Letter;
}

private:
char *Letter;
bool tolower;

};
class MakeUpper : public convertch
{
public:
MakeUpper(char *passLetter)
{
Letter = new char[30];
toupper = true;
strcpy(Letter, passLetter);
}
char* convertChar()
{

size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] - 32;
}
return Letter;
}
virtual ~MakeUpper()
{
      cout<< "called ~MakeUpper()"<<"\n";
delete Letter;
}
private:
char *Letter;
bool toupper;
};


int _tmain(int argc, _TCHAR* argv[])
{

convertch *makeupper = new MakeUpper("hai");

cout<< "Eneterd : hai = " <<makeupper->convertChar()<<" \n";

delete makeupper;
convertch *makelower = new MakeLower("HAI");;
cout<<"Eneterd : HAI = " <<makelower->convertChar()<<"\n ";


delete makelower;
return 0;
}
请参阅下面的示例和一些虚拟函数

该示例还说明如何将字母转换为大写或小写

#include "stdafx.h"
#include<iostream>
using namespace std;
// program to convert the lower to upper orlower
class convertch
{
public:
  //void convertch(){};
  virtual char* convertChar() = 0;
  ~convertch(){};
};

class MakeLower :public convertch
{
public:
  MakeLower(char *passLetter)
  {
    tolower = true;
    Letter = new char[30];
    strcpy(Letter, passLetter);
  }

  virtual ~MakeLower()
  {
    cout<< "called ~MakeLower()"<<"\n";
    delete[] Letter;
  }

  char* convertChar()
  {
    size_t len = strlen(Letter);
    for(int i= 0;i<len;i++)
      Letter[i] = Letter[i] + 32;
    return Letter;
  }

private:
  char *Letter;
  bool tolower;
};

class MakeUpper : public convertch
{
public:
  MakeUpper(char *passLetter)
  {
    Letter = new char[30];
    toupper = true;
    strcpy(Letter, passLetter);
  }

  char* convertChar()
  {   
    size_t len = strlen(Letter);
    for(int i= 0;i<len;i++)
      Letter[i] = Letter[i] - 32;
    return Letter;
  }

  virtual ~MakeUpper()
  {
    cout<< "called ~MakeUpper()"<<"\n";
    delete Letter;
  }

private:
  char *Letter;
  bool toupper;
};


int _tmain(int argc, _TCHAR* argv[])
{
  convertch *makeupper = new MakeUpper("hai"); 
  cout<< "Eneterd : hai = " <<makeupper->convertChar()<<" ";     
  delete makeupper;
  convertch *makelower = new MakeLower("HAI");;
  cout<<"Eneterd : HAI = " <<makelower->convertChar()<<" "; 
  delete makelower;
  return 0;
}
#包括“stdafx.h”
#包括
使用名称空间std;
//将下部转换为上部或下部的程序
类转换
{
公众:
//void convertch(){};
虚拟字符*convertChar()=0;
~convertch(){};
};
类MakeLower:公共convertch
{
公众:
MakeLower(字符*密码)
{
托洛尔=真;
字母=新字符[30];
strcpy(信件、存折);
}
虚拟~MakeLower()
{

cout我认为这个问题的核心是关于虚拟方法和多态性,而不是具体的析构函数。下面是一个更清晰的例子:

class A
{
public:
    A() {}
    virtual void foo()
    {
        cout << "This is A." << endl;
    }
};

class B : public A
{
public:
    B() {}
    void foo()
    {
        cout << "This is B." << endl;
    }
};

int main(int argc, char* argv[])
{
    A *a = new B();
    a->foo();
    if(a != NULL)
    delete a;
    return 0;
}
Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 
如果没有
virtual
,它将打印出:

This is B.
This is A.

现在,您应该了解何时使用虚拟析构函数。

何时需要从基类调用派生类析构函数。您需要在基类中声明虚拟基类析构函数。

任何公开继承的类,无论是否是多态的,都应该有一个虚拟析构函数。换句话说,如果它可以被如果是类指针,则其基类应具有虚拟析构函数


如果是虚拟的,则调用派生类析构函数,然后调用基类构造函数。如果不是虚拟的,则只调用基类析构函数。

当通过基类指针删除对象时,需要使用不同析构函数的virtual关键字。 例如:

class A
{
public:
    A() {}
    virtual void foo()
    {
        cout << "This is A." << endl;
    }
};

class B : public A
{
public:
    B() {}
    void foo()
    {
        cout << "This is B." << endl;
    }
};

int main(int argc, char* argv[])
{
    A *a = new B();
    a->foo();
    if(a != NULL)
    delete a;
    return 0;
}
Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 
如果基类析构函数是虚拟的,则对象将按顺序(首先是派生对象,然后是基对象)析构函数。如果基类析构函数不是虚拟的,则只删除基类对象(因为指针是基类“base*myObj”)。因此,派生对象将出现内存泄漏。

通过指向基类的指针调用析构函数 虚拟析构函数调用与任何其他虚拟函数调用没有区别

对于
base->f()

间接调用析构函数时也会发生同样的情况,例如
delete base;
#include <iostream> using namespace std; struct a { ~a() {} unsigned long long i; }; struct b : a { ~b() {} unsigned long long j; }; struct c : b { ~c() {} virtual void m3() {} unsigned long long k; }; struct d : c { ~d() {} virtual void m4() {} unsigned long long l; }; int main() { cout << "sizeof(a): " << sizeof(a) << endl; cout << "sizeof(b): " << sizeof(b) << endl; cout << "sizeof(c): " << sizeof(c) << endl; cout << "sizeof(d): " << sizeof(d) << endl; // No issue. a* a1 = new a(); cout << "a1: " << a1 << endl; delete a1; // No issue. b* b1 = new b(); cout << "b1: " << b1 << endl; cout << "(a*) b1: " << (a*) b1 << endl; delete b1; // No issue. c* c1 = new c(); cout << "c1: " << c1 << endl; cout << "(b*) c1: " << (b*) c1 << endl; cout << "(a*) c1: " << (a*) c1 << endl; delete c1; // No issue. d* d1 = new d(); cout << "d1: " << d1 << endl; cout << "(c*) d1: " << (c*) d1 << endl; cout << "(b*) d1: " << (b*) d1 << endl; cout << "(a*) d1: " << (a*) d1 << endl; delete d1; // Doesn't crash, but may not produce the results you want. c1 = (c*) new d(); delete c1; // Crashes due to passing an invalid address to the method which // frees the memory. d1 = new d(); b1 = (b*) d1; cout << "d1: " << d1 << endl; cout << "b1: " << b1 << endl; delete b1; /* // This is similar to what's happening above in the "crash" case. char* buf = new char[32]; cout << "buf: " << (void*) buf << endl; buf += 8; cout << "buf after adding 8: " << (void*) buf << endl; delete buf; */ }
#include <iostream>
#include <memory>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){ // not virtual
        cout << "Base Destructor called\n";
    }
};

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

int main()
{
    shared_ptr<Base> b(new Derived());
}
#include <iostream>

class Mother{

public:

    Mother(){

          std::cout<<"Mother Ctor"<<std::endl;
    }

    virtual~Mother(){

        std::cout<<"Mother D-tor"<<std::endl;
    }


};

class Child: public Mother{

    public:

    Child(){

        std::cout<<"Child C-tor"<<std::endl;
    }

    ~Child(){

         std::cout<<"Child D-tor"<<std::endl;
    }
};

int main()
{

    Mother *c = new Child();
    delete c;

    return 0;
}