同城分类网站建设,jsp网站地图生成器,建设银行辽宁招聘网站,2014网站设计风格一.虚函数 在基类的函数前加上virtual关键字#xff0c;在派生类中重写该函数#xff0c;运行时将会根据所指对象的实际类型来调用相应的函数#xff0c;如果对象类型是派生类#xff0c;就调用派生类的函数#xff0c;如果对象类型是基类#xff0c;就调用基类的函数。 …一.虚函数 在基类的函数前加上virtual关键字在派生类中重写该函数运行时将会根据所指对象的实际类型来调用相应的函数如果对象类型是派生类就调用派生类的函数如果对象类型是基类就调用基类的函数。 一虚表和虚基表指针
虚函数表Virtual Function Table
在C中每个类包括基类和派生类包含一个虚函数表也称为虚表vtable。虚表是一个包含虚函数指针的数组每个虚函数指针指向相应虚函数的地址。虚表是在编译时创建的并且对于每个类只有一个。虚表是类的元数据它记录了该类的虚函数及其位置。当类的对象被创建时一个指向虚表的指针会添加到对象的内存布局中。 虚表指针
在含有虚函数的类实例化对象时对象地址的前四个字节存储的指向虚表的指针它是在构造函数中被初始化的。 二纯虚函数
纯虚函数Pure Virtual Function是C中的一个特殊类型的虚拟函数它在基类中声明但没有定义。纯虚函数的声明使用virtual关键字并在函数声明的末尾添加 0来表示它是一个纯虚函数。子类派生类必须提供纯虚函数的实际实现否则子类也会被标记为抽象类无法创建对象。
class Shape {
public:// 声明纯虚函数virtual void draw() 0;// 普通成员函数void displayInfo() {// 这里可以包含一些通用的代码std::cout This is a shape. std::endl;}
};class Circle : public Shape {
public:// 子类必须提供纯虚函数的实现void draw() override {std::cout Drawing a circle. std::endl;}
};class Square : public Shape {
public:// 子类必须提供纯虚函数的实现void draw() override {std::cout Drawing a square. std::endl;}
};int main() {Circle circle;Square square;circle.displayInfo(); // 调用基类函数circle.draw(); // 调用派生类函数square.displayInfo(); // 调用基类函数square.draw(); // 调用派生类函数return 0;
}
在上述示例中Shape类包含一个纯虚函数draw()因此Shape类本身是一个抽象类不能创建它的对象。然后Circle和Square类都继承自Shape类并必须提供对draw()的实际实现。这种机制允许多态性Polymorphism的实现允许不同的派生类以不同的方式实现相同的虚拟函数。 二.多态的实现
根据上图举例分析
#includeiostream
#includevector
using namespace std;
class A {
public:virtual void prints() {cout A::prints endl;}A() {cout A:构造函数 endl;}
};
class B:public A {
public:virtual void prints() {cout B::prints endl;}B() {cout B:构造函数 endl;}
};
class C :public A {
public:};
int main() {A *b new B();b-prints();b new C();b-prints();return 0;
} 多态的原理
1.虚函数表和虚指针上面
2.虚函数声明和覆盖 在基类中声明虚函数时使用virtual关键字。这告诉编译器将该函数视为虚函数它可以在派生类中被覆盖重写。 派生类中的虚函数覆盖基类中的虚函数时必须使用override关键字以确保正确的函数被覆盖。这也使得代码更容易阅读和维护
3.动态绑定的过程 当您使用基类指针或引用调用虚函数时编译器不会在编译时决定要调用哪个函数版本。相反它会在运行时根据对象的实际类型来选择正确的函数版本。 这个过程大致如下 在基类指针或引用上调用虚函数。运行时系统会查找对象的虚表指针。使用虚表指针找到虚表。从虚表中获取正确的函数指针。调用相应的函数。
4.多态性的优势
多态性无需关心对象的具体类型从而提高了代码的可复用性和可维护性。它允许轻松扩展代码通过添加新的派生类来增加功能而不必修改现有的代码。多态性还支持抽象编程允许定义基于接口的类然后通过派生类来实现具体的功能。
总结一下多态的原理基于虚函数和虚表它允许在运行时根据对象的实际类型来选择要调用的函数版本从而实现了面向对象编程中的灵活性和可扩展性。多态性是C和其他面向对象编程语言的核心概念之一有助于构建可维护和可扩展的软件系统。 三.为什么析构函数一般写成虚函数 由于类的多态性通常通过父类指针或引用来操作子类对象。因为多套允许我们以统一的方式处理不同的派生类对象并且在运行时确定要调用的方法。 如果析构函数不被声明为虚函数则编译器实施静态绑定在删除基类指针时只会调用基类的析构函数而不调用派生类析构函数这样会造成派生类析构不完全造成内存泄漏。 这种行为是为了确保资源的正确释放。由于我们只知道父类的类型编译器无法确定指针指向的是哪个子类对象因此只能调用父类的析构函数来释放资源。
没有虚析构
#includeiostream
#includevector
using namespace std;
class A {
public:virtual void prints() {cout A::prints endl;}A() {cout A:构造函数 endl;}virtual ~A() {cout A:析构函数 endl;}
};
class B:public A {
public:virtual void prints() {cout B::prints endl;}B() {cout B:构造函数 endl;}~B() {cout B:析构函数 endl;}
};
int main() {A *b new B();b-prints();delete b;b NULL;return 0;
} 虚析构
#includeiostream
#includevector
using namespace std;
class A {
public:virtual void prints() {cout A::prints endl;}A() {cout A:构造函数 endl;}virtual ~A() {cout A:析构函数 endl;}
};
class B:public A {
public:virtual void prints() {cout B::prints endl;}B() {cout B:构造函数 endl;}~B() {cout B:析构函数 endl;}
};
int main() {A *b new B();b-prints();delete b;b NULL;return 0;
} 分析可以看到析构函数是先从子类析构再到父类析构