8、多态与继承


1、多态

    多态是通过虚函数来实现的,也就是说虚函数是允许子类重新定义成员函数,而子类通过定义和父类一样的函数的方法,被成为覆盖或者是重写。

    多态的作用,使得代码可以重用,代码模块化;

函数重写:

    (1)子类定义的函数与父类原型相同的函数

    (2)函数的重写只有发生在父类和子类之间

class Parent 
{
public:
    void f()
    {
        cout << "Parent" << endl;
    }
};
class Child : public Parent
{
public:
    void f()
    {
        cout << "Child" << endl;
    }
};
int main()
{
    Child c1;
    c1.f();
    while (1);
}

 

    打印出子类的同名函数:Child,除非使用 c1.Parent::f(); 才会执行 父类的打印函数,一般的情况是,编译器会将父类的打印函数进行隐藏,使用的是子函数重写的函数去执行,

void run()
{
    Child c1;
    c1.f();
    Parent *pp = &c1;
    pp->f(); // 父类的打印
    Parent &ppp = c1;
    ppp.f(); // 父类的打印
}
int main()
{
    run();
    while (1);
}

打印出来:

Child
Parent
Parent

    打印结果,居然和分析不一样,原因是:C和C++是静态编译型语言(编译器会根据指针的类型去判断执行的是一个什么用的对象),所以,我们的指针和引用的是 是 Parent,所以执行的时候,输出的结果是父类的打印函数。言而总之,总而言之,我们就看指针,指针是什么类型。

多态的本质:

    通过添加 virtual 关键字对多态进行支持,将会被子类进行重写的函数,前面加上 virtual 关键字,

class Parent 
{
public:
    virtual void f()
    {
        cout << "Parent" << endl;
    }
};
class Child : public Parent
{
public:
    virtual void f()
    {
        cout << "Child" << endl;
    }
};
void run()
{
    Child c1;
    c1.f();
    Parent *pp = &c1;
    pp->f();
    Parent &ppp = c1;
    ppp.f();
}
这三个打印输出的就是: child child child

    只要在会被进行函数重写的函数假如 virtual 关键字,那么这个函数就是虚函数,到具体使用的时候,就会根据实际的情况进行打印输出。传入的是什么类型的,打印的就是什么类型的。

重载和重写的区别:什么时候重载、什么时候重写

    (1)重载:

    A 是同时存在多个同名的函数,但是在参数的个数,参数的类型、参数的顺序存在区别,

    B 事实上,重载只能发生在一个类里面,继承是不能实现函数的重载。

    C 原理上,C++编译器根据参数的不同,重新生成函数名,所以不同的参数会生成不同的函数名,所以本质上就是不同的函数了,也就是说编译器在编译的时候,就已经可以区分到底调用的是哪一个重载的函数,编译器早早已经确定,这些函数的编译的时候的地址就已经是被绑定了(早绑定)。

    (2)重写:

    A 是指子类重新定义父类的虚函数。子类重新定义了父类的虚函数,

     B 必须是发生在父类和子类之间。 父类和子类中的函数,必须完全相同的原型

    C 使用 virtual 可以产生多态

    D 多态是在运行的期间跟根据具体的对象的类型决定调用函数。

将所有的函数都加上 virtual 关键字:

    完全没有必要,这就涉及到虚函数的实现编译器将包含了 virtual 的虚函数的函数信息,存放到虚函数表里面,每次运行的时候,都会去虚函数表里面进行比对,看看是不是虚函数,如果是的话,那么就使用虚函数表里面的函数,不是的话,就去运行类内部其他的函数,所以不要将所有的函数全部设置为虚函数,会造成大量的浪费,处于效率考虑的话,其实就不要全部设置为虚函数。

纯虚函数:

    是一种特殊的虚函数,在基类中(父类)不能对虚函数给据有意义的实现,而把他声明为纯虚函数,它的实现则留给子类去做,这就是纯虚函数的作用,

    纯虚函数在父类只做函数原型的声明,而故意不定义函数体的虚函数,函数的定义等子类去完成(必须),这样就完成了纯虚函数的作用。

class Shape 
{
public: // 纯虚函数声明,没有实际的意义
    virtual double area() = 0;
};
class Rectangle : public Shape
{
    double a;
    double b;
public:
    Rectangle(double a, double b)
    {
        this->a = a;
        this->b = b;
    }
    // 函数的重写,纯虚函数重写
    double area()
    {
        return a * b;
    }
    
};
void area(Shape *s)
{
    cout << s->area() << endl;
}
void run()
{
    Rectangle rectangle(3, 4);
    cout<<"area is "<<rectangle.area()<<endl;
    area(&rectangle); // 可以使用指针
    //Shape shape; // 不能定义抽象类
}

   看到纯虚函数的声明是,virtual 函数 = 0; 告诉编译器,这个只是声明,所以必须在子类去实现这个函数;注意到: 抽象类(shape,包含了一个纯虚函数的类)已经是不能定义对象了,但是仍然可以使用指针。

注意:

    不要将多态应用于数组当中,

多重继承:

    多重继承就是 子类既继承自父类1,同时也继承父类2。实际的应用中,是不使用多重继承,

原文地址:https://www.cnblogs.com/qxj511/p/5217594.html