C++面试题(1)

193 阅读5分钟

image.png

(欢迎大家关注我的微信公众号——控制工程研习,上面会分享很多我学习过程中总结的笔记。)

在寻求C++开发的相关工作,面试时会遇到各种各样的基础题,这些题常被人称为八股文,也即面经。所以我在网络上(牛客网等)搜罗了一些面经进行学习,一方面加强基础的学习,另一方面分享给大家。

1.介绍一下C++的多态

    多态性(polymorphism)可以简单地概括为“一个接口,多种方法”,它是面向对象编程领域的核心概念。多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。

    C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。也就是指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:

(1)编译时多态性(静态多态):通过重载函数实现:先期联编 early binding

(2)运行时多态性(动态多态):通过虚函数实现 :滞后联编 late binding

    多态的目的:封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了 “接口重用” 。也即,不论传递过来的究竟是类的哪个对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。

1.1 编译时多态性(静态多态)

    重载是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者都不同。编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的预处理函数,来实现同名函数调用时的重载问题。但这并没有体现多态性

    这里举个例子,内容是类成员函数area()根据不同的参数输入调用同名但是实现不一样:

#include <iostream> 
using namespace std;
class Shape 
{
protected:    
    int width, height;
public:    
    Shape(int a = 0, int b = 0)    
    {        
        width = a;        
        height = b;    
    }    
    int area(int a,int b)
    {        
        cout << "area 1." << endl;        
        return a * b;    
    }    
    int area(int a)
    {        
        cout << "area 2." << endl;        
        return a * a;    
    }
};
int main(){    
    Shape shape;
    cout << shape.area(1, 2) << endl;    
    cout << shape.area(3) << endl;
    return 0;
}    

结果:

图片

1.2 运行时多态性(动态多态)

    C++运行时多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(Override) ,或者称为重写

   下面举一个例子来便于大家理解。这个例子中创建了一个基类Shape,然后有两个子类Rectangle和Triangle都继承了Shape基类,并且重构了成员函数area(),所以这里就是希望在调用area()函数时会根据不同的类调用不同的成员函数,从而实现多态的效果:

#include <iostream> 
using namespace std;

class Shape {
protected:
    int width, height;
public:
    Shape(int a = 0, int b = 0)
    {
        width = a;
        height = b;
    }
    int area()
    {
        cout << "Parent class area :" << endl;
        return 0;
    }
};
class Rectangle : public Shape {
public:
    Rectangle(int a = 0, int b = 0) :Shape(a, b) { }
    int area()
    {
        cout << "Rectangle class area :" << endl;
        return (width * height);
    }
};
class Triangle : public Shape {
public:
    Triangle(int a = 0, int b = 0) :Shape(a, b) { }
    int area()
    {
        cout << "Triangle class area :" << endl;
        return (width * height / 2);
    }
};
// 程序的主函数
int main()
{
    Shape* shape;
    Rectangle rec(10, 7);
    Triangle  tri(10, 5);

    // 存储矩形的地址
    shape = &rec;
    // 调用矩形的求面积函数 area
    shape->area();

    // 存储三角形的地址
    shape = &tri;
    // 调用三角形的求面积函数 area
    shape->area();

    return 0;
}

但是运行结果如下,这说明并没有按照我们的意思执行。

图片

    导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。

    但现在,让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 virtual(即声明为虚函数),如下所示:

 (   注意:

    ①只有类的成员函数才能声明为虚函数,虚函数仅适用于有继承关系的类对象。普通函数不能声明为虚函数。

    ②静态成员函数不能是虚函数,因为静态成员函数不受限于某个对象。

    ③内联函数(inline)不能是虚函数,因为内联函数不能在运行中动态确定位置。

    ④构造函数不能是虚函数。

    ⑤析构函数可以是虚函数,而且建议声明为虚函数。)

class Shape {
protected:
    int width, height;
public:
    Shape(int a = 0, int b = 0)
    {
        width = a;
        height = b;
    }
    virtual int area()
    {
        cout << "Parent class area :" << endl;
        return 0;
    }
};

修改后,当编译和执行前面的实例代码时,它会产生以下结果:

图片

    从这个结果可以看出来,每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。有了多态,可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。

2.函数返回值类型不同也能实现重载吗?

    仅仅根据函数返回值是不能重载函数,仅能根据不同的形参列表,包括参数个数和类型。

    例如下面两个函数如果不调用函数的返回值就无法知道调用的是哪一个函数。

void func(void);
int func(void);

这是一个百度C++面试的题目,这里解释的比较冗长,代码是为了帮助大家理解。多态是C++的一个重要特性,所以要深入的理解。实际这里还包含了其他几个面试问题的回答:

· 运行时多态 与 编译时多态的区别?

· C++的静态多态是怎么做到的?为什么函数名相同却可以调到不同的实现呢?