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。实际的应用中,是不使用多重继承,