相关继承概念请参考:>继承相关概念<
虚函数&多态
- 虚函数 — 类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
- 虚函数重写 — 当在子类的定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。
多态的实现机制:
C++中虚函数的主要作用就是实现多态。简单说父类的指针/引用调用重写的虚函数,当父类指针/引用指向父类对象时调用的是父类的虚函数,指向子类对象时调用的是子类的虚函数。
这是通过虚函数表实现的,虚函数表通过一段连续的内存存放虚函数的地址,解决了虚函数重写(地址覆盖)问题。在有虚函数的对象实例中都有一张虚函数表,虚函数表就像一张地图,指明了具体调用哪个虚函数。
总结:
- 派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
- 基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
- 只有类的成员函数才能定义为虚函数。
- 静态成员函数不能定义为虚函数。
- 如果在类外定义虚函数,只能在声明函数时加virtual,类外定义函数时不能加virtual。
- 构造函数不能为虚函数,虽然可以将operator=定义为虚函数,但是最好不要将operator=定义为虚函数,因为容易使用时容易引起混淆。
- 不要在构造函数和析构函数里面调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为。
- 最好把基类的析构函数声明为虚函数。(因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里是因为编译器做了特殊处理)
多态
多态就是多种形态,C++的多态分为静态多态和动态多态。
静态多态就是重载,因为是在编译期确定,所以称为静态多态。
动态多态就是通过继承重写基类的虚函数实现的多态,因为是在运行时确定,所以称为动态多态。
静态联编与动态联编:
#include <iostream>
#include <stdio.h>
using namespace std;
class Base
{
public:
virtual void func1()
{
cout << "Base::func1()" << endl;
}
virtual void func2()
{
cout << "Base::func2()" << endl;
}
void func3()
{
cout << "Base::func3()" << endl;
}
void func4()
{
cout << "Base::func4()" << endl;
}
protected:
int _b;
};
class Derive : public Base
{
public:
virtual void func1()
{
cout << "Derive::func1()" << endl;
}
protected:
int _d;
};
void func(Base& b)
{
b.func1();
b.func2();
b.func3();
b.func4();
}
int main()
{
Base b;
Derive d;
func(b);
func(d);
return 0;
}
单继承对象模型
#include <iostream>
#include <stdio.h>
using namespace std;
class Base
{
public:
virtual void func1()
{
cout << "Base::func1()" << endl;
}
virtual void func2()
{
cout << "Base::func2()" << endl;
}
private:
int _a;
};
class Derive : public Base
{
virtual void func1()
{
cout << "Derive::func1()" << endl;
}
virtual void func3()
{
cout << "Derive::func3()" << endl;
}
void func4()
{
cout << "Derive::func4()" << endl;
}
private:
int _b;
};
typedef void(*FUNC)();
void PrintVTable(int* table)
{
printf("table:0x%p\n", table);
for (int i = 0; table[i] != NULL; ++i)
{
printf("[%d]:0x%p -> ", i, table[i]);
FUNC f = (FUNC)table[i];
f();
}
cout << endl;
}
int main()
{
Base b;
Derive d;
PrintVTable((int*)(*((int*)&b)));
PrintVTable((int*)(*((int*)&d)));
return 0;
}
由于func1()函数是虚函数,并且在Derive中被重写了,所以可以看到d对象中Derive的func1()函数覆盖了Base中的func1()函数,func2()直接继承。
在Derive中首次声明func3()为虚函数,编译器不会再建立一张虚函数表,而是直接将这个虚函数地址写入到第一张虚函数表(Base)后面,并以0(NULL)结尾。此处并未将func3()在虚表中的地址打印出来是因为编译器的原因,我们可以手动将其打印出来。这里的_vfptr是虚函数表指针。
多重继承的对象模型
#include <iostream>
#include <stdio.h>
using namespace std;
class Base1
{
public:
virtual void func1()
{
cout << "Base1::func1()" << endl;
}
virtual void func2()
{
cout << "Base1::func2()" << endl;
}
private:
int _b1;
};
class Base2
{
public:
virtual void func1()
{
cout << "Base2::func1()" << endl;
}
virtual void func2()
{
cout << "Base2::func2()" << endl;
}
private:
int _b2;
};
class Derive : public Base1,public Base2
{
virtual void func1()
{
cout << "Derive::func1()" << endl;
}
virtual void func3()
{
cout << "Derive::func3()" << endl;
}
void func4()
{
cout << "Derive::func4()" << endl;
}
private:
int _d;
};
typedef void(*FUNC)();
void PrintVTable(int* table)
{
printf("table:0x%p\n", table);
for (int i = 0; table[i] != NULL; ++i)
{
printf("[%d]:0x%p -> ", i, table[i]);
FUNC f = (FUNC)table[i];
f();
}
cout << endl;
}
int main()
{
Base1 b1;
Base2 b2;
Derive d1;
PrintVTable((int*)(*((int*)&b1)));
PrintVTable((int*)(*((int*)&b2)));
PrintVTable((int*)(*((int*)&d1)));
PrintVTable((int*)(*((int*)((char*)&d1 + sizeof(Base2))))); //打印第二张虚表
return 0;
}
Derive同时继承Base1和Base2。
在多重继承的对象模型中,先是Base1的虚表指针,再是Base1的成员变量,再是Base2的虚表指针,再是Base2的成员变量,最后是Derive的成员变量 。在Derive中首次声明func3()为虚函数,并不会再建立一张虚函数表,而是直接将这个虚函数地址写入到第一张虚函数表(Base1)后面,并以0(NULL)结尾。
菱形继承的对象模型
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
virtual void f1()
{
cout << "A::f1()" << endl;
}
public:
int _a;
};
class B : public A
{
public:
virtual void f1()
{
cout << "B::f1()" << endl;
}
virtual void f2()
{
cout << "B::f2()" << endl;
}
public:
int _b;
};
class C : public A
{
public:
virtual void f1()
{
cout << "C::f1()" << endl;
}
virtual void f2()
{
cout << "C::f2()" << endl;
}
public:
int _c;
};
class D : public B, public C
{
public:
virtual void f1()
{
cout << "D::f1()" << endl;
}
virtual void f3()
{
cout << "D::f3()" << endl;
}
void f4()
{
cout << "D::f4()" << endl;
}
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
菱形继承的虚继承对象模型
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
virtual void f1()
{
cout << "A::f1()" << endl;
}
public:
int _a;
};
class B : virtual public A
{
public:
virtual void f1()
{
cout << "B::f1()" << endl;
}
virtual void f2()
{
cout << "B::f2()" << endl;
}
public:
int _b;
};
class C : virtual public A
{
public:
virtual void f1()
{
cout << "C::f1()" << endl;
}
virtual void f2()
{
cout << "C::f2()" << endl;
}
public:
int _c;
};
class D : public B, public C
{
public:
virtual void f1()
{
cout << "D::f1()" << endl;
}
virtual void f3()
{
cout << "D::f3()" << endl;
}
void f4()
{
cout << "D::f4()" << endl;
}
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}