重载:是指在同一作用域中允许存在多个同名函数,⽽这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。重载与类无关,重载实现编译时多态,属于静态绑定。
重写:指⼦类新定义⽗类的函数的做法。如果重写的函数在父类中是虚函数,那么能够实现动态多态。
如果在父类中没有将函数声明为虚函数,子类仍然可以重写(覆盖)父类的函数,但是在运行时将无法实现多态性,即无法通过父类指针或引用调用子类的函数。
什么叫运行时多态?父类指针或引用可以指向子类对象,并且如果父类中定义的虚函数被派生类重写,那么通过该指针或引用使用该函数时调用的是子类中重写后的函数。注意,必须通过虚函数才能实现动态多态。虚函数定义使用virtual关键字
点击查看代码
class Base {
public:
virtual void print() {
cout << "Base class" << endl;
}
};
class Derived : public Base {
public:
void print() override {
cout << "Derived class" << endl;
}
};
int main() {
Base* basePtr = new Derived();
basePtr->print(); // 输出 "Derived class"
delete basePtr;
return 0;
}
上述代码中使用virtual关键字是定义基类中的函数为虚函数,在子类中进行重写。重写虚函数时override关键字不是必须的,但是建议写上,这是一个良好的习惯。override关键字提供编译器级别的检查,若重载的不是虚函数同时又使用了override关键字,那么会抛出错误异常。
纯虚函数和虚函数的区别
纯虚函数(Pure Virtual Functions)是在基类中声明的没有实际实现的虚函数,它只是为了让派生类去实现。基类中包含纯虚函数的类被称为抽象类(Abstract Class),抽象类不能被实例化,只能作为接口使用。为了将函数声明为纯虚函数,需要在基类中使用关键字virtual,并将函数定义为纯虚函数,即在函数声明的末尾加上= 0。
点击查看代码
class Shape {
public:
virtual void draw() = 0; // 纯虚函数
};
class Circle : public Shape {
public:
void draw() {
cout << "Drawing a circle." << endl;
}
};
int main() {
// Shape shape; // 错误,抽象类不能被实例化
Shape* shapePtr = new Circle();
shapePtr->draw(); // 输出 "Drawing a circle."
delete shapePtr;
return 0;
}
在一个类中可以同时定义纯虚函数和虚函数,被称为混合类。在包含纯虚函数的类中,该类仍然是一个抽象类,无法直接实例化。必须在派生类中对纯虚函数进行实现,才能实例化派生类对象。
点击查看代码
class Base {
public:
virtual void virtualFunction() {
cout << "Virtual function" << endl;
}
virtual void pureVirtualFunction() = 0; // 纯虚函数
};
class Derived : public Base {
public:
void virtualFunction() {
cout << "Derived class's virtual function" << endl;
}
void pureVirtualFunction() {
cout << "Derived class's implementation of pure virtual function" << endl;
}
};
int main() {
Base* basePtr = new Derived();
basePtr->virtualFunction(); // 输出 "Derived class's virtual function"
basePtr->pureVirtualFunction(); // 输出 "Derived class's implementation of pure virtual function"
delete basePtr;
return 0;
}
虚函数实现动态多态在编译器上如何实现
在编译器上,虚函数的实现主要涉及两个关键概念:虚函数表(vtable)和虚函数指针(vptr)。
虚函数表(vtable):
对于包含虚函数的类,在编译过程中,编译器会为该类生成一个虚函数表(vtable)。虚函数表是一个存储函数指针的数据结构,每个虚函数在表中都有一个对应的函数指针。虚函数表位于类的内存布局中,通常是以静态数据成员的形式存在于类的头部。虚函数表是针对类层次结构中的每个类生成的,每个类都有自己的虚函数表。
虚函数指针(vptr):
对于每个包含虚函数的类的对象,在内存中都会被分配一个额外的隐藏成员,称为虚函数指针(vptr)。如果子类继承的父类中包含虚函数,那么子类从逻辑上也包含虚函数,因此子类的对象也会有一个虚函数指针
虚函数指针是一个指向虚函数表的指针,它指向与对象所属的类对应的虚函数表。虚函数指针位于对象的内存布局中,通常是作为对象的第一个或前几个字节。通过虚函数指针,程序能够在运行时动态地确定调用哪个虚函数。
实现动态多态的过程如下:
- 当使用基类指针或引用指向派生类对象时,编译器会根据指针或引用的静态类型(即基类类型)来访问虚函数。
- 在运行时,通过虚函数指针(vptr)找到对象所属的虚函数表。
- 通过虚函数表中的函数指针,确定要调用的虚函数的地址。
最终,运行时会调用正确的派生类函数,实现多态性。
派生类如何继承和修改基类的虚函数表
派生类会继承并修改基类的虚函数表。
当一个派生类继承自一个基类时,它会继承基类的虚函数表。继承的过程中,派生类会保留基类的虚函数表,并在其后添加自己新增的虚函数的地址。如果派生类重写(覆盖)了基类的虚函数,它会将自己的虚函数的地址替换掉基类虚函数表中相应位置的地址。这样,通过派生类对象的虚函数指针,就可以调用派生类自己的虚函数。如果派生类新增了虚函数,它会将新增虚函数的地址添加到自己的虚函数表的末尾。这样,通过派生类对象的虚函数指针,就可以调用新增的虚函数。需要注意的是,派生类的虚函数表与基类的虚函数表是相互独立的,它们有不同的内存空间。派生类的虚函数表会继承基类的虚函数表,并在其基础上进行修改或扩展。
理解多态的关键在于virtual关键字,还是有很多深入讨论的内容,后续继续补充
标签:函数,派生类,多态,C++,纯虚,基类,重载,函数指针 From: https://www.cnblogs.com/starstxg/p/18151223