C++面试题-面向对象-多态性与虚函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liubing8609/article/details/86137626

C++面试题-面向对象-多态性与虚函数

问:在C++程序中调用被C编译器编译后的函数,为什么要加extern "C"?

答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:void foo(int c,int y); 该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern "C"来解决名字匹配问题。(其实这就是表明此函数是C语言编写的)

 

问:多态是什么?作用是什么?

答:是对于不同对象接收相同消息时产生不同的动作。C++的多态性具体体现在运行和编译两个方面:

  1. 在程序运行时的多态性通过继承和虚函数来体现;
  2. 在程序编译时多态性体现在函数和运算符的重载上;
  3. 编译时多态性:通过重载函数实现,函数模板;
  4. 运行时多态性:通过虚函数实现。

作用:

  1. 隐藏实现细节,使得代码能够模块化;扩展代码模块。实现代码重用。
  2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。说白了,就是()虚函数。

 

问:多态的两个必要条件?

答:

  1. 父类需要定义虚函数,子类改写该函数。
  2. 定义一个基类指针,调用子类构造函数构建对象。

简答版:

  1. 一个基类的指针或引用指向一个派生类对象。
  2. 虚函数。

 

问:什么叫静态关联,什么叫动态关联?

答:

  1. 在多态中,如果程序在编译阶段就能确定实际执行动作,则称为静态关联,即编译时多态性。
  2. 如果等到程序运行才能确定叫动态关联,即运行时多态性。

 

问:以下关于多态性的描述中,错误的是()

A. 动态多态是通过类的继承关系和虚函数来实现的。

B. 静态多态是通过函数的重载或运算符的重载来实现的。

C. 多态性通常使用虚函数或重载技术来实现

D. 为实现动态多态,基类必须定义为含有纯虚函数的抽象类。

答:D

分析:动态多态,基类可以都有虚函数和纯虚函数,也可以两者中存在一种,但不可以两者都没有。

 

问:类成员函数的重载、覆盖和隐藏的区别?

答:成员函数被重载的特征:

  1. 相同的范围(在同一个类中);
  2. 函数名字相同;
  3. 参数不同;
  4. virtual关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征是:

  1. 不同的范围(分别位于派生类与基类);
  2. 函数名字相同
  3. 参数不同;
  4. 基类函数必须有virtual关键字。

“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

  1. 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数被隐藏(注意别与重载混淆)。
  2. 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏。(注意别与覆盖混淆)

简答版:

  1. 重载:在同一个类中,存在两个以上的同名但参数不同的函数。
  2. 覆盖:基类指向派生类,两者的函数同名但参数不同的()虚函数。
  3. 隐藏:派生类的函数与基类的函数同名,参数可能相同也可能不同,但是基类没有virtual关键字。

 

问:虚函数是什么?作用是什么?

答:虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态覆盖(Override)。为了允许用基类的指针来调用子类的这个函数。当基类的指针指向子类对象时候,该定义在基类的虚函数已经成为子类的函数,是属于子类的。其他成员函数是属于父类的。

虚函数,它虚就虚在所谓“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。

 

问:纯虚函数是什么?作用是什么?

答:纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”:

virtual void funtion()=0

引入原因:

  1. 为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
  2. 在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。

声明了纯虚函数的类是一个抽象类。所以,用户不能创建类的实例,只能创建它的派生类的实例。

纯虚函数最显著的特征是:它们必须在继承类中重新声明函数(不要后面的=0,否则该派生类也不能实例化),而且它们在抽象类中往往没有定义。

定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。

纯虚函数的意义,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。所以类纯虚函数的声明就是在告诉子类的设计者,“你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。

注意:所有在基类的纯虚函数都要在子类当中被实现,否则子类仍然是个抽象类。

 

问:虚函数表是什么?作用是什么?

答:虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址。

  1. 虚函数是通过一张虚函数表来实现的。
  2. 每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有类对象共享。
  3. 类的每个虚成员占据虚函数表中的一行。
  4. 如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。在有虚函数的实例中分配了指向这个表的指针的内存。

解决问题:解决了继承、覆盖的问题。

 

问:虚函数与普通成员函数的区别?

答:虚函数有virtual关键字,有虚指针和虚函数表。虚指针就是虚函数的接口,而普通成员函数没有。

 

问:内联函数和构造函数能否为虚函数?

答:两者都不能为虚函数。

 

问:什么是函数的早绑定和晚绑定?

答:多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定(也属于动态绑定)。所以早绑定是非多态的函数地址绑定,该函数地址在编译期间是固定的。而固定的地址将始终调用到同一个函数,这就无法实现一个接口。虚函数就是个晚绑定,而普通函数是早绑定。

 

问:构造函数和析构函数的调用顺序?析构函数为什么要虚拟(设置为虚函数)?

答:构造函数的调用顺序:基类构造函数--对象成员构造函数--派生类构造函数;

析构函数的调用顺序与构造函数相反。

析构函数虚拟式为了防止析构不彻底,造成内存的泄露。

 

问:什么时候需要虚析构函数?

答:当基类指针指向用new运算符生成的派生对象时,delete基类指针时,派生类部分没有释放掉而造成释放不彻底对象,需要虚析构函数。

 

问:什么函数不能声明为虚函数?

答:构造函数。

 

问:怎样消除多重继承中的二义性?

答:

  1. 成员限定符
  2. 虚基类

 

问:什么是上转型对像,主要作用是什么?

答:假设A类是B类的父类,当用子类创建一个对象,并把这个对象的引用放到父类的对象中,例如:

A  a;

a=new B();

A  a;

B b=new B();

a=b;

这时,称对象a是对象b的上转型对象。对象的上转型对象的实体是子类负责创建的,但上转型对象会失去原对象的一些属性和功能。

上转型对象具有如下特点:

  1. 上转型对象不能操作子类新增的成员变量(失去了这部分属性),不能调用子类新增的方法(失掉了这一部分的行为)。
  2. 2)上转型对象可以访问子类继承或隐藏的成员变量,也可以调用子类继承的方法或子类重写的实例方法。上转型对象操作子类继承的方法或子类重写的实例方法,起作用等价于           子类去调用这些方法。因此,如果子类重写了父类的某个实例方法后,当对象的上转型对象调用这个实例方法时一定是调用了子类重写的实例方法。
  3. 如果子类重写了父类的静态方法,那么子类对象的上转型对象不能调用子类重写的静态方法,只能调用父类的静态方法。

 

问:以下关于类模板描述正确的是()

A. 模板参数只能使用class定义

B. 类模板中不能定义静态数据成员

C. 模板参数只能是类类型

D. 模板参数名不能与模板中自定义类型或类的名字相同

答:D

分析:本题中3个选项是对模板参数的描述。对于模板参数,如果参数是类类型,可以使用class或者typename关键字类定义,所以选项A是不正确的,模板参数除了是类类型,还可以是其他数据类型,例如整型。例如,类模板中需要根据模板参数确定缓存的大小,就需要设计一个整型的模板参数,所以选项C也是不正确的。在类模版中可以像普通类一样定义静态数据成员,使得某一类的实例化模板可以共享数据,因此选项B也是错误的。只有选项D是正确的。因为,如果参数名与模板中自定义类型同名,在类模板中定义的成员将无法确定具体是哪个类型。观察代码:

template <class Type>

 class CList

 {

 private:

  typedef int Type;

  Type *m_pHeader;

 };

猜你喜欢

转载自blog.csdn.net/liubing8609/article/details/86137626
今日推荐