C++箴言:多态基类中将析构函数声明为虚拟
C++基抽象类的构造析构(纯)虚函数
C++基抽象类的构造析构(纯)虚函数⼀、析构函数可定义为⼀、析构函数可定义为纯虚函数纯虚函数,但也必须给出函数定义,但也必须给出函数定义 Effective C++ 条歀07: 为多态基类声明virtual 析构函数(Declare destructors virtual in polymorphic base classes ) 在某些类⾥声明纯虚析构函数很⽅便。
纯虚函数将产⽣抽象类——不能实例化的类(即不能创建此类型的对象)。
有些时候,你想使⼀个类成为抽象类,但刚好⼜没有任何纯虚函数。
怎么办?因为抽象类是准备被⽤做基类的,基类必须要有⼀个虚析构函数,纯虚函数会产⽣抽象类,所以⽅法很简单:在想要成为抽象类的类⾥声明⼀个纯虚析构函数。
1 //这⾥是⼀个例⼦:2 class awov {3 public :4 virtual ~awov() = 0; // 声明⼀个纯虚析构函数5 }; 这个类有⼀个纯虚函数,所以它是抽象的,⽽且它有⼀个虚析构函数,所以不会产⽣析构函数问题。
但这⾥还有⼀件事:必须提供纯虚析构函数的定义: awov::~awov() { ... } // 纯虚析构函数的定义 这个定义是必需的,因为虚析构函数⼯作的⽅式是:最底层的派⽣类的析构函数最先被调⽤,然后各个基类的析构函数被调⽤。
这就是说,即使是抽象类,编译器也要产⽣对~awov 的调⽤,所以要保证为它提供函数体。
如果不这么做,链接器就会检测出来,最后还是得回去把它添上。
⼆、? 关于C++为什么不⽀持虚拟构造函数,Bjarne 很早以前就在C++Style and Technique FAQ ⾥⾯做过回答 Avirtual call is a mechanism to get work done given partialinformation. In particular, "virtual" allows us to call afunction knowing only an interfaces and not the exact type of theobject. To create an object you need complete information.Inparticular, you need to know the exact type of what you want tocreate. Consequently, a "call to a constructor" cannot bevirtual. 含义⼤概是这样的:虚函数调⽤是在部分信息下完成⼯作的机制,允许我们只知道接⼝⽽不知道对象的确切类型。
C++考试试题重点
一、概念题1.类和对象有什么区别和联系?类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。
类是对某一类对象的抽象,而对象是某一种类的实例。
2.什么是类的实现?将类所有未编写函数体的成员函数在类体外全部编写出来。
3.this指针的概念是什么?类中所有的成员函数(静态成员函数除外)都隐含了第一个参数,这个隐含的第一个参数就是this指针,在成员函数的实现代码中,所有涉及对类的数据成员的操作都隐含为对this 指针所指对象的操作。
4.为什么要引入构造函数和析构函数?构造函数的作用是为类对象的数据成员赋初值,构造函数在定义类对象时由系统自动调用;在一个对象死亡或者说退出生存期时,系统会自动调用析构函数,因此可以在析构函数定义中,设置语句释放该对象所占用的一些资源。
5.什么时候需要自定义拷贝构造函数?若程序员没有定义拷贝构造函数,则编译器自动生成一个缺省的拷贝构造函数,它可能会产生什么问题?当有用一个已经存在对象创建一个同类型的新对象的需求时。
当对象含有指针数据成员,并用它初始化同类型的另一个对象时,默认的拷贝构造函数只能将该对象的数据成员复制给另一个对象,而不能将该对象中指针所指向的内存单元也复制过去。
这样,就可能出现同一内存单元释放两次,导致程序运行出错。
6.什么是堆对象?创建和回收堆对象的方法是什么?堆区用来存放在程序运行期间,根据需要随时建立的变量(对象),建立在堆区的对象称为堆对象,当堆对象不再使用时,应予以删除,回收所占用的动态内存。
创建和回收堆对象的方法是使用new和delete运算符。
7.为什么需要静态数据成员?静态数据成员的定义和初始化方法是什么?定义静态数据成员是为了同一个类的不同对象之间共享公共数据成员;用关键字static 可以把数据成员定义成静态数据成员;在定义的类被使用前,要对其中的静态数据成员进行初始化,初始化时不必添加关键字static。
CPP复习题答案
13. 派生类的对象对它的基类成员中 ( A ) 是可以采用对象·或者对象指针->的方 B. 公有继承的私有成员 D. 私有继承的公有成员 C ) 。
14. 关于纯虚函数和抽象类的描述中,错误的是( B. 抽象类是指具有纯虚函数的类。
A. 纯虚函数是一种特殊的虚函数,它没有具体的实现。 C. 一个基类中说明有纯虚函数,该基类的派生类一定不再是抽象类。 D. 抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。 15.下列说法错误的是( C )。 A.若语言只支持类而不支持多态,则不能称为面向对象的。 B.在运行时根据其类型确定调用哪个函数的能力叫多态性。 C.静态多态性也是在运行时根据其类型确定调用哪个函数。 D.C++中的静态多态性是通过函数重载进行实现的。 16. ( D )不是构造函数的特征 A. 构造函数的函数名与类名相同。 B. 构造函数可以重载。 C. 构造函数可以设置缺省参数。 D. 构造函数必须指定类型说明。 17.下列标识符中, A 不是 C++的关键字; A. cin B. private C. this D. operator 18.下列标识符中, A. cout A. cout 18.下列标识符中, A A 不是 C++的关键字; C. this C. this D. template D. sizeof 不是 C++的关键字; 不是 C++的关键字; C. this D. sizeof B. virtual B. public
A. 缩短程序代码,少占用内存空间 B. 既可以保证程序的可读性,又能提高程序的运行效率 C. 占用内存空间少,执行速度快 D. 使程序的结构比较清晰 3. 重载函数是( A ) A. 以函数参数来区分,而不用函数的返回值来区分不同的函数 B. 以函数的返回值来区分,而不用函数参数来区分不同的函数 C. 参数表完全相同而返回值类型不同的两个或多个同名函数 D. 参数表和返回值类型都必须是不同的两个或多个同名函数 4. 在 C++中,数据封装要解决的问题是( A. 数据的规范化 C. 避免数据丢失 5. 下列特性中,( B A.继承 之间的关系是( A. 组合关系 B.内联函数 C )。 B. 间接关系 C. 继承关系 D. 直接关系 D )。 B. 便于数据转换 D. 防止不同模块之间数据的非法访问 )不是面向对象的程序设计的特征。 C.多态性 D.封装
C++复习题及参考答案
31关于友元类的描述中,错误的是(C )A.友元类中的成员函数都是友元函数B. 友元类被说明在一个类中,它与访问权限无关C. 友元类是被定义在某个类中的嵌套类D. 如果Y是类X的友元,则类X不一定是类Y的友元32友元在访问类的对象的成员时(D)。
A)直接使用类的成员名B)使用this指针指向成员名C)使用“类名::成员名”的形式 D)使用“对象名.成员名”的形式33如果类A被说明成类B的友元,则(D )。
A. 类A的成员即类B的成员B. 类B的成员即类A的成员C. 类A的成员函数不得访问类B的成员D. 类B不一定是类A的友元34在公有继承的情况下,基类成员在派生类中的访问权限( B )。
A、受限制B、保持不变C、受保护D、不受保护35类A是类B的友元,类B是类C的友元,则(D)。
A. 类B是类A的友元B. 类C是类A的友元C. 类A是类C的友元D. 以上都不对36.下列运算符中,(A)不能在C++中重载A.?: B.[] C.new D.&&37.下列运算符不能用友元函数重载的是(C) //这个题目之前出错了,修改了。
A.+ B.- C..* D.<<38.关于运算符重载的下列描述中正确的是(B)A.可改变优先级B.需保持原有的结合性C.可改变操作数的个数 D.可改变语法结构39.为了满足运算符+的可交换性,必须将其重载为(D)A.成员函数 B.静态成员函数 C.虚函数 D.友元函数40 下列对派生类的描述中,( C)是错误的。
A. 一个派生类可以作为另一个派生类的基类B. 派生类至少应有一个基类C. 基类中成员的访问权限被派生类继承后都不改变D. 派生类的成员除了自己定义的成员外,还包含了它的基类成员二.判断正误(1) 析构函数是一个函数体为空的成员函数。
(×)(2)虚函数是为实现某种功能而假设的函数,派生类的虚函数定义影响其基类,而基类的虚函数定义不影响其派生类。
c++ virtual修饰析构函数
C++中的virtual修饰析构函数1. 概述在C++中,虚析构函数是一种特殊的析构函数,可以让派生类对象在被删除时,能够适当地调用其基类析构函数。
这种机制可以实现多态性,对于构建一个多态的对象继承体系是非常有用的。
2. 什么是虚析构函数虚析构函数是在基类中将析构函数声明为虚函数。
在C++中,虚析构函数要求在基类中用virtual关键字声明,在派生类中重写。
这样可以确保当基类的指针指向派生类的对象时,能够正确地调用派生类的析构函数。
3. 虚析构函数的作用虚析构函数的作用是解决当基类指针指向派生类对象时,只调用基类析构函数的问题。
如果不使用虚析构函数,当通过基类指针来删除继承类的对象时,只会调用基类的析构函数,而不会调用派生类的析构函数,导致可能会出现资源泄漏或者未能正确释放资源的问题。
4. 虚析构函数的声明方式虚析构函数只需在基类中进行声明即可,在派生类中无需再次声明为虚函数。
虚析构函数的声明方式如下所示:```cppclass Base {public:virtual ~Base() {// 析构函数的实现}};```5. 虚析构函数的示例下面是虚析构函数的一个简单示例:```cppclass Base {public:Base() {std::cout << "Base constructor" << std::endl; }virtual ~Base() {std::cout << "Base destructor" << std::endl; }};class Derived : public Base {public:Derived() {std::cout << "Derived constructor" << std::endl;}~Derived() {std::cout << "Derived destructor" << std::endl;}};int m本人n() {Base* ptr = new Derived();delete ptr;return 0;}```在上面的示例中,基类Base中声明了虚析构函数,而派生类Derived 中覆盖了基类的析构函数。
virtualfree函数的详细用法
虚函数是C++中的一个非常重要的概念,它允许我们在派生类中重新定义基类中的函数,从而实现多态性。
在本文中,我们将深入探讨virtual关键字的作用,以及virtual函数和纯虚函数的使用方法。
在C++中,virtual关键字用于声明一个虚函数。
这意味着当派生类对象调用该函数时,将会调用其在派生类中的定义,而不是基类中的定义。
这种行为使得我们能够在派生类中定制化地实现函数的逻辑,从而实现不同对象的不同行为。
对于virtual函数,我们需要注意以下几点:1. 在基类中声明函数时,使用virtual关键字进行声明。
2. 派生类中可以选择性地使用virtual关键字进行重声明,但通常最好也使用virtual,以便明确表明这是一个虚函数。
3. 当使用派生类对象调用虚函数时,将会根据对象的实际类型调用适当的函数实现。
4. 虚函数的实现通过虚函数表(vtable)来实现,这是一张函数指针表,用于存储各个虚函数的位置区域。
除了普通的虚函数外,C++还提供了纯虚函数的概念。
纯虚函数是在基类中声明的虚函数,它没有函数体,只有声明。
这意味着基类不能直接实例化,只能用作其他类的基类。
纯虚函数通常用于定义一个接口,而具体的实现则留给派生类。
接下来,让我们以一个简单的例子来说明虚函数和纯虚函数的用法。
假设我们有一个基类Shape,它包含一个纯虚函数calcArea用于计算面积。
有两个派生类Circle和Rectangle,它们分别实现了calcArea 函数来计算圆形和矩形的面积。
在这个例子中,我们可以看到基类Shape定义了一个纯虚函数calcArea,它没有函数体。
而派生类Circle和Rectangle分别实现了这个函数来计算不同形状的面积。
当我们使用Shape指针指向Circle或Rectangle对象时,调用calcArea函数将会根据对象的实际类型来调用适当的实现。
除了虚函数和纯虚函数外,C++中还有虚析构函数的概念。
C++之virtual析构函数
C++之virtual析构函数1.析构函数class A{public:A();~A(); //析构函数}2.析构函数需要声明为virtual条件:当定义的类中含有除去析构函数外的其他函数为virtual函数时,这时需要将此类的析构函数定义为virtual函数;3.virtual析构函数好处:当⽗类指针指向⼦类对象时,执⾏释放操作,⼦类对象也会被释放掉class Base{public:Base();virtual ~Base();//...}class Derived: public Base{public:Derived();~Derived();//...}当有如下调⽤时:Base *base =new Derived(); //⽗类的⼀个指针实际向⼦类对象delete base ;将Base析构函数声明为virtual函数时,执⾏delete base ;语句就会删除derived的对象;4.析构函数的virtual使⽤不正确时例如class Point{public :Point(int x,int y);~Point();private:int x,y;}需要实现出virtual函数时,对象就必须携带某些信息来决定在运⾏期调⽤哪⼀个virtaul函数,通常是由vptr(virtual table point)指针决定的,它指向⼀个由函数指针构成的数组,称为vtbl(virtual table);每⼀个class都有⼀个对应的vtbl。
当对象调⽤某⼀virtual函数时,实际被调⽤的函数取决于该对象的vptr所指的那个vtbl。
含有virtual的函数其对象的体积会增加,因为它多了⼀个vptr指针,所以C++的Point对象就不能和其他语⾔有着⼀样的声明结构了,因为也不再具有可移植性。
综上所述,所以当定义析构函数为虚函数时需要知道当前类是否还有⼦类,如果没有⼦类,则可以将其析构函数不定义为虚函数,否则则定义为虚函数。
C语言试题
1.下面各项不属于派生新类范畴的是(C )A.吸收基类的成员B.改造基类的成员C.删除基类的成员D.添加新成员2.在派生新类的过程中,( D )A.基类的所有成员都被继承B.只有基类的构造函数不被继承C.只有基类的析构函数不被继承D.基类的构造函数和析构函数都不被继承3.下面不属于类的继承方式的是( C )A.publicB.privateC.operatorD.protected4.作用域分辨符是指( B )A.?:B.::C.->D.&&5.专用多态是指( A )A.重载多态和强制多态B.强制多态和包含多态C.包含多态和参数多态D.参数多态和重载多态6.通用多态是指( C )A.重载多态和强制多态B.强制多态和包含多态C.包含多态和参数多态D.参数多态和重载多态7.下面各项中属于不可重载的一组运算符是( C )A.+、—、*、/B.[ ]、()C.::、.、?:、sizeof、.*D.++、——8.关于类的构造函数,下面说法不正确的是( C )A.构造函数的作用是在对象被创建时将对象初始化为一个特定的状态B.构造函数的函数名与类名相同C.构造函数可以声明为虚函数D.构造函数在对象被创建时被系统自动调用9.在C++中,数组类型属于( B )A.基本数据类型B.自定义数据类型D.结构体类型10.若有语句:float array[3][5][7];则数组array的元素个数为( D )A.3B.5C.7D.10511.关于虚基类,下面说法正确的是( D )A.带有虚函数的类称为虚基类B.带有纯虚函数的类称为虚基类C.虚基类不能实例化D.虚基类可以用来解决二义性问题12.关于析构函数,下面说法不正确的是( B )A.析构函数用来完成对象被删除前的一些清理工作B.析构函数可以声明为重载函数C.析构函数可以声明为虚函数D.析构函数在对象的生存期即将结束时被系统自动调用13.关于虚函数,下列说法不正确的是( C )A.虚函数是动态联编的基础B.虚函数的定义只能出现在类定义中的函数原形声明中C.类的成员函数均可声明为虚函数D.虚函数是用virtual关键字声明的非静态成员函数14.关于抽象类,下列说法不正确的是( B )A.抽象类不能实例化B.带有虚函数的类称为抽象类C.带有纯虚函数的类称为抽象类D.抽象类的作用是为一个类族建立一个公共接口15.下列对一维数组a的正确定义是( C )A.int n=5,a[n];B.int a(5);C.const int n=5;int a[n];D.int n;cin>>n;int a[n];16.下列数组定义语句中,不合法的是( A )A.int a[3]={0,1,2,3};B.int a[]={0,1,2};C.int a[3]={0,1,2};D.int a[3]={0};17.已知int a[10]={0,1,2,3,4,5,6,7,8,9}和*p=a,则不能表示数组a中元素的式子是( C )。
virtual 析构函数
virtual 析构函数虚析构函数是C++中一个重要的概念,它是指一个类的析构函数被声明为虚函数。
在C++中,虚函数允许在子类中重新定义父类的方法,以实现多态性。
在本文中,我们将探讨虚析构函数的概念、用途以及实现的方法,以帮助读者更好地了解和使用虚析构函数。
一、虚析构函数的概念虚析构函数是一个在基类中声明为虚函数的析构函数。
在C++中,析构函数用于释放对象所分配的内存空间,而虚析构函数则被用来处理一个派生类对象从基类指针中删除时的情况。
如果一个类的析构函数不是虚函数,那么如果我们使用一个基类指针释放一个派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数。
这可能导致内存泄漏等问题。
但如果我们声明一个虚析构函数,那么在释放派生类对象时,会首先调用派生类的析构函数,然后再调用基类的析构函数,确保对象的释放顺序正确。
虚析构函数是多态性的重要组成部分。
如果一个类的析构函数不是虚函数,那么派生类对象的空间会被正确释放,但如果它的析构函数是虚函数,则保证不论是指向基类的指针还是指向派生类的指针,都可以正确地调用它的析构函数,包括其派生类的析构函数。
虚析构函数在多态性的继承学中非常有用。
例如,我们可以使用一个基类指针来管理一个派生类对象的内存,如下所示:```class Base {public:virtual ~Base() {}};Base* pBase = new Derived;delete pBase;```虚析构函数的实现方式与虚函数相似。
我们可以在类声明中将析构函数声明为虚函数,如下所示:需要注意的是,虚析构函数必须声明为在基类中,否则派生类析构函数将不会被正确调用。
因此,如果一个派生类未声明析构函数,编译器会自动生成一个非虚析构函数。
虚析构函数的注意事项包括以下几点:1. 虚析构函数应该被声明为public。
2. 调用虚析构函数时,应该使用指向基类的指针或引用。
3. 如果一个类声明了虚析构函数,它还应该声明一个虚拷贝构造函数和虚赋值运算符,以允许正确地管理类的复制和赋值操作。
c语言虚函数
c语言虚函数一、什么是虚函数在C++中,虚函数是指在基类中被声明为虚函数的成员函数,在派生类中重新定义后,会根据对象的实际类型来选择调用哪个版本的函数。
这种机制称为动态绑定或运行时多态。
二、C语言中是否支持虚函数C语言并不直接支持虚函数,因为它没有面向对象的特性。
但是,我们可以通过结构体和函数指针模拟出类和虚函数的概念。
三、如何实现虚函数1. 定义一个基类结构体,并在其中定义一个指向成员函数的指针作为虚函数。
struct Base {int (*fun)(struct Base *self);};2. 定义一个派生类结构体,并在其中定义一个指向成员函数的指针作为重写后的虚函数。
struct Derived {struct Base base;int (*fun)(struct Derived *self);};3. 实现基类和派生类各自对应的成员函数。
int base_fun(struct Base *self) {printf("Base fun\n");return 0;}int derived_fun(struct Derived *self) {printf("Derived fun\n");return 0;}4. 在程序中创建基类对象和派生类对象,并分别调用它们各自对应的成员函数。
int main() {struct Base base = {base_fun};struct Derived derived = {{base_fun}, derived_fun};base.fun(&base);derived.base.fun(&derived);derived.fun(&derived);return 0;}四、虚函数的优缺点1. 优点虚函数可以实现多态,使得不同类型的对象在调用同一个函数时,可以根据实际类型来选择调用哪个版本的函数。
c++中virtual的用法
c++中virtual的用法第1 章C++中virtual的用法1.1 概述C++中的virtual用法是指在类声明中声明函数成员为虚函数,即在函数前加入关键字virtual,表示该函数为虚函数,可以使用多态特性。
C++中的virtual用法大多都是在类继承时使用,以便利用多态特性。
1.2 虚函数C++中的virtual关键字,表示该函数为虚函数,也就是说,该函数可以拥有多态特性,也就是子类可以实现覆盖父类中同名的虚函数,并在调用时,根据实际类型调用子类覆盖后的函数,以此实现多态特性。
1.3 virtual的用法(1)virtual 用法示例class A{public:virtual void dosomething();};class B:public A{public:void dosomething();};在上述示例中,A类中有一个dosomething函数声明为虚函数(virtual),B类是A类的子类,B类中也有一个同名的dosomething函数,该函数实际上会覆盖A类中的dosomething函数,也就是实现了多态特性,当A类的实例化对象调用函数dosomething时,会按照实际类型来调用B类中的dosomething函数。
(2)virtual 的注意事项(2.1)纯虚函数class A{public:virtual void dosomething()=0;};纯虚函数(pure virtual function)是一种特殊的虚函数,它没有实现任何具体的功能,它只是声明,以表示它可以被其他子类实现,A类中的dosomething函数就是一个纯虚函数,它的意思是A 类并没有实现dosomething函数,只是声明可以被子类实现,也就是说子类必须覆盖A类中的dosomething函数以实现多态特性。
(2.2)覆盖virtual的函数class A{public:virtual void dosomething();};class B:public A{public:virtual void dosomething();};当B类继承自A类,并实现同名的virtual函数时,B类会覆盖A类中的dosomething函数,在调用B类的dosomething函数时,会去调用B类中的dosomething函数,而不是A类中的dosomething函数,从而实现多态特性。
C++ 8多态与虚函数
fun(i);
8.6.1 虚函数的定义
【例8.5_1】根据赋值兼容规则可以用基类的指针指向 派生类对象,如果由该指针撤销派生类对象,则必须将析构函 数说明为虚函数,实现多态性,自动调用派生类析构函数。 通常要求将类设计成通用的,无论其他程序员怎样调用都 必须保证不出错,所以必须把析构函数定义为虚函数。 下面把【例8.5】析构函数改造为虚函数 class Person{ //数据成员略 public: virtual ~Person(); //只需在此声明一次,派生类的析构函数全为虚函数 }; //其他成员函数略
8.6.1 虚函数的定义
成员函数设置为虚函数的要点:
1. 派生类中定义虚函数必须与基类中的虚函数同名外,还必须 同参数表,同返回类型。否则被认为是重载,而不是虚函数。 如基类中返回基类指针,派生类中返回派生类指针是允许的, 这是一个例外。 静态类型:在编译时可知的引用类型或指针类型 2. 只有类的成员函数才能说明为虚函数。这是因为虚函数仅适 class Base; Base *p; //指针p的静态类型为Base 用于有继承关系的类对象。 动态类型:指针或引用所绑定的对象类型,仅运行时可知 3. 静态成员函数,是所有同一类对象共有,不受限于某个对象, class Derived:public Base; 不能作为虚函数。 Derived d; Base *p=&d; //指针p的动态类型为Derived 4. 一个类对象的静态和动态构造是相同的,实现动态多态性时, 必须使用基类类型的指针变量或引用,使该指针指向该基类的 不同派生类的对象,并通过该指针指向虚函数,才能实现动态 的多态性。
编译时的多态性 在C++ 中有两 种多态 性 运行时的 多态性
c++ 类 继承 多态 面试题
c++ 类继承多态面试题C++是一种面向对象的编程语言,支持类的继承和多态特性。
在C++的面试中,经常会面试关于类、继承和多态的问题。
下面给出一些常见的C++类继承多态面试题,介绍它们的概念和用法。
一、类和对象1.什么是类和对象?类是一种用户定义的数据类型,用于封装数据和方法。
对象是类的实例化,即类的一个具体实例。
2.什么是成员函数和成员变量?成员函数是类中定义的用于操作数据和执行任务的函数。
成员变量是保存在类中的数据。
3.什么是构造函数和析构函数?构造函数是一种特殊的成员函数,用于初始化对象的数据。
析构函数是对象销毁时调用的函数,用于释放资源。
二、继承1.什么是继承?继承是指一个类从另一个类获取属性和方法的过程。
通过继承,我们可以创建一个新的类,这个新的类会获取基类的所有属性和方法。
2.请简要介绍C++中的继承方式。
C++中有三种继承方式:公有继承(public inheritance)、私有继承(private inheritance)和受保护继承(protected inheritance)。
-公有继承:派生类继承基类的公有成员,基类的所有公有成员在派生类中保持公有访问权限。
-私有继承:派生类继承基类的私有成员,基类的所有成员在派生类中都变为私有成员。
-受保护继承:派生类继承基类的受保护成员,基类的公有成员在派生类中保持受保护访问权限。
3.如何防止派生类修改基类的成员?可以使用关键字`const`来限制派生类对基类成员的修改。
在基类中将成员函数声明为`const`,则派生类不能修改这些成员。
例如:```cppclass Base {public:void Print() const;};class Derived : public Base {public://错误:不能修改基类成员void Print() {//修改基类成员}};```4.多重继承和虚继承有什么区别?多重继承是指一个派生类从多个基类中继承属性和方法的过程。
析构函数 纯虚函数
析构函数纯虚函数
析构函数是C++中的一个特殊成员函数,用于在对象即将被销毁时执行清理工作。
与构造函数相对应,析构函数的名称与类名相同,前面加上波浪号(~)。
纯虚函数是在基类中声明但没有定义的虚函数。
纯虚函数没有函数体,只有函数声明,其目的是让派生类继承并重写这个函数。
如果一个类中包含了至少一个纯虚函数,那么这个类就成为抽象类,无法实例化对象。
在C++中,析构函数可以声明为纯虚函数。
这样的类称为纯虚析构函数。
纯虚析构函数的存在主要是为了实现多态性,同时确保派生类在析构时能够正确地释放资源。
当基类的析构函数为纯虚函数时,派生类必须实现自己的析构函数,否则会导致链接错误。
在派生类的析构函数中,应当调用基类的析构函数,以确保基类和派生类的资源都能正确释放。
在实际应用中,纯虚析构函数常常与工厂模式一起使用。
工厂模式是一种创建型设计模式,用于创建对象,但隐藏了对象的创建细节。
通过将基类的析构函数声明为纯虚函数,可以将对象的销毁过程交给工厂类来管理,从而实现更好的封装和解耦。
纯虚析构函数也可以用于接口类。
接口类是一种只包含纯虚函数的抽象类,用于规范派生类的行为。
通过将析构函数声明为纯虚函数,
可以强制派生类实现自己的析构函数,确保资源正确释放。
析构函数作为对象生命周期管理的一部分,与纯虚函数结合使用可以达到更好的设计效果。
通过合理地使用纯虚析构函数,可以提高代码的可维护性、可扩展性和可重用性,是面向对象编程中的重要概念之一。
C++经典面试题(2012年校园招聘)
1、在C++程序中调用被C编译器编译后的函数,为什么要加extern "C"?答案:C语言不支持函数重载,C++ 提供了C连接交换制定符号extern "C"解决名字匹配问题。
2、如何判断一段程序是由C编译程序还是由C++编译程序编译的?答案:C++编译时定义了_cplusplus。
C编译时定义了_STDC_。
3、main主函数执行完毕后,是否可能会再执行一段代码?给出说明。
答案:如果需要加入一段在main退出后执行的代码,可以使用atexit()函数注册一个函数,代码为:int atexit(void (*funciton)(void));4、用预处理指令#define声明一个常数,用以表明一年中有多少秒(忽略闰年问题)。
答案:#define SECONDS_PER_YEAR (60*60*24*365)UL 注意括号,同时不能用分号,不要写出计算出来的实际值,用到无符号长整型。
5、const与#define有什么区别?答案:两者都可以定义常量。
1)const常量有数据类型,而宏常量没有数据类型。
编译器可以对前者进行安全检查,而对后者只进行字符替换,没有类型安全检查;2)有些集成化调试工具可以对const进行调试,不能对宏常量进行调试。
const在C中其实是值不能修改的变量,因此会给它分配存储空间(默认是外部连接的),在C++中对于基本数据类型的常量,编译器会把它放到符号表里而不是分配存储空间(默认是内部连接的,若强制声明为extern,则需要分配存储空间)。
const int bufsize = 100; //在C++中注意此处int一定不可少,因为c++中不支持默认int型char buf[bufsize]; //在C++中这样写没问题const bufsize;//在C中这样写没问题,但是在C++中是有错误的。
//要做同样的事情,C++需改强制声明为extern:extern const bufsize;6、写一个标准宏MIN,输入两个参数返回最小值。
C++网络作业6答案
作业4一、选择题1.下列关于动态联编的描述中,错误的是_________。
DA)动态联编是以虚函数为基础的B)动态联编是在运行时确定所调用的函数代码的C)动态联编调用函数操作是指向对象的指针或对象引用D)动态联编是在编译时确定操作函数的注:先期联编也称静态联编,迟后联编也称动态联编。
注释:动态联编一直要到程序运行时才能确定调用哪个函数。
虚函数是实现动态联编的必要条件之一。
没有虚函数一定不能实现动态联编,但有虚函数存在时,必须同时满足下列条件,才能够实现动态联编:●类之间满足子类型关系;●调用虚函数操作的是指向对象的指针或者对象引用:或者是由成员函数调用虚函数。
2 关于虚函数的描述中,正确的是________。
DA)虚函数是一个静态成员函数B)虚函数是一个非成员函数C)虚函数既可以在函数说明时定义,也可以在函数实现时定义D)派生类的虚函数与基类中对应的虚函数具有相同的参数个数和类型注释:虚函数是非静态的成员函数。
它不能是友元函数,但可以在另一个类中被声明为友元函数。
虚函数声明只能出现在类定义的函数原型声明中,而不能在成员函数的函数体实现的时候。
派生类的虚函数与基类中对应的虚函数必须满足下列条件,否则派生类中的虚函数将丢失其虚特性,在调用时进行静态联编:●派生类中的虚函数与基类中的虚函数具有相同的名称:●派生类中的虚函数与基类中的虚函数具有相同的参数个数和相同的对应参数类型:●派生类中的虚函数与基类中的虚函数的返回值或者相同,或者都返回指针或引用,并且派生类虚函数所返回的指针或引用的基类型是基类中的虚函数所返回的指针或引用的基类型的子类型。
3 在下面四个选项中,________是用来声明虚函数的。
AA)virtual B)public C)using D)false注释:说明虚函数的一般格式如下:virtua1<函数返回类型><函数名>(<参数表>)4 对虚函数的调用________。
C++:构造函数和析构函数能否为虚函数
C++:构造函数和析构函数能否为虚函数C++:构造函数和析构函数能否为虚函数?简单回答是:构造函数不能为虚函数,⽽析构函数可以且常常是虚函数。
(1)构造函数不能为虚函数让我们来看看⼤⽜C++之⽗ Bjarne Stroustrup 在《The C++ Programming Language》⾥是怎么说的:To construct an object, a constructor needs the exact type of the object it is to create. Consequently, a constructor cannot be virtual. Furthermore, a constructor is not quite an ordinary function, In particular, it interacts with memory management in ways ordinary member functions don't. Consequently, you cannot have a pointer to a constructor.--- From 《The C++ Progamming Language》15.6.2然⽽⼤⽜就是⼤⽜,这段话对⼀般⼈来说太难理解了。
那下⾯就试着解释⼀下为什么:这就要涉及到C++对象的构造问题了,C++对象在三个地⽅构建:1、函数堆栈;2、⾃由存储区,或称之为堆;3、静态存储区。
⽆论在那⾥构建,其过程都是两步:⾸先,分配⼀块内存;其次,调⽤构造函数。
好,问题来了,如果构造函数是虚函数,那么就需要通过vtable 来调⽤,但此时⾯对⼀块 raw memeory,到哪⾥去找 vtable 呢?毕竟,vtable 是在构造函数中才初始化的啊,⽽不是在其之前。
因此构造函数不能为虚函数。
(2)析构函数可以是虚函数,且常常如此这个就好理解了,因为此时 vtable 已经初始化了;况且我们通常通过基类的指针来销毁对象,如果析构函数不为虚的话,就不能正确识别对象类型,从⽽不能正确销毁对象。
c中virtual的作用
c中virtual的作用C中virtual的作用什么是virtual?在C++中,virtual是一个关键字,用于声明类的成员函数为虚函数。
虚函数是一种特殊的成员函数,允许在继承关系中进行动态多态的调用。
virtual函数的作用1.实现多态通过将基类的成员函数声明为虚函数,可以在派生类中重写该函数,实现不同的功能。
这样,在基类指针指向派生类对象时,通过调用虚函数,可以根据实际对象的类型来调用相应的函数。
2.实现动态绑定使用虚函数可以在运行时动态绑定函数调用。
通过使用基类指针或引用指向派生类对象,可以根据实际的对象类型来调用相应的函数,而不是根据指针或引用的类型来确定函数调用。
3.实现运行时多态虚函数的另一个重要作用是实现运行时多态。
通过基类指针或引用指向不同的派生类对象,可以在运行时根据对象的具体类型来调用相应的函数,实现动态多态的效果。
使用virtual的注意事项1.virtual函数必须是成员函数虚函数必须是类的成员函数,不能是类的静态成员函数和全局函数。
2.基类中的虚函数应该有默认实现或纯虚函数基类中的虚函数可以有默认的实现,也可以声明为纯虚函数。
纯虚函数是指在基类中没有具体的实现,派生类必须实现该函数。
3.析构函数应该为虚函数如果基类中有虚函数,则析构函数应该声明为虚函数。
这是为了确保在通过基类指针删除派生类对象时,能够正确调用到派生类的析构函数。
否则,可能导致派生类的资源无法正确释放。
4.虚函数的调用开销较大虚函数的调用需要在运行时进行动态绑定,因此会有额外的开销。
对于不需要动态多态性的函数,不应该声明为虚函数,以减少运行时的开销。
总结虚函数是C++中实现多态性的重要手段之一。
通过声明虚函数,可以在派生类中重写该函数,实现动态多态的调用。
然而,虚函数的使用需要注意性能开销和函数的设计,以确保程序的正常运行和高效性能。
虚函数的实现原理虚函数的实现原理涉及到C++的对象模型和虚函数表。
在C++中,每个类对象都有一个虚函数表(vtable),虚函数表是一个指针数组,存储着该类的虚函数地址。
析构函数虚函数的原因
析构函数虚函数的原因析构函数的虚函数是一种通常用于处理带有多态性(polymorphism)的类的基类析构函数的方法。
多态性是面向对象编程中的一个重要概念,它允许使用基类指针或引用来管理派生类对象,从而增强代码的可扩展性和可维护性。
在C++中,继承(inheritance)提供了一种从现有类创建新类的机制。
基类是被继承的类,派生类(derived class)则继承了基类的特性,并可以添加自己的数据成员和函数成员。
在使用继承时,经常需要通过基类指针或引用来处理派生类对象。
这种情况下,基类的析构函数通常被用来删除动态分配的内存和释放资源,以确保内存泄漏和资源浪费的问题。
虚函数是一种在运行时选择执行的函数,它通过在函数声明中添加`virtual`关键字来定义。
基类中的虚函数可以被派生类重写,从而实现多态性。
当基类指针或引用指向派生类对象时,调用虚函数时将根据指针或引用所指对象的实际类型来确定调用哪个类的函数。
当需要使用基类指针或引用来处理派生类对象的时候,如果基类的析构函数不是虚函数,那么在使用`delete`释放派生类对象内存时就会出现问题。
考虑以下示例代码:```cppclass Basepublic:Base( { }~Base( { cout << "Base destructor" << endl; }};class Derived : public Basepublic:Derived( { }~Derived( { cout << "Derived destructor" << endl; }};int maiBase* ptr = new Derived(;delete ptr;return 0;```在这个例子中,`Base`类和`Derived`类之间存在继承关系,`Derived`类重写了`Base`类的析构函数。
c++ 析构函数是否虚函数
c++ 析构函数是否虚函数
在C++ 中,析构函数可以是虚函数。
当类的析构函数是虚函数时,它将在对象被删除时自动调用,这样可以避免内存泄漏。
然而,在大多数情况下,析构函数不需要是虚函数,因为当对象被删除时,编译器会自动调用正确的析构函数。
当析构函数是虚函数时,如果一个指向基类对象的指针被删除,它会调用派生类中重写的析构函数。
这是因为,在运行时,编译器会根据指针所指向的对象的实际类型来决定调用哪个析构函数。
这种机制称为“动态绑定”或“多态”。
这对于管理资源非常有用,比如当一个基类指针指向一个派生类对象时,如果基类析构函数不是虚函数,那么在删除基类指针时只会调用基类的析构函数,而派生类对象的析构函数不会被调用,这可能导致内存泄漏。
然而,析构函数是虚函数有一些缺点,首先,虚函数需要额外的内存来存储虚函数表,这会增加对象的大小,其次,虚函数需要额外的时间来解析虚函数表,使性能降低。
因此,如果不需要使用虚函数特性,应该避免使用它。
C++ 中还有一种叫做纯虚析构函数的概念。
纯虚析构
函数是一种虚函数,但它没有函数体,因此它不能被直接调用。
纯虚析构函数的主要用途是在基类中定义一个“接口”,要求派生类必须定义自己的析构函数。
当一个类具有纯虚析构函数时,它不能实例化对象,因为纯虚析构函数无法被实现。
这种类被称为抽象类,它只能作为基类使用。
总的来说,析构函数可以是虚函数,它具有动态绑定的特性,在继承体系中非常有用。
但是,如果不需要这种特性,应该避免使用虚函数,以提高性能。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C++箴言:多态基类中将析构函数声明为虚拟
有很多方法可以跟踪时间的轨迹,所以有必要建立一个 TimeKeeper 基类,并为不同的计时方法建立派生类:
class TimeKeeper {
public:
TimeKeeper();
~TimeKeeper();
...
};
class AtomicClock: public TimeKeeper { ... };
class WaterClock: public TimeKeeper { ... };
class WristWatch: public TimeKeeper { ... };
很多客户只是想简单地取得时间而不关心如何计算的细节,所以一个 factory 函数--返回一个指向新建派生类对象的基类指针的函数--被用来返回一个指向计时对象的指针:TimeKeeper* getTimeKeeper(); // returns a pointer to a dynamic-
// ally allocated object of a class
// derived from TimeKeeper
按照 factory 函数的惯例,getTimeKeeper 返回的对象是建立在堆上的,所以为了避免泄漏内存和其他资源,最重要的就是要让每一个返回的对象都可以被完全删除。
TimeKeeper *ptk = getTimeKeeper(); // get dynamically allocated object
// from TimeKeeper hierarchy
... // use it
delete ptk; // release it to avoid resource leak
现在我们精力集中于上面的代码中一个更基本的缺陷:即使客户做对了每一件事,也无法预知程序将如何运转。
问题在于 getTimeKeeper 返回一个指向派生类对象的指针(比如 AtomicClock),那个对象通过一个基类指针(也就是一个 TimeKeeper* 指针)被删除,而且这个基类
(TimeKeeper)有一个非虚的析构函数。
祸端就在这里,因为 C++ 指出:当一个派生类对象通过使用一个基类指针删除,而这个基类有一个非虚的析构函数,则结果是未定义的。
运行时比较有代表性的后果是对象的派生部分不会被销毁。
如果 getTimeKeeper 返回一个指向AtomicClock 对象的指针,则对象的 AtomicClock 部分(也就是在 AtomicClock 类中声明的数据成员)很可能不会被销毁,AtomicClock 的析构函数也不会运行。
然而,基类部分(也就是 TimeKeeper 部分)很可能已被销毁,这就导致了一个古怪的"部分析构"对象。
这是一个泄漏资源,破坏数据结构以及消耗大量调试时间的绝妙方法。
排除这个问题非常简单:给基类一个虚析构函数。
于是,删除一个派生类对象的时候就有了你所期望的正确行为。
将销毁整个对象,包括全部的派生类部分:
class TimeKeeper {
public:
TimeKeeper();
virtual ~TimeKeeper();
...
};
TimeKeeper *ptk = getTimeKeeper();
...
delete ptk; // now behaves correctly
类似 TimeKeeper 的基类一般都包含除了析构函数以外的其它虚函数,因为虚函数的目的就是允许派生类定制实现(参见 Item 34)。
例如,TimeKeeper 可能有一个虚函数getCurrentTime,在各种不同的派生类中有不同的实现。
几乎所有拥有虚函数的类差不多都应该有虚析构函数。
如果一个类不包含虚函数,这经常预示不打算将它作为基类使用。
当一个类不打算作为基类时,将析构函数声明为虚拟通常是个坏主意。
考虑一个表现二维空间中的点的类:class Point { // a 2D point
public:
Point(int xCoord, int yCoord);
~Point();
private:
int x, y;
};
如果一个 int 占 32 位,一个 Point 对象正好适用于 64 位的寄存器。
而且,这样一个 Point 对象可以被作为一个 64 位的量传递给其它语言写的函数,比如 C 或者FORTRAN。
如果 Point 的析构函数是虚拟的,情况就完全不一样了。
虚函数的实现要求对象携带额外的信息,这些信息用于在运行时确定该对象应该调用哪一个虚函数。
典型情况下,这一信息具有一种被称为 vptr(virtual table pointer,虚函数表指针)的指针的形式。
vptr 指向一个被称为 vtbl(virtual table,虚函数表)的函数指针数组,每一个包含虚函数的类都关联到 vtbl。
当一个对象调用了虚函数,实际的被调用函数通过下面的步骤确定:找到对象的 vptr 指向的 vtbl,然后在 vtbl 中寻找合适的函数指针。
虚函数如何被实现的细节是不重要的。
重要的是如果 Point 类包含一个虚函数,这个类型的对象的大小就会增加。
在一个 32 位架构中,它们将从 64 位(相当于两个 int)长到 96 位(两个 int 加上 vptr);在一个 64 位架构中,他们可能从 64 位长到 128 位,因为在这样的架构中指针的大小是 64 位的。
为 Point 加上 vptr 将会使它的大小增长50-100%!Point 对象不再适合 64 位寄存器。
而且,Point 对象在 C++ 和其他语言(比如 C)中,看起来不再具有相同的结构,因为其它语言缺乏 vptr 的对应物。
结果,Points 不再可能传入其它语言写成的函数或从其中传出,除非你为 vptr 做出明确的对应,而这是它自己的实现细节并因此失去可移植性。