虚成员函数的实现(多态)

虚成员函数是动态确定的(在运行时)。也就是说,成员函数(在运行时)被动态地选择,该选择基于对象的类型,而不是指向该对象的指针/引用的类型。这被称作“动态绑定”。大多数的编译器使用以下的一些的技术:如果对象有一个或多个虚函数,编译器将一个
隐藏的指针放入对象,该指针称为“virtual-pointor”或“v-pointer”。这个v-pointer指向一个全局表,该表称为“虚函数表(virtural-table)”或“v-table”。

编译器为每个含有至少一个虚函数的类创建一个v-table。例如,如果Cirle类有虚函数ddraw()move() 和 resize(),那么将有且只有一个和Cricle类相关的v-table,即使有一大堆Circle对象。并且每个 Circle对象的 v-poiner将指向 Circle的这个 v-table。该 v-table自己有指向类的各个虚函数的指针。例如,Circle 的v-table 会有三个指针:一个指向Circle::draw(),一个指向 Circle::move(),还有一个指向Circle::resize()

在分发一个虚函数时,运行时系统跟随对象的 v-pointer找到类的 v-table,然后跟随v-table中适当的项找到方法的代码。

以上技术的空间开销是存在的:每个对象一个额外的指针(仅仅对于需要动态绑定的对象),加上每个方法一个额外的指针(仅仅对于虚方法)。时间开销也是有的:和普通函数调用比较,虚函数调用需要两个额外的步骤(得到v-pointer的值,得到方法的地址)。由于编译器在编译时就通过指针类型解决了非虚函数的调用,所以这些开销不会发生在非虚函数上。
举个例子:

class Class1   {
   public :
      data1;
      data2;
      memfunc();
      virtual vfunc1();
      virtual vfunc2();
      virtual vfunc3();
};
Class1 物件實體在記憶體中佔據這樣的空間:

每一個由此類別衍生出來的物件,都有這麼一個 vptr。當我們透過這個物件呼叫虛擬函
式,事實上是透過 vptr   找到虛擬函式表,再找出虛擬函式的真正位址。
奧妙在於這個虛擬函式表以及這種間接呼叫方式。虛擬函式表的內容是依據類別中的虛
擬函式宣告次序,一一填入函式指標。衍生類別會繼承基礎類別的虛擬函式表(以及所
有其他可以繼承的成員),當我們在衍生類別中改寫虛擬函式時,虛擬函式表就受了影
響:表中元素所指的函式位址將不再是基礎類別的函式位址,而是衍生類別的函式位址。
看看這個例子:
class Class2 : public Class1 {
   public :
     data3;
     memfunc();
     virtual vfunc2();
};

於是,一個「指向   Class1   所生物件」的指標,所呼叫的   vfunc2   就是 Class1::vfunc2,而
一個「指向   Class2   所生物件」的指標,所呼叫的 vfunc2   就是   Class2::vfunc2。
動態繫結機制,在執行時期,根據虛擬函式表,做出了正確的選擇。

下面是一个使用C++函数实现多态的代码示例: ```cpp #include <iostream> using namespace std; class A { public: virtual void foo() { cout << "A::foo()" << endl; } virtual ~A() { cout << "~A()" << endl; } }; class B : public A { public: void foo() { cout << "B::foo()" << endl; } ~B() { cout << "~B()" << endl; } }; int main() { A* p = new B(); p->foo(); delete p; return 0; } ``` 在这个示例A和B都有一个名为foo()的函数。当我们通过基指针p调用foo()函数时,实际上会根据指针所指向的对象的型来确定调用哪个的foo()函数。这就是多态实现原理。 输出结果为: ``` B::foo() ~B() ~A() ``` 可以看到,通过函数实现多态,调用的是派生B的foo()函数,而不是基A的foo()函数。同时,析构函数也是函数,确保在删除指针p时,会先调用派生B的析构函数,再调用基A的析构函数。这是因为在C++,如果基的析构函数不是函数,那么通过基指针删除派生对象时,只会调用基的析构函数,而不会调用派生的析构函数。因此,为了确保正确的析构顺序,析构函数应该声明为函数。 请注意,函数只能在成员函数声明,构造函数不能声明为函数。这是因为在创建对象时,需要先调用构造函数来初始化对象,而函数的机制是在运行时根据对象的型来确定调用哪个函数,而构造函数在对象创建时就已经确定了。因此,构造函数不能声明为函数。 #### 引用[.reference_title] - *1* [函数实现多态原理](https://blog.csdn.net/qq_24309981/article/details/89102183)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [C++函数与多态实现](https://blog.csdn.net/qq_27576655/article/details/124535530)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值