C++多态
Posted 蚍蜉撼树谈何易
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++多态相关的知识,希望对你有一定的参考价值。
一、多态的概念?
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态
二、多态的条件?
三、什么是重写?
派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数
条件:
1.基类的成员函数必须是虚函数
2.子类的成员函数可以不加virtual关键字,但一般建议加上
3.基类和子类的虚函数原型必须一致,(返回值类型,函数名字,参数列表),有两个例外,返回值为类型指针或引用时可以,第二个是析构函数。
4,重写与访问权限无关。
class Base
public:
virtual void f1()
cout << "Base::f1()" << endl;
virtual void f2()
cout << "Base::f2()" << endl;
void f3()
cout << "Base::f3()" << endl;
virtual void f4()
cout << "Base::f4()" << endl;
virtual void f5()
cout << "Base::f5()" << endl;
//virtual void f6()
//
// cout << "Base::f6()" << endl;
//
;
class Derived : public Base
public:
// 与基类f1原型完全相同
virtual void f1()
cout << "Derived::f1()" << endl;
// 与基类f2不同,缺少virtual关键
void f2()
cout << "Derived::f2()" << endl;
// 与基类中f3不同--->原型一致,但是基类f3不是虚函数
virtual void f3()
cout << "Derived::f3()" << endl;
// 与基类虚函数参数列表不同
virtual void f4(int a)
cout << "Derived::f4(int)" << endl;
// 与基类虚函数f6的返回值类型不同
//virtual int f6()
//
// cout << "Derived::f6()" << endl;
// return 0;
//
protected:
// 与基类f5虚函数的访问权限不同
virtual void f5()
cout << "Derived::f5()" << endl;
;
// pb指向一个子类的对象
// 如果重写成功了,调用的一定是子类的方法
void TestVirtual(Base* pb)
pb->f1();
pb->f2();
pb->f3();
pb->f4();
pb->f5();
//pb->f6();
int main()
Derived d;
TestVirtual(&d);
return 0;
两种成员函数不同仍旧构成重载的情况?
1.协变:
// 基类虚函数返回基类的指针或者引用
// 子类虚函数返回子类的指针或者引用
// 注意:"基类"虚函数 返回"基类"指针或者引用
// 这两个基类可以不是同一个继承体系
/ 协变:
// 基类虚函数返回基类的指针或者引用
// 子类虚函数返回子类的指针或者引用
// 注意:"基类"虚函数 返回"基类"指针或者引用
// 这两个基类可以不是同一个继承体系
class A
;
class B : public A
;
class Base
public:
virtual A* f1()
cout << "Base::f1()" << endl;
return nullptr;
;
class Derived : public Base
public:
virtual B* f1()
cout << "Derived::f1()" << endl;
return nullptr;
;
void TestVirtual(Base& b)
b.f1();
int main()
Base b;
Derived d;
TestVirtual(b);
TestVirtual(d);
2.析构函数
class Base
public:
Base()
cout << "Base::Base()" << endl;
// 基类的析构函数如果设置为虚函数
// 子类的析构函数只要显式给出,子类的析构函数就会将基类的析构函数重写
virtual ~Base()
cout << "Base::~Base()" << endl;
protected:
;
class Derived : public Base
public:
Derived()
: Base()
, _d(0)
p = new int[10];
cout << "Derived::Derived()" << endl;
~Derived()
delete[] p;
cout << "Derived::~Derived()" << endl;
protected:
int _d;
int* p;
;
int main()
Base* pb = new Derived;
/*
delete pb;
1. 调用析构函数
子类已经将基类的析构函数重写了,因此调用那个类的析构函数
就需要看pb实际指向的是那个类的对象
2. 调用operator delete(void* p)释放对象的空间
*/
delete pb;
return 0;
为什么在继承体系下,一般建议将基类析构函数设置为虚函数? ****
若基类的析构函数不为virtual修饰的话,若子类对象涉及到资源管理的话,此时将子类对象赋值给基类对象,此时若函数调用完成的话,只会调用基类的析构函数,而不会去调用子类的析构函数,这就会造成内存泄露的问题。
四、C++11中的override和final
override:单词含义 重写
作用:在编译阶段,检测子类虚函数是否对基类那个虚函数进行重写了
如果被override修饰的子类虚函数重写了基类的某个虚函数,编译通过
否则编译失败
override:只能修饰子类的虚函数
final:
// 1. 修饰虚函数—表明该虚函数不能被其子类重写了
// final一般用来修饰子类的虚函数,也可以修饰基类,不过没什么意义
// 2. 修饰类—>表明该类不能被继承了
// 需求:C类中的fun1这个虚函数不想被C的子类再重写了
class B
public:
virtual void fun1()final
cout << "B::fun1()" << endl;
;
class C : public B
public:
//virtual void fun1()//error
//
// cout << "C::fun1()" << endl;
//
virtual void fun2()
cout << "C::fun2()" << endl;
;
// 需求:D类要求:我不能再被继承
class D final : public C
public:
//virtual void fun1()
//
// cout << "D::fun1()" << endl;
//
virtual void fun2()
cout << "D::fun2()" << endl;
;
class E : public D//error
;
五、重载、覆盖(重写)、隐藏(重定义)的对比 ******
六、抽象类 **
包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。 (因为无法确定给多大的空间)派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象纯虚函数规范了派生类必须重写,另外 纯虚函数更体现出了接口继承
class Shape
public:
// 纯虚函数
virtual double CalcCircle() = 0;
// virtual double CaclArea() = 0;
;
// 三角形
class Triangle : public Shape
public:
Triangle(double a, double b, double c)
this->a = a;
this->b = b;
this->c = c;
// 获取周长
virtual double CalcCircle()
return a + b + c;
private:
double a;
double b;
double c;
;
// 矩形
class Rectangle : public Shape
public:
Rectangle(double l, double w)
: length(l)
, width(w)
virtual double CalcCircle()
return 2 * (length + width);
private:
double length;
double width;
;
// 子类必须重写抽象类中所有的纯虚函数,如果有一个纯虚函数没有重写
// 则子类也是抽象类
class Circle : public Shape
public:
Circle(double r)
this->r = r;
virtual double CalcCircle()
return 3.14 * r * r;
private:
double r;
;
void TestShape(Shape& s)
s.CalcCircle();
int main()
// Shape s;
Triangle t(3, 4, 5);
Rectangle r(2, 3);
Circle c(2);
TestShape(t); //
TestShape(r);
TestShape(c);
return 0;
接口继承与实现继承:
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。
虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。
七、多态原理
7.1什么情况下肯定会生成构造函数?
// 多态的实现原理
// 1. 包含有虚函数的类与不包含虚函数的类有什么区别?
// >> 带有虚函数的类会多4个字节
// >> 如果没有显式定义构造函数时,编译器一定会给带有虚函数的类合成构造函数
// >> 在构造函数中,编译器会给对象的前4个字节赋值
class B
public:
void f()
cout << "B::f()" << endl;
int _b;
;
// 多的4个字节是什么作用?
class D
public:
virtual void f1()
cout << "D::f1()" << endl;
virtual void f2()
cout << "D::f2()" << endl;
virtual void f3()
cout << "D::f3()" << endl;
int _d;
;
int main()
cout << sizeof(B) << endl;
cout << sizeof(D) << endl;
B b;
b._b = 10;
D d;
d._d = 10;
return 0;
class B1
public:
virtual void f1()
cout << "B1::f1()" << endl;
virtual void f2()
cout << "B1::f2()" << endl;
int _b1;
;
class B2
public:
virtual void f3()
cout << "B2::f3()" << endl;
virtual void f4()
cout << "B2::f4()" << endl;
int _b2;
;
class D : public B1, public B2
public:
virtual void f1()
cout << "D::f1()" << endl;
virtual void f4()
cout << "D::f4()" << endl;
virtual void f5()
cout << "D::f5()" << endl;
int _d;
;
typedef void(*PVFT)();
void PrintVFT1(B1& b, const string& s)
cout << s << endl;
PVFT* pvft = (PVFT*)*(int*)&b;
while (*pvft)
(*pvft)();
++pvft;
cout << endl;
void PrintVFT2(B2& b, const string& s)
cout << s << endl;
PVFT* pvft = (PVFT*)*(int*)&b;
while (*pvft)
(*pvft)();
++pvft;
cout << endl;
int main()
cout << sizeof(D) << endl;
D d;
d._b1 = 1;
d._b2 = 2;
d._d = 3;
PrintVFT1(d, "D->B1->VFT:");
PrintVFT2(d, "D->B1->VFT:");
return 0;
八、总结
以上是关于C++多态的主要内容,如果未能解决你的问题,请参考以下文章