c++ 多态 运行时多态和编译时多态_C++学习刷题18--多态性的实现

一、前言

本部分为C++语言刷题系列中的第18节,主要讲解这几个知识点:多态性的概念、虚函数与覆盖函数、虚析构函数、纯虚函数与抽象类。欢迎大家提出意见、指出错误或提供更好的题目!

二、知识点讲解

知识点1:多态性的概念

· 在C++语言中,多态性是指对相同名称函数的调用,在不同情况下能调用不同的实现函数。

· 通过对重载函数的调用实现的多态性,称为静态多态性。在程序编译阶段,编译器通过实参匹配形参的方式确定具体调用哪个函数,故也称为编译时的多态性

· 通过基类指针或引用调用虚函数实现的多态性,称为动态多态性。在程序运行阶段,基类指针指向哪个派生类对象,则调用哪个对象的覆盖函数,故也称为运行时的多态性

知识点2:虚函数与覆盖函数(更深的理解可看后续的文章)

· 在基类中,被关键字virtual修饰的成员函数,都称为虚函数

· 在派生类中,只要定义的成员函数与基类的虚函数原型相同,都称为覆盖函数需要注意的是,派生类中覆盖函数的virtual关键字可以省略

· 通过基类指针或引用调用虚函数时,当指针指向派生类对象,则调用派生类的成员函数;当指针指向基类对象时,则调用基类的成员函数。

知识点3:虚析构函数

通过基类指针释放派生类对象时,需要将基类的析构函数设置为虚函数。否则,仅清除基类的内容即仅执行基类的析构函数,而不执行派生类的析构函数。

知识点4:纯虚函数与抽象类

· 当基类无法(或没有必要)提供虚函数的实现的实现时,可以将虚函数设置成纯虚函数。

· 将虚函数声明为纯虚函数的格式:virtual void print() = 0;

· 包含纯虚函数的类为抽象类。抽象类不能被实例化,并且派生类中没有提供纯虚函数的实现时,该派生类仍然为抽象类。

三、试题解析

★☆☆☆☆

1.下列关于多态性的描述,错误的是( )。

A.C++语言中的多态性分为编译时的多态性和运行时的多态性

B.编译时的多态性可通过函数重载实现

C.运行时的多态性可通过模板和虚函数实现

D.实现运行时多态性的机制称为动态绑定

正确答案:C

解析:编译时的多态性是通过函数重载和模板体实现的,运行时的多态性是通过虚函数实现的。

★★☆☆☆

2.已知类XX中声明了如下的公有虚函数:

virtual void f()const;

XX的派生类YY覆盖了这个虚函数,XX和YY都有默认的构造函数,且有如下定义:

YY yy;

XX xx, *px=&xx, &rx=yy, *pp=&yy;

则下列对函数f的调用中,属于非多态调用的是( )。

A.px->f()

B.rx.f()

C.xx.f()

D.pp->f()

答案:C

解析:在C++语言中,多态调用是指通过基类的指针或引用调用虚函数。已知函数f()是虚函数,故关键是判断哪些选项是基类的指针或引用。因此,选项A、B、D都满足这一条件,所以本题正确答案为C。

★★★☆☆

3.有如下程序:

#include

using namespace std;

class Vehicle

{

public:

virtual int wheels()const{ return 0; }

};

class Car: public Vehicle

{

public:

int wheels() const{ return 4; }

};

void f1(Vehicle v){ cout<< v.wheels()<

void f2(Vehicle &v){ cout<

void f3(Vehicle *pv){ cout<wheels()<

void main()

{

Car c;

f1(c); f2(c); f3(&c);

}

运行后的输出结果是( )。

A.4 4 4

B.0 4 4

C.4 0 4

D.4 4 0

答案:B

解析:本题与第2题的知识点一样,但考查的形式不同。考查通过基类的指针或引用调用虚函数时,调用指针所指向派生类的成员函数。但如果是基类对象调用成员的话,则不管调用的是虚函数或非虚函数,都只能调用基类中的成员。函数f2的形参是基类指针,实参是派生类对象,则调用派生类中的成员,输出为4。函数f2的形参是基类引用,实参是派生类对象,则调用派生类中的成员,输出为4。而函数f1的形参按值传递,当以派生类对象为实参调用f1时,会创建一个基类对象副本,以基类对象调用时只能是基类中的成员,输出0。因此,最终输出为0 4 4,选项B正确。

★★★☆☆

4.有如下程序:

#include

#include

using namespace std;

class Instrument

{

public:

Instrument(){ }

virtual string GetType() const { return "AA"; }

virtual string GetName() const { return "BB"; }

};

class Piano: public Instrument

{

public:

Piano(){ }

string GetType() const { return "CC"; }

string GetName() const { return "DD"; }

};

void main()

{

Instrument *pi=new Piano();

cout<GetType()<GetName();

delete pi;

}

运行时的输出结果是( )。

A. CC-DD

B. CC-BB

C. AA-BB

D. AA-DD

正确答案:A

解析:本题考查的是标准动态多态性的知识,即通过基类指针调用虚函数。指针pi是基类的指针,其调用的函数GetType()和GetName()都是虚函数,并且派生类中都提供了覆盖函数。因此,运行时调用哪个类中的函数要看指针pi具体指向哪个类的对象。本题指针pi指向派生类对象,因此输出结果为:CC-DD,选项A正确。

★★★★☆

5.有如下程序:

#include

#include

using namespace std;

class Instrument

{

public:

Instrument(){ }

string GetType() const { return "AA"; }

virtual string GetName() const { return "BB"; }

};

class Piano: public Instrument

{

public:

Piano(){ }

string GetType() const { return "CC"; }

string GetName() const { return "DD"; }

};

void main()

{

Instrument *pi=new Piano();

cout<GetType()<GetName();

delete pi;

}

运行时的输出结果是( )。

A. CC-DD

B. CC-BB

C. AA-BB

D. AA-DD

正确答案:D

解析:本题与第4题看起来差不多,但更具迷惑性。因为同时考查了基类指针调用虚函数和非虚函数两种情况:其一通过基类指针调用非虚函数(pi->GetType()),只能调用基类中的成员,故先输出AA。其二通过基类指针调用虚函数(pi->GetName()),调用指针所指向的派生类中的成员,故后输出DD。因此,最终输出结果为:AA-DD,选项A正确。

★★★☆☆

6.下列程序执行后,输出结果是:

#include

class A

{

public:

~A() { cout<

};

class B : public A

{

public:

~B() { cout<

};

void main()

{

A *pA=new B;

delete pA;

}

正确答案:cout<

解析:正常释放派生类对象时,都会先执行派生类的析构函数,后执行基类的析构函数。但是,当通过基类指针释放派生类对象时,如果基本的析构函数不是虚函数,则只会释放基类的内容,即仅执行基本的析构函数,不执行派生类的析构函数。特别提醒:在这种情况下,应该将基类析构函数设置为虚析构函数。

7.有如下程序:

#include

using namespace std;

class Instrument

{

public:

virtual void Display()=0;

};

class Piano: public Instrument

{

public:

void Display() { /*函数体略*/ }

};

void main()

{

Instrument s;

Instrument *p=0;

//… ;

}

下列叙述中正确的是( )。

A.语句"Insturment *p=0;"编译时出错

B.语句"Instrument s;"编译时出错

C.类Piano中的Display函数不是虚函数

D.类Instrument是一个虚基类

正确答案:B

答案解析:本题考查纯虚函数与抽象类。纯虚函数在声明虚函数时被"初始化"为0,而包含纯虚函数的类为抽象类,抽象类不能被实例化,即不能由抽象类创建对象,可以用来定义指针或引用。所以语句"Instrument s;"在编译时出错,选项B正确。

四、试题测试

1.下列关于虚函数的说明中,正确的是( )。

A.从虚基类继承的函数都是虚函数

B.虚函数不得是静态成员函数

C.只能通过指针或引用调用虚函数

D.抽象类中的成员函数都是虚函数

2.有如下程序:

#include

using namespace std;

class Base

{

public:

void fun1() {cout<

virtual void fun2() { cout<

};

class Derived : public Base

{

public:

void fun1() { cout<

void fun2() { cout<

};

void f(Base& b) { b.fun1(); b.fun2(); }

void main()

{

Derived obj;

f(obj);

}

执行这个程序的输出结果()

A.Base

Base

B.Base

Derived

C.Derived

Base

D.Derived

Derived

3.有如下程序段:

#include

using namespace std;

class Base

{

public:

Base(int cnt){ resource=new int[cnt]; }

~Base(){ delete [] resource; }

virtual void show(){ cout<

int *resource;

};

class Derived: public Base

{

public:

Derived(int cnt):Base(cnt){ resource=new float[cnt]; }

~Derived(){ delete [] resource; }

void show(){ cout<

float *resource;

};

上述程序使得derived对象中的动态内存空间有可能不被释放,有内存泄漏风险,原因是( )。

A.base类是抽象类

B.derived类的析构函数没有释放基类的动态内存

C.base类的析构函数不是虚函数

D.derived类的resource与公有继承得到的resource同名

4. 有如下类定义:

class Shape

{

public:

___________________ //纯虚函数Draw的声明

};

横线处缺失的纯虚函数 Draw 的声明是( )。

A. void Draw()=0;

B. virtual void Draw()=0;

C. virtual void Draw() { }

D. virtual void Draw(int=0);

------------------------------

欢迎大家提出意见、指出错误或提供更好的题目,只有大家的共同努力,才能帮助更多人掌握C++的基本概念,顺利通过考试!

705f246e5d6487703054d80ccf1d5378.png
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值