C++的多态性
简单来说就是一个接口,多种方法。多态性是通过虚函数实现的,在基类的函数前加上virtual关键字,在派生类中重写该函数,运行的时候会根据对象的实际类型来调用相应的函数。如果对象是基类就调用基类的函数,对象是派生类就调用派生类的函数。
比如声明基类的指针,利用该指针指向随意一个子类对象,调用对应的虚函数,能够依据指向的子类的不同而实现不同的方法。假设没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用对应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。由于没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。
基类不含虚函数的情况
类A为基类,类B继承A,类C继承B,没有利用c++的多态性
class A {
public:
A() { cout << "in A()" << endl; }
void fun() { cout << "in A::fun()" << endl; }
~A() { cout << "in ~A()" << endl; }
};
class B : public A {
public:
B() { cout << "in B()" << endl; }
void fun() { cout << "in B::fun()" << endl; }
~B() { cout << "in ~B()" << endl;}
};
class C : public B {
public:
C() { cout << "in C()" << endl; }
void fun() { cout << "in C::fun()" << endl; }
~C() { cout << "in ~C()" << endl; }
};
main函数里面代码如下,新建一个派生类赋给基类实例:
A* test = new C();
test->fun(); //注意:这里调用的是A::fun()
delete test;
运行结果:
将main函数代码改成,新建一个派生类赋给派生类实例:
C* test = new C();
test->fun();
delete test;
运行结果:
基类包含虚函数的情况
class A {
public:
A() { cout << "in A()" << endl; }
virtual void fun() { cout << "in A::fun()" << endl; }
~A() { cout << "in ~A()" << endl; }
};
class B : public A {
public:
B() { cout << "in B()" << endl; }
void fun() { cout << "in B::fun()" << endl; }
~B() { cout << "in ~B()" << endl;}
};
class C : public B {
public:
C() { cout << "in C()" << endl; }
void fun() { cout << "in C::fun()" << endl; }
~C() { cout << "in ~C()" << endl; }
};
A* test = new C();
test->fun();
delete test;
运行结果:
但如果将A的析构函数变为虚函数,所有类的析构函数又都会被调用
virtual ~A(){ cout << "in ~A()" << endl; }
public继承和private继承
上面的例子都是public继承
如果将B对A的继承改成private继承,
class B : private A { //私有继承
public:
B() { cout << "in B()" << endl; }
void fun() { cout << "in B::fun()" << endl; }
~B() { cout << "in ~B()" << endl;}
};
main函数里就会显示出错
具体错误为
虚函数表
如果一个类中包含虚函数,那么这个类就会包含一张虚函数表,虚函数表存储的每一项是一个虚函数的地址。这个类的每一个对象都会包含一个虚指针指向虚函数表(虚指针存在于对象实例地址的最前面,保证虚函数表有最高的性能)。对象不包含虚函数表,只有虚指针,类才包含虚函数表,派生类会生成一个兼容基类的虚函数表。
原始基类的虚函数表
单继承时的虚函数表
没有重写基类函数的情况
重写了基类函数的情况
多重继承时的虚函数表
class C : public A, public B