一、选择题

1、下列关于动态联编的描述中,错误的是(D)。

A.动态联编是以虚函数为基础

B.动态联编是运行时确定所调用的函数代码的

C.动态联编调用函数操作是指向对象的指针或对象引用

D.动态联编是在编译时确定操作函数的

3、在派生类中重新定义虚函数时必须在(A,B,E)方面与基类保持一致。(多选题)

A.参数个数  B.参数类型 C.参数名字 D.操作内容 E.返回类型

5、C++类体系中,能被派生类继承的是(B)。

A.构造函数 B.虚函数 C.析构函数 D.友元函数

6、关于虚函数的描述中,正确的是(D)。

A.虚函数是一个静态成员函数

B.虚函数是一个非成员函数

C.虚函数即可以在函数说明定义,也可以在函数实现时定义

D.派生类的虚函数与基类中对应的虚函数具有相同的参数个数和类型

7、下面4个选项中,(A)是用来声明虚函数的。

A.virtual B.public C.using D.false

8、编译时的多态性可以通过使用( B )获得。

A.虚函数和指针 B.重载函数和析构函数 C.虚函数和对象 D.虚函数和引用

9、关于纯虚函数和抽象类的描述中,错误的是( C )。

A.纯虚函数是一种特殊的虚函数,它没有具体的实现

B.抽象类是指具有纯虚函数的类

C.一个基类中声明有纯虚函数,该基类派生类一定不再是抽象类

D.抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出

10、下列描述中,( D )是抽象类的特征。

A.可以声明虚函数 B.可以进行构造函数重载 C.可以定义友元函数 D.不能声明其对象

11、以下( C )成员函数表示虚函数。

A.virtual int vf(int);B.void vf(int)=0;C.virtual void vf()=0; D.virtual void vf(int) { };

12、如果一个类至少有一个纯虚函数,那么就称该类为( A)。

A.抽象类 B.虚函数 C.派生类 D.以上都不对

13、要实现动态联编,必须通过(A)调用虚函数。

A.对象指针 B.成员名限定 C.对象名 D.派生类名

14、下面描述中,正确的是(A,B,)。

A.virtual可以用来声明虚函数

B.含有纯虚函数的类是不可以用来创建对象的,因为它是虚基类

C.即使基类的构造函数没有参数,派生类也必须建立构造函数

D.静态数据成员可以通过成员初始化列表来初始化

二、写出下列程序的输出结果。

1、分析下列程序的输出结果。

#include <iostream>using std::endl;
using std::cout;class A
{
public:A() { cout << "A's cons." << endl; }virtual ~A() { cout << "A's des." << endl; }virtual void f() { cout<<"A's f()."<<endl; }void g() { f();}};class B
: public A
{
public:B() { f(); cout << "B's cons." << endl; }~B() { cout << "B's des." << endl; }};class C
: public B
{
public:C() { cout<<"C's cons."<<endl; }~C(){ cout<<"C's des."<<endl;}void f() { cout<<"C's f()."<<endl; }};
int  main(void)
{  A *pa=new C();pa->g();delete pa;return 0;}

运行结果

A's cons. //A *pa = new C(); 先指向A的构造函数,再指向C的父类B的构造函数,最后C
A's f().
B's cons.
C's cons.
C's f().
C's des.
B's des.
A's des.

2、根据给定的程序执行结果,将下列程序补充完整。

#include <iostream>using std::endl;
using std::cout;class Base
{
public:Base(int i) {b = i;}(1)   
protected:int b;
};class Derive1
:public Base
{
public:(2)   void Print(){cout<<"Derive1's Print() called."<<endl;}
};class Derive2
:public Base
{   (3)
};void fun(  (4)  )
{      obj->Print();
}int main(void)
{ (5)   fun(d1);fun(d2);return 0;}
程序的执行结果如下:
Derive1's Print() called.
Derive2's Print() called.

(1)virtual void Print()=0;
(2)Derive1(int i=0):Base(i){}
(3)
Derive2(int i=0):Base(i){}
void Print()
    {
        cout<<"Derive2's Print() called."<<endl;
    }
(4)Base *obj
(5)
Derive1 derive1;
Derive2 derive2;
Base *d1 = &derive1;
Base *d2 = &derive2;

3、根据给定的程序执行结果

#include <iostream>
using std::endl;
using std::cout;class A
{
public:A(int i,int j) {a=i;b=j;}void move(int i,int j) {a += i;b += j;}void disp() { cout << "(" << a << "," << b << ")" << endl;}
private:int a,b;
};class B
: public A
{
public:B(int i,int j,int k,int l):A(i,j),x(k),y(l){ }void disp() {cout << x << "," << y << endl;}void fun() {move(3,5);}private:int x,y;
};int main()
{A m(1,2);m.disp();B d(3,4,5,6);d.fun();d.A::disp();d.disp();return 0;}

(1,2)
(6,9)
5,6

4、根据给定的程序执行结果

#include<iostream>using std::endl;
using std::cout;class Base1
{
public:virtual void fun()       {   cout<<"--Base1--\n";  }
};class Base2
{
public:void fun()                   {   cout<<"--Base2--\n"; }
};class Derived
:public Base1
,public Base2
{
public:void fun(){   cout<<"--Derived--\n";  }
};int main()
{Base1 obj1, *ptr1;   Base2 obj2, *ptr2;    Derived obj3; ptr1=&obj1;          ptr1->fun(); //Base1ptr2=&obj2;             ptr2->fun(); //Base2ptr1=&obj3;             ptr1->fun(); //Derivedptr2=&obj3;  //Base2          ptr2->fun(); return 0;                     }

运行结果

--Base1--
--Base2--
--Derived--
--Base2--

解析

Base2中的fun函数不是虚函数,Derived对Base2中的fun函数是隐藏不是重写,故编译器根据指针类型确定调用的函数。用Base2类型的对象指针去访问,会执行Base2中的fun()

5、写出下列程序的结果:

class A
{
public:void FuncA(){printf( "FuncA called\n" );}virtual void FuncB(){printf( "FuncB called\n" );}
};class B
: public A
{
public:void FuncA(){A::FuncA();printf( "FuncAB called\n" );}virtual void FuncB(){printf( "FuncBB called\n" );}
};int main( void )
{B  b;A *pa;pa = &b;A *pa2 = new A;pa->FuncA(); //FuncA calledpa->FuncB(); //FunBB calledpa2->FuncA();//FuncA calledpa2->FuncB();//FUncB calleddelete pa2;return 0;}

FuncA called
FuncBB called
FuncA called
FuncB called

6、写出下列程序的结果:

class Base
{
public:Base(int j): i(j) {}virtual  ~Base() {}void func1() {i *= 10;func2();}int getValue(){return  i;}protected:virtual void func2(){i++;}protected:int i;
};class Child
: public Base
{
public:Child(int j): Base(j) {}void func1(){i *= 100;func2();}protected:void func2(){i += 2;}
};
int main()
{Base * pb = new Child(1);pb->func1();cout << pb->getValue() << endl; //12delete pb; return 0;
} 

运行结果

12

参考解析:

Base中的func1是非虚函数,Child中的func1是对的Base中的func1的隐藏,通过Base类型对象指针去访问,是访问的Base的func1。而在func1中访问func2时,实际是this->func2(),由于指针指向的是Child类的对象,故会触发多态,访问的是Child中重写的func2。

7、写出下面程序的结果

class A
{
public:virtual void func(int val = 1){cout << "A->" << val << endl;} virtual void test(){func();}
private:long _a;
};class B
: public A
{
public:virtualvoid func(int val = 10){cout << "B->" << val << endl;}
private:long _b;
};
int main(void)
{B b;A *p1 = (A*)&b;B *p2 = &b;p1->func();//A->1p2->func();//B->10return 0;
}
B->1
B->10

三、简答题

1.什么是多态?虚函数的实现原理是什么?

多态:同一个操作,作用于不同的对象,可以有不同的解释,会产生不同的效果。

虚函数的实现原理:
解释版本一-黑马:
1.编译器发现类中有虚函数的时候,会创建一张虚表,表中存放了类中所有虚函数的入口地址。

2.编译器会在类中安插一个虚表指针,指针指向本类中的虚函数表。

3.继承了虚函数的派生类都有自己的虚函数指针------指针是从基类继承下来的,编译器为了初始化从基类继承过来的虚函数表指针,编译器在构造函数中添加了初始化虚函数指针的代码,让从父类继承过来的虚函数表指针指向了子类自己的虚函数表。

4.当编译器发现子类重写了父类中的虚函数时,那么子类重写的函数会覆盖掉父类的函数

5.基类的指针指向派生类对象的时候,再用该指针去调用虚函数,编译器根据当前指针的内存空间位置来找到函数的入口地址来调用函数,实现了多态。

解释版本二:
1.当基类定义了虚函数后,会在基类对象的存储布局前面产生一个虚函数指针,该虚函数指针指向基类的虚函数表,虚函数表里存储的是类中各虚函数的入口地址。

2.派生类继承基类的时候,吸收基类的虚函数,产生自己的虚函数指针,指针指向自己的虚函数表,该虚表存放的是派生类自己的虚函数入口地址,派生类对基类虚函数重定义后,把虚表中的虚函数的入口地址进行替换。

2.不能设置为虚函数的函数有哪些?构造函数能设置为虚函数吗?为什么

1.普通函数:普通成员函数无继承机制,无多态。发生在编译时。

2.静态成员函数:不依赖于对象而存在,每个类共用一个。发生在编译时。

3.内联成员函数:在编译时在代码中直接展开,而多态在运行时动态联编。

4.构造函数:构造函数不可继承,虚函数要能够继承。语义上也完全不合,虚函数是动态联编,运行前动作是不明确的,而构造函数是明确的

5.友元函数:将普通函数声明为友元,则由于是普通函数就不是虚函数。当友元函数是成员函数时,则可以作为其所在类的虚函数。但总之,友元关系与虚函数无关。

构造函数不能为虚函数,因为构造函数是用于初始化的,而虚函数的目的是为了在完全不了解细节的情况下处理对象的,如果将构造函数定义为虚函数,此时连对象都没有,因此不能。

3.在什么情况下析构函数要设置成虚函数?为什么?

一般来说,如果类中定义了虚函数,析构函数也应被定义为虚析构函数,尤其是类内有申请的动态内存,需要清理和释放的时候。

当基类的指针指向申请了动态内存的派生类指针,在释放的时候如果把基类的析构函数设置为虚函数,delete的时候,会先去调用派生类的析构函数,再去调用基类的析构函数,才能够完全释放。

原因:基类的析构函数被设为虚函数之后,派生类的析构函数自动变成了虚函数,编译器认为此时也是一种重写,会将基类的析构函数解释为destructor,派生类只要重定义了虚函数,也会解释为destructor,这种重写是名字不一样的重定义,之所以可以这么做就是因为对于任何一个类而言,一个类只有一个析构函数,具有唯一性。

4.什么是纯虚函数?什么是抽象类?抽象类的作用是什么?

1.纯虚函数是一种特殊的虚函数,是一种不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现就留给该基类的派生类去实现。

2.抽象类是类中含有至少一个纯虚函数,也称为抽象层。抽象层是连接起实现层和业务层的桥梁,面向对象的设计原则是开闭原则:对扩展开发,对修改关闭。

3.抽象层作为接口,只作声明,实现由派生类的实现层来完成。

5.什么是重载?什么是隐藏?什么是覆盖?他们之前的区别是?

1.重载:发生在同一个作用域内,函数名称相同,参数类型、个数、顺序不同(参数列表不同)

2.隐藏:发生在基类于派生类之间,派生类中的函数屏蔽了基类中的同名函数。如基类函数是非虚函数时,又或者派生类中有与基类中虚函数同名但参数列表或返回值不同的函数时,都会发生隐藏。

3.覆盖:发生在基类于派生类之间,同名虚函数,参数列表和返回值完全相同

C++学习之第十一天-多态、虚函数相关推荐

  1. 【C++ 语言】面向对象 ( 继承 | 重写 | 子类调用父类方法 | 静态多态 | 动态多态 | 虚函数 | 纯虚函数 )

    文章目录 类的继承 方法的重写 子类中调用父类方法 多态 虚函数 虚函数示例 纯虚函数 相关代码 类的继承 1. 继承表示 : C++ 中继承可以使用 ":" 符号 , 格式为 & ...

  2. C++ 多态 虚函数与纯虚函数

    C++ 多态 虚函数与纯虚函数 虚函数是C++重要思想-多态中不可或缺的一个知识点与用法,但初学者一般很难理解,在这里用通俗语言介绍一下. 百度百科: 在某基类中声明为 virtual 并在一个或多个 ...

  3. C++ 学习之旅(16)——虚函数与纯虚函数virtual

    关于虚函数的概念讲解,可以看这篇文章: https://blog.csdn.net/siwuxie095/article/details/71159414 以下用例子进行实际说明: #include ...

  4. C++多态虚函数/纯虚函数demo

    #include <iostream> using namespace std;class A //A是一个抽象类,不能new A直接实例化. { public:virtual void ...

  5. C++多态虚函数demo

    #include<iostream> #include<algorithm> using namespace std;class Base{ public:void Fun(i ...

  6. C++ 多态虚函数常见问题

    哪些函数不能为虚函数? 非类成员的普通函数 静态(static)函数 构造函数不能是虚函数 (存储角度)虚函数的vtable,是存储在对象的内存空间的.对象没有实例化,意味着内存空间还没有,所以无法找 ...

  7. 《C++捷径教程》学习笔记【一】:虚函数

    声明虚函数的方法是在基类中的成员函数原型前加上关键字virtual.格式如下: class 类名{ -- virtual 类型 函数名(参数表): -- }: 当一个类的成员函数声明为虚函数后,这就意 ...

  8. 【C++】多态 - 虚函数/虚析构函数以及虚函数表

    什么是多态: 指不同对象收到相同消息时或相同对象收到不同消息时产生不同的动作. 这里先说下为什么会用到虚函数: 以下面的程序为例: 这个程序中,Carp是Fish的继承类,而Carp中覆盖了Swim这 ...

  9. C++——多态|虚函数|重写|虚表

    文章目录 1. 多态的概念 1.1 概念 2. 多态的定义及实现 2.1多态的构成条件 2.2 虚函数 2.3虚函数的重写 虚函数重写的三个例外: 2.4 普通调用和多态调用: 2.5 C++11 o ...

最新文章

  1. 皮一皮:这样的消息我也想收...
  2. 初识-Android之智能短信项目相关技术整理
  3. HttpClientFactory与Steeltoe结合来完成服务发现
  4. DeePMD-kit的conda安装方法以及速度慢的解决方案
  5. 《Effective C#》快速笔记(三)- 使用 C# 表达设计
  6. MAVEN setting.xml配置文件详解
  7. 人类按什么动物的优点发明什么
  8. 仿照CIFAR-10数据集格式,制作自己的数据集
  9. matlab求取状态方程的传递函数并实现系统解耦
  10. Flask Uploads
  11. cad图纸怎么看懂_快速看懂cad图纸的教程全解
  12. apple pay,--牛逼,
  13. VBA入门到进阶常用知识代码总结47
  14. VUE.JS 实现图片随鼠标变换的动画效果
  15. gitlab self-hosted server
  16. Unity打包的PC项目生成一个EXE文件
  17. 决策模型(一):不确定型决策法
  18. 3月第一周总结(3.1~3.7)
  19. 不用穿越,也能体验百年前的老北京,这个AI修复视频火爆全网
  20. 分布式存储系统:4.容错

热门文章

  1. sqlserver查询前10分钟数据
  2. linux时间地区纽约,linux下修改时区(TIMEZONE)
  3. vn.py开源量化框架把我整蒙了,开始填坑。
  4. Java IO流 思维导图
  5. linux怎么查看当前的用户,linux怎么查看当前用户属于哪个用户组
  6. linux查用户group,Linux查看某个用户组下面的所有用户
  7. 软件测试流程及产出物
  8. python之mysql数据备份和恢复
  9. Vue << 拦截器(interceptors ) 过滤器(filter)
  10. SuSE Linux防火墙配置