面向对象程序设计问答题OOP12-13多态


1.什么是纯虚函数?什么是抽象数据类型(ADT)?抽象类的作用是什么?抽象类是否可实例化?抽象类的什么样子类可以实例化?

  • 纯虚函数: 一个函数只有函数名和形参列表,没有具体实现;语法:virtual double GetArea()=0;

    如果父类中 有了 纯虚函数 子类继承父类,就必须要实现 纯虚函数
    如果父类中 有了 纯虚函数 ,这个父类 就无法实例化对象了

  • 抽象数据类型: 抽象数据类型($Abstract$ $Data$ $Type$,$ADT$)是将数据对象、数据对象之间的关系和数据对象的基本操作封装在一起的一种表达方式,它和工程中的应用是一致的。在C++中可以用类的声明表示抽象数据类型,用类的实现来实现抽象数据类型的具体操作
  • 抽象类的作用: 抽象类可以用来表明某种软件设计需求。
  • 抽象类不可实例化,但可以派生
  • 当继承一个抽象类的时候,必须实现所有的纯虚函数,即可以实现实例化,否则由抽象类派生的类也是一个抽象类

2. 对于函数调用,什么是前期绑定(Early Binding,又称为静态联编)?什么是后期绑定(Late Binding,又称为动态联编)?重载函数是后期绑定吗,如果不是为什么?

  • 前期绑定: 绑定工作在编译连接阶段完成的,因为绑定过程是在程序开始执行之前进行的,因此也称为早期绑定或前绑定。在编译、连接过程中,系统就可以根据类型匹配等特征确定程序中操作调用与执行该操作代码的关系,即确定了某一个同名标识到底是要调用哪一段程序代码。
  • 后期绑定: 绑定工作在程序运行阶段完成的。
class A {
public:
     virtual void Get( );
};
class B : public A{ 
public:
    virtual void Get( );  
};

void MyFunction( A * pa ){
    pa->Get( );
}
  • 重载函数不是后期绑定,是静态绑定,原因: C++代码在编译时会根据参数列表对函数进行重命名,从而实现函数重载。

3. 要让一个函数调用表现出多态特征,必须满足哪些条件?

  • 是父、子类的函数,即存在继承体系结构
  • 父类中标为$virtual$的成员函数(虚函数),且为名字覆盖(重置)关系(不是重载)。
  • 通过父类的指针或是引用指向或引用一个对象,并调用虚函数。

4. 简述虚函数动态绑定的实现原理。

  • 构造函数中为对象的虚指针赋值,通过多态类型的指针或引用调用成员函数时,通过虚指针找到虚表,进而找到所调用的虚函数的入口地址,通过该入口地址调用虚函数。

动态绑定的实现原理

举例

5. 什么是隐藏(hiding)、覆盖(overriding)、重载(overloading)?对比它们的异同?以C++代码为例进行说明。

  • 名字隐藏:

名字隐藏

  • 名字覆盖:

名字覆盖

  • 名字重载:

名字重载

6. 什么是多态?

  • 多态是一种运行时绑定机制($run-time$ $binding$) ,通过这种机制,实现将函数名绑定到函数具体实现代码的目的。

7. dynamic-cast的作用是什么?试举例说明。

dynamic-cast的作用

见$OOP1-003$面向对象概论问题$9$,此处为了方便将其复制过来

作用: 将基类的地址转换为派生类的地址,如果类型匹配则转换成功,否则转换失败,返回空地址。

转换原则:

  • 如果$p$是一个指向$B$类型的对象的指针,则转换成功;
  • 如果$p$不是一个指向$B$类型的对象的指针,则转换失败,返回一个空地址.
dynamic_cast<B *>(p);
例子
#include <iostream>
using namespace std;

class A {
public :
    virtual void say() {
        cout << "Class A" << endl;
    }
};

class B : public A {
public :
    virtual void say() {
        cout << "Class B" << endl;
    }
};


class C : public A {
public :
    virtual void say() {
        cout << "Class C" << endl;
    }
};

int main() {
    int op;
    cin >> op;
    A *p;
    switch (op % 2) {
        case 0: p = new B(); break;
        case 1: p = new C(); break;
    }
    //判断p到底是什么对象
    //将p转换为目标地址B *
    cout << dynamic_cast<B *>(p) << endl;
    //将p转换为目标地址C *
    cout << dynamic_cast<C *>(p) << endl;
    return 0;
}

文章作者: 保底不歪抽早柚
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 保底不歪抽早柚 !
评论
  目录