C++中虚析构函数的作用
- 格式:doc
- 大小:33.00 KB
- 文档页数:2
析构函数和虚函数的用法和作用
析构函数的作用是在对象被销毁时进行一些清理工作,例如释放动态分配的内存或关闭文件等。
析构函数的命名通常为类名前加“~”。
虚函数是一种在基类中声明并在派生类中重新定义的函数。
它允许在基类指针或引用上调用派生类对象的特定版本。
虚函数通过在运行时确定被调用的函数,实现多态性。
使用析构函数的情况:
1. 当一个类需要在对象被销毁时释放动态分配的内存时,需要使用析构函数。
2. 如果一个类具有成员对象、引用或指针,这些成员对象本身也需要在其它对象销毁之前被销毁时,可以使用析构函数。
使用虚函数的情况:
1. 当派生类需要重写基类的成员函数时,可以将基类的成员函数声明为虚函数。
2. 当需要在程序运行时确定运行时类型而不是编译时类型时,可以使用虚函数。
这样在使用基类指针或引用调用该函数时,将调用实际运行时类型的函数。
需要注意的是,虚函数的使用会稍微增加一些运行时开销,因为需要在运行时查找并确定运行时类型的函数。
而析构函数通常需要在继承链上进行调用,因此应该将析构函数声明为虚函数,以确保正确调用派生类的析构函数。
第八章多态1.单选题(1).下列关于运算符重载的描述中,( D )是正确的。
(A) 可以改变参与运算的操作数个数 (B) 可以改变运算符原来的优先级(C) 可以改变运算符原来的结合性(D) 不能改变原运算符的语义(2).下列函数中,不能重载运算符的函数是( b )。
(A) 成员函数(B) 构造函数(C) 普通函数 (D) 友员函数(3).要求用成员函数重载的运算符是( A )。
(A) =(B) == (C) <= (D) ++(4).要求用友员函数重载的运算符是( C )。
(A) = (B) [] (C) <<(D) ()(5).在C++中,要实现动态联编,必须使用( D )调用虚函数。
(A) 类名(B) 派生类指针(C) 对象名(D) 基类指针(6).下列函数中,不能说明为虚函数的是( C )。
(A) 私有成员函数(B) 公有成员函数(C) 构造函数(D) 析构函数(7).在派生类中,重载一个虚函数时,要求函数名、参数的个数、参数的类型、参数的顺序和函数的返回值( A )。
(A) 相同(B)不同(C) 相容(D) 部分相同(8).C++中,根据(D )识别类层次中不同类定义的虚函数版本。
(A) 参数个数(B) 参数类型(C) 函数名(D) this指针类型(9).虚析构函数的作用是(C )。
(A) 虚基类必须定义虚析构函数(B) 类对象作用域结束时释放资源(C)delete动态对象时释放资源(D) 无意义(10).下面函数原型中,( B )声明了fun为纯虚函数。
(A) void fun()=0; (B) virtual void fun()=0;(C) virtual void fun(); (D) virtual void fun(){ };(11).若一个类中含有纯虚函数,则该类称为( C )。
(A) 基类(B)纯基类(C) 抽象类(D) 派生类(12).假设Aclass为抽象类,下列正确的说明语句是( B )。
1. 析构函数和虚析构函数如果基类的析构函数是虚的,那么它的派生类的析构函数都是虚的这将导致:当派生类析构的时候,它的所有的基类的析构函数都将得到调用否则,只调用派生类的析构函数(这可能导致基类的某些对象没有得到释放)所以CObject类的析构函数是虚的,所有由它派生的类析构的时候一级一级的进行,不会造成内存泄漏。
无论基类的析构函数是否为虚析构函数. 基类的析构函数总是会被自动调用的;但是, 如果用基类指针去操作一个了派生类对象,如果不为虚就不能保证派生类的析构函数被调用。
2. 纯虚析构函数析构函数的纯虚性唯一效果就是保证抽象类的实例化。
《Effective C++》中第14条条款的一部分,既是对虚析构函数的彻底理解,亦是对纯虚析构函数作用的解释。
在某些类里声明纯虚析构函数很方便。
纯虚函数将产生抽象类——不能实例化的类(即不能创建此类型的对象)。
有些时候,你想使一个类成为抽象类,但刚好又没有任何纯虚函数。
怎么办?因为抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以方法很简单:在想要成为抽象类的类里声明一个纯虚析构函数。
这里是一个例子:class awov {public:virtual ~awov() = 0; // 声明一个纯虚析构函数};这个类有一个纯虚函数,所以它是抽象的,而且它有一个虚析构函数,所以不会产生析构函数问题。
但这里还有一件事:必须提供纯虚析构函数的定义:awov::~awov() {} // 纯虚析构函数的定义这个定义是必需的,因为虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。
这就是说,即使是抽象类,编译器也要产生对~awov的调用,所以要保证为它提供函数体。
如果不这么做,链接器就会检测出来,最后还是得回去把它添上。
3. 虚函数【1】在基类用virtual声明成员函数为虚函数。
这样就可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用。
C++开发基础(习题卷20)说明:答案和解析在试卷最后第1部分:单项选择题,共59题,每题只有一个正确答案,多选或少选均不得分。
1.[单选题]C++当中,若局部变量和全局变量重名,会发生什么?A)只能访问局部变量B)局部变量会屏蔽全局变量,要使用全局变量,需要使用“::”C)全局变量会屏蔽局部变量,要使用局部变量,需要使用“::”D)只能访问全局变量2.[单选题]对于线性表的描述正确的是()A)一个有限序列,可以为空B)一个有限序列,不可以为空C)一个无限序列,可以为空D)一个无限序列,不可以为空3.[单选题]下列函数原型声明中错误的是( )。
A)void Fun(int x=0, int y=0);B)void Fun(int x, int y);C)void Fun(int x, int y=0);D)void Fun(int x=0, int y);4.[单选题]树的度不为0的结点称为A)根B)叶子C)分支结点D)父结点5.[单选题]已知如下程序:class B{public:B(){}B(int c):count(c){}virtual void print() const=0;private:int count;};class D:public B{public:D():B(0){}void print() const {cout<<""hello""<<endl;}};void main(){D d(10);B *pb;pb = &d; // OKB &cb = d;D dd = *pb; //2 B对象不能转换为DD &cd = cb; //3 B对象不能转换为D&B bb = d; //4 B类,不能实例化抽象类}则在标出的几个语句中,没有错误的是( )。
A)1B)2C)3D)46.[单选题]下列选项中,不属于类模型结构图中的是( )A)类名B)属性C)操作D)对象名7.[单选题]在一棵有n个结点的二叉树中,若度为2的结点数为n2,度为1的结点数为n1,度为0的结点数为n0,则树的最大高度为A)n0+n1+n2B)n1+n2C)n2+1D)18.[单选题]使用派生类的主要原因是( )A)提高代码的可重用性B)提高程序的运行效率C)加强类的封装性D)实现数据的隐藏9.[单选题]下列关于实参和形参说法错误的是( )。
析构函数纯虚函数析构函数是C++中的一个重要概念,用于在对象被销毁时执行特定的清理工作。
与构造函数相对应,析构函数的作用是释放对象在程序运行过程中所占用的资源,确保程序的内存管理不发生泄漏。
在面向对象编程中,析构函数是非常重要的,特别是在需要动态分配内存或者进行资源管理的情况下。
纯虚函数则是另一个重要的概念,它是一个在基类中声明但在派生类中没有具体实现的虚函数。
纯虚函数通过在基类中定义接口,要求所有派生类必须实现这个接口,从而实现多态性。
纯虚函数在C++中通过在函数声明后加上“= 0”来声明,使得这个函数成为纯虚函数,不能在基类中具体实现。
析构函数和纯虚函数的结合可以带来更灵活和高效的程序设计。
在面向对象的编程中,经常会遇到需要在基类中定义一些通用的操作,但具体的实现需要在派生类中完成的情况。
这时可以通过将这些通用操作定义为纯虚函数,然后在析构函数中调用这些纯虚函数,以实现基类和派生类之间的协作。
在实际的程序设计中,通常会使用虚析构函数来释放对象所占用的资源。
虚析构函数允许在基类中定义一个虚析构函数,然后在派生类中进行具体的资源释放操作。
这样可以确保在删除一个指向派生类对象的基类指针时,能够正确调用派生类的析构函数,从而释放对象所占用的资源。
在使用纯虚函数时,通常会将基类的析构函数定义为虚析构函数。
这样可以确保在删除一个指向派生类对象的基类指针时,能够正确调用派生类的纯虚函数,从而实现派生类特有的清理工作。
通过将纯虚函数定义为析构函数的一部分,可以实现基类和派生类之间的解耦,使得程序设计更加灵活和可维护。
总的来说,析构函数和纯虚函数在面向对象编程中扮演着重要的角色。
通过合理设计和使用析构函数和纯虚函数,可以实现更加灵活、高效和易于维护的程序设计。
析构函数用于对象的资源释放,纯虚函数用于接口的定义和实现,二者结合起来可以实现基类和派生类之间的协作,提高程序的可扩展性和可维护性。
在实际的程序设计中,我们应该充分利用析构函数和纯虚函数的特性,以实现更加优秀的面向对象设计。
清华大学计算机系C++期末考试题及答案一、填空题(25小题,共50分)(以下每小题1分,共10分)1.在C++中,函数的参数有两种传递方式,它们是值传递和地址或指针或引用传递。
2.当一个成员函数被调用时,该成员函数的this指针指向调用它的对象。
3.在基类和派生类中,派生类可以定义其基类中不具备的数据和操作.对两个有相同名字的数据成员进行访问时,如果没有作用域分隔符限定时,对此数据成员的访问将出现歧义。
4.拷贝构造函数使用引用作为参数初始化创建中的对象。
5.在公有继承的情况下,基类数据成员在派生类中的访问权限保持不变 .6.描述命题”A小于B或小于C”的表达式为A〈B||A〈C。
7.用new申请某一个类的动态对象数组时,在该类中必须能够匹配到没有形参的或缺省参数构造函数,否则应用程序会产生一个编译错误。
8.静态数据成员在类外进行初始化,且静态数据成员的一个拷贝被类的所有对象共享。
9.为了避免可能出现的歧义,C++对if…else语句配对规则规定为:else总是与与最近的if配对。
10.设"int a=3,b=4,c=5;",表达式”(a+b)〉c&&b==c"的值是0。
(以下每小题2分,共20分)11.面向对象的程序设计有四大特征,它们是抽象、封装、继承、多态 .12.在Visual C++中,定义重载函数时,应至少使重载函数的参数个数或参数类型不同 ;在基类和派生类中,成员函数的覆盖是指派生类成员函数与在基类被覆盖的成员函数名、参数个数、参数类型和返回值类型均相同。
13.构造函数与析构函数除功能不同外,在定义形式上,它们的区别还包括构造函数名与类名相同,而析构函数名是在类名前加一个~、析构函数没有参数、析构函数可以定义为虚函数。
14.动态联编要满足两个条件,它们是被调用的成员函数是虚函数、用指针或引用调用虚函数。
15.在C++类中,有一种不能定义对象的类,这样的类只能被继承,称之为抽象类,定义该类至少具有一个纯虚函数。
1. C++的类和C里面的struct有什么区别?struct成员默认访问权限为public,而class成员默认访问权限为private 2. 析构函数和虚函数的用法和作用析构函数是在对象生存期结束时自动调用的函数,用来释放在构造函数分配的内存。
虚函数是指被关键字virtual说明的函数,作用是使用C++语言的多态特性3. 全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?1) 全局变量的作用用这个程序块,而局部变量作用于当前函数2) 前者在内存中分配在全局数据区,后者分配在栈区3) 生命周期不同:全局变量随主程序创建和创建,随主程序销毁而销毁,局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在4) 使用方式不同:通过声明后全局变量程序的各个部分都可以用到,局部变量只能在局部使用4. 有N个大小不等的自然数(1–N),请将它们由小到大排序.要求程序算法:时间复杂度为O(n),空间复杂度为O(1)。
void sort(int e[], int n){int i;int t;for (i=1; i{t = e[e[i]];e[e[i]] = e[i];e[i] = t;}}5. 堆与栈的去区别A. 申请方式不同Stack由系统自动分配,而heap需要程序员自己申请,并指明大小。
B. 申请后系统的响应不同Stack:只要栈的剩余空间大于申请空间,系统就为程序提供内存,否则将抛出栈溢出异常Heap:当系统收到程序申请时,先遍历操作系统中记录空闲内存地址的链表,寻找第一个大于所申请空间的堆结点,然后将该结点从空间结点链表中删除,并将该结点的空间分配给程序。
另外,大多数系统还会在这块内存空间中的首地址处记录本次分配的大小,以便于delete语句正确释放空间。
而且,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表。
C. 申请大小限制的不同Stack:在windows下,栈的大小是2M(也可能是1M它是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。
virtual 析构函数虚析构函数是C++中一个重要的概念,它是指一个类的析构函数被声明为虚函数。
在C++中,虚函数允许在子类中重新定义父类的方法,以实现多态性。
在本文中,我们将探讨虚析构函数的概念、用途以及实现的方法,以帮助读者更好地了解和使用虚析构函数。
一、虚析构函数的概念虚析构函数是一个在基类中声明为虚函数的析构函数。
在C++中,析构函数用于释放对象所分配的内存空间,而虚析构函数则被用来处理一个派生类对象从基类指针中删除时的情况。
如果一个类的析构函数不是虚函数,那么如果我们使用一个基类指针释放一个派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数。
这可能导致内存泄漏等问题。
但如果我们声明一个虚析构函数,那么在释放派生类对象时,会首先调用派生类的析构函数,然后再调用基类的析构函数,确保对象的释放顺序正确。
虚析构函数是多态性的重要组成部分。
如果一个类的析构函数不是虚函数,那么派生类对象的空间会被正确释放,但如果它的析构函数是虚函数,则保证不论是指向基类的指针还是指向派生类的指针,都可以正确地调用它的析构函数,包括其派生类的析构函数。
虚析构函数在多态性的继承学中非常有用。
例如,我们可以使用一个基类指针来管理一个派生类对象的内存,如下所示:```class Base {public:virtual ~Base() {}};Base* pBase = new Derived;delete pBase;```虚析构函数的实现方式与虚函数相似。
我们可以在类声明中将析构函数声明为虚函数,如下所示:需要注意的是,虚析构函数必须声明为在基类中,否则派生类析构函数将不会被正确调用。
因此,如果一个派生类未声明析构函数,编译器会自动生成一个非虚析构函数。
虚析构函数的注意事项包括以下几点:1. 虚析构函数应该被声明为public。
2. 调用虚析构函数时,应该使用指向基类的指针或引用。
3. 如果一个类声明了虚析构函数,它还应该声明一个虚拷贝构造函数和虚赋值运算符,以允许正确地管理类的复制和赋值操作。
实验七虚函数及应用一、实验目的1.理解虚函数与运行时(动态)多态性之间的关系,掌握虚函数的定义及应用;2.理解纯虚函数与抽象类的概念,掌握抽象类的定义及应用;3.理解虚析构函数的概念及作用。
二、实验学时课内实验:2课时课外练习:2课时三本实验涉及的新知识㈠虚函数与动态多态性在C++中,如果将基类与派生类的同名成员函数定义为虚函数,就可以定义一个基类指针,当基类指针指向基类对象时访问基类的成员函数,当基类指针指向派生类对象时访问派生类的成员函数,实现在运行时根据基类指针所指向的对象动态调用成员函数,实现动态多态性。
换句话说,虚函数与派生类相结合,使C++能支持运行时(动态)多态性,实现在基类中定义派生类所拥有的通用“接口”,而在派生类中定义具体的实现方法,即“一个接口,多种方法”。
㈡虚函数的定义1.在基类中定义在定义函数的前面加上“virtual ”。
即:virtual 返回类型函数名(参数表){ …… }2.在派生类中定义函数的返回类型、函数名、参数的个数、参数类型及顺序必须与基类中的原型完全相同。
3.说明:⑴在派生类中定义虚函数时,可用“virtual”也可不用“virtual”(最好都使用)。
⑵虚函数在派生类中重新定义时,其原型必须与基类中相同。
⑶必须用基类指针访问虚函数才能实现运行时(动态)多态性;当用普通成员函数的调用方法(即用圆点运算符)调用虚函数时,为静态调用;⑷虚函数在自身类中必须声明为成员函数(不能为友元函数或静态成员函数),但在另一个类中可以声明为友元函数。
⑸虚函数可以公有继承多次,其虚函数的特性不变。
⑹构造函数不能定义为虚函数,但析构函数可以定义为虚函数。
⑺虚函数与重载函数的关系①普通函数重载是通过参数类型或参数的个数不同实现的;重载一个虚函数时,其函数原型(返回类型、参数个数、类型及顺序)完全相同。
②当重载的虚函数只有返回类型不同时,系统将给出错误信息;如果定义的虚函数只有函数名相同,而参数个数或类型不同时,则为普通函数重载。
C++中虚析构函数的作用
我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。
可是,为什么要这样做呢?下面用一个小例子来说明:
有下面的两个类:
class ClxBase
{
public:
ClxBase() {};
virtual ~ClxBase() {cout<<”aaa”<<endl;};
virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; }; };
class ClxDerived : public ClxBase
{
public:
ClxDerived() {};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};
代码
ClxBase *pTest = new ClxDerived;
pTest->DoSomething();
delete pTest;
的输出结果是:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
aaa
这个很简单,非常好理解。
但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了:
Do something in class ClxDerived!
aaa
也就是说,类ClxDerived的析构函数根本没有被调用!(注:肯定不会被调用,因为动态联
编,在运行时会检查有无派生类对象重载本函数,有则调用之,基类析构不会调用)一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。
我想所有的C++程序员都知道这样的危险性。
当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。
所以,文章开头的那个问题的答案就是--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。
当然,并不是要把所有类的析构函数都写成虚函数。
因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。
所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
#include <iostream>
using namespace std;
class ClxBase
{
public:
ClxBase() {cout<<"ClxBase Constructuer"<<endl;};
virtual ~ClxBase() {cout<<"aaa"<<endl;};
virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};
class ClxDerived : public ClxBase
{
public:
ClxDerived() {cout<<"ClxDerived Constructuer"<<endl;};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};
void main()
{
ClxDerived *pTest = new ClxDerived;
// ClxBase *pTest = new ClxDerived;
pTest->DoSomething();
delete pTest;
}。