文档章节

何时使用虚拟析构函数?

 技术盛宴
发布于 2019/12/08 14:20
字数 1224
阅读 10
收藏 0

我对大多数面向对象理论有扎实的了解,但令我困惑的一件事是虚拟析构函数。

我以为无论链中的每个对象是什么,析构函数总是被调用。

您打算何时将它们虚拟化?为什么?


#1楼

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


#2楼

虚拟构造函数是不可能的,但虚拟析构函数是可能的。 让我们尝试一下...

#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;
}

上面的代码输出以下内容:

Base Constructor Called
Derived constructor called
Base Destructor called

派生对象的构造遵循构造规则,但是当我们删除“ 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;
}

输出更改如下:

Base Constructor Called
Derived Constructor called
Derived destructor called
Base destructor called

因此,基本指针(在派生对象上进行分配!)的破坏遵循破坏规则,即首先是“派生”,然后是“基”。 另一方面,没有什么像虚拟构造函数。


#3楼

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

类析构函数是与〜相同的类名称的函数,它将重新分配由该类分配的内存。 为什么我们需要虚拟析构函数

请参阅以下带有虚拟功能的示例

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

#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;
}

从上面的示例中,您可以看到未同时调用MakeUpper和MakeLower类的析构函数。

查看带有虚拟析构函数的下一个示例

#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;
}

虚拟析构函数将显式调用类的最派生运行时析构函数,以便能够以适当的方式清除对象。

或访问链接

https://web.archive.org/web/20130822173509/http://www.programminggallery.com/article_details.php?article_id=138


#4楼

我认为这个问题的核心是关于虚拟方法和多态性,而不是专门的析构函数。 这是一个更清晰的示例:

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;
}

将打印出:

This is B.

没有virtual它将打印输出:

This is A.

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


#5楼

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

本文转载自:https://stackoom.com/question/1vyl/何时使用虚拟析构函数

粉丝 0
博文 1200
码字总数 0
作品 0
深圳
高级程序员
私信 提问
加载中

评论(0)

C++中将构造函数和析构函数定义为private的用意

很多情况下要求当前的程序中只有一个object。例如一个程序只有一个和数据库的连接,只有一个鼠标的object。通常我们都将构造函数的声明置于public区段,假如我们将 其放入private区段中会发生...

Kylen
2012/07/30
1.8K
0
继承中构造函数和析构函数的调用顺序

由架构中基类的设计想到的...... 现在,有三个类,类的定义如下 #include <iostream> using namespace std; class CA{public: }; class CB:public CA{public: };class CC:public CB{public:......

Start-up
2012/05/17
514
0
[C++再学习系列] 虚函数的4条规则

对于常规的基类函数来说: 1 尽量使用非虚拟接口模式(NVI)让接口函数成为非虚拟的。 2 尽量让虚函数成为私用的。 3 只有当派生类需要调用基类对某个虚函数的实现时,才把虚函数声明为保护的。...

技术小美
2017/11/12
0
0
CUDA学习(六十九)

设备内存空间说明符: ,和内存空间说明符不允许用于: 类,结构和联合数据成员, 形参 在主机上执行的函数中的局部变量。 和变量隐含了静态存储。 和变量定义只能在命名空间范围(包括全局命...

night李
2018/02/23
0
0
[C++再学习系列] 全局或静态变量(对象)的初始化

  对于C语言的全局和静态变量,不管是否被初始化,其内存空间都是全局的;如果初始化,那么初始化发生在任何代码执行之前,属于编译期初始化。由于内置变量无须资源释放操作,仅需要回收内...

技术小美
2017/11/05
0
0

没有更多内容

加载失败,请刷新页面

加载更多

UGUI图片层级和渲染顺序的奇怪关系

之前见别人的文章总是说,在Hierachy下,相同图集的图片要连续排列,这样Unity会对相同图集的图片进行合批,从而减少draw call。今天做了简单的试验发现情况并不是这么简单的。 第一种情况:...

myctrd
16分钟前
29
0
jQuery中$.each()方法的使用-Json对象-dom元素

对于循环我们首先会想到for循环,但是在前端对数组我们可以使用,但是对于json对象,想把对象中的属性的key-value循环去取出,那么for循环提供不了的。而each方法则给我们提供了便利,下面介...

imzchloe
26分钟前
50
0
Linuxprobe第六天

待补充

nt狮子男人
27分钟前
50
0
gem install:无法构建gem native扩展(找不到头文件)

我正在使用Fedora 14,我安装并运行了MySQL和MySQL服务器5.1.42。 现在我尝试以root用户身份执行此操作: gem install mysql 但我得到这个错误: Building native extensions. This could ...

技术盛宴
34分钟前
51
0
就8张图片带你搞清楚JS的原型链

JS(JavaScript)是目前互联网开发中十分重要的一门编程语言,他承载着网页、手机应用程序、硬件程序、微信、微信小程序中的各种特效及处理逻辑功能。

涂老师
37分钟前
52
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部