【面面俱到/c++】多态的实现(重载、模板、虚函数表、虚基表)

目录

  • 一分钟速面
  • 静态多态(编译时多态)
    • 函数重载
    • 运算符重载
    • 模板
  • 动态多态(运行时多态)
    • 虚函数
    • 虚函数表vtable、虚函数表指针vptr
    • 虚基表指针vbptr


一分钟速面

c++的多态有静态多态(编译时多态)和动态多态(运行时多态)。静态多态主要依靠函数重载、运算符重载和函数模板实现,在编译期间生成不同的函数与类型,由编译器根据函数签名或模板实例化选择正确函数与类型。多态多态主要依靠继承、虚函数与虚函数重写实现,在运行时选择执行哪个函数。


静态多态(编译时多态)

函数重载

函数重载是指在同一作用域内,可以有一组相同函数名,不同参数列表的函数。重载函数通常用来命名一组功能相似的函数,这样可以减少函数名的数量,避免命名空间的污染。

  • 重载函数在编译时被编辑器赋予不同的名字,这个名字由函数名与参数列表决定,这样这些函数就不存在命名冲突的问题。
  • 编辑器在程序调用这些函数的地方,匹配最合适的重载函数执行。
void add(int i, int i);		// 函数1
void add(float f, float f);	// 函数2

int main()
{
	add(1, 2);	// 执行函数1
    add(1.0f, 2.0f);	// 执行函数2
    return 0;
}
  • 定义重载函数的参数模棱两可,导致编辑器歧义,无法找到最适合的函数,将会报错

运算符重载

运算符重载是一种特殊的函数重载。c++类中有默认的运算符函数,重载运算符就是定义一个重载函数,当遇到该运算符时就使用此运算符重载函数来处理。

模板

模板本身不是完整的代码,而是生成代码所用的蓝图。当使用具体类型实例化模板类或模板函数时,编译器会根据该类型生成特点版本的类或函数。
模板分为类模板函数模板。依据模板生成不同的类或函数的过程,称之为模板的实例化,可化分为隐式实例化显式实例化。 编译器为每个实例化的模板生成唯一的修饰名,避免命名冲突。

// 模板函数
template <typename T>
T add(T a, T b) {
    return a + b;  // 要求类型T支持+操作
}
int result = add(3, 5);  // 隐式实例化,自动生成 add(int, int)函数

// 模板类
template <typename T>
class Box {
public:
    void set(T t) { content = t; }
    T get() { return content; }
private:
    T content;
};
Box<std::string> stringBox;  // 显式实例化,生成Box类,box.content的类型为string

动态多态(运行时多态)

虚函数

在基类中可以使用virtual关键字声明虚函数,在子类中可以重写该虚函数。

class Base {
public:
	// 定义虚函数
	virtual int Get() {
		return 1;
	}
};

class Inherit : public Base {
public:
	// 重写父类虚函数
	virtual int Get() override {
		return 2;
	}
};

虚函数表vtable、虚函数表指针vptr

  • 每个声明有虚函数的类,都隐含着一个虚函数表,表中存放着所有虚函数的地址。当这个类重写了来自父类的某个虚函数时,将会将虚函数表中对应函数的地址改为该类中重写的函数地址。
  • 有虚函数的类的每个对象中有虚函数表指针,指向类的虚函数表,程序运行时,通过类的虚函数表指针查询虚函数表,在运行时确定调用哪个函数。
int main() {
	Base* base = new Base();
	Inherit inherit = new Inherit();
	base.Get();	// 返回1,调用Base类的Get()函数
	inherit.Get();	//	返回2,调用Inherit类的Get()函数
	return 0;
}
  • 多重继承时,对象中存在多个虚函数表指针,方便指向每个基类对应的虚函数表

虚基表指针vbptr

  • 菱形继承会导致类中出现重复的基类成员。使用虚继承解决此问题
  • 如果一个类虚继承了一个基类(这个基类被成为虚基类),它的对象内就会包含一个虚基表vbptr
  • 虚基表记录的是对象起始地址到虚基类A成员对象的偏移量,用于定位虚基类对象的位置。
  • 间接继承虚基类的类中也会有虚基表,用于其多个父类访问它们共享的虚基类成员对象。
class A { int a; virtual void func(); };
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};	// D继承B、C,存放B、C的虚基表,通过虚基表访问共享基类成员

/*
 / D对象中:
 /		B类虚函数表指针
 / 		B类虚基表指针	// 由此到A类成员
 /		[B类成员]
 /
 /		C类虚函数表指针
 / 		C类虚基表指针	// 由此到A类成员
 /		[C类成员]
 /		
 /		[D类成员]
 /
 /		[唯一共享的A类成员]
*/

正春华枝俏,待秋实果茂,愿与君共勉

你可能感兴趣的:(面面俱到/c++面试,c++,开发语言,笔记,经验分享,面试)