虚函数,直接上个代码吧,这份代码很长,因为其中研究了一下虚函数的实现,虽说好像什么也没懂。
#include <iostream>
#include <stdio.h>
using namespace std;
class Base {
public:
Base() {}
virtual ~Base() {}
void non_virtual_show() {
cout << "it shows I am a Base(non_virtual)" << endl;
}
virtual void virtual_show() {
cout << "it shows I am a Base(virtual)" << endl;
}
virtual void virtual_show_1() {}
virtual void virtual_show_2() {
cout << "successfully call virtual_show_2 in base" << endl;
}
};
class Derive_1 : public Base {
public:
void non_virtual_show() {
cout << "it shows I am a Derive_1(non_virtual)" << endl;
}
virtual void virtual_show() {
cout << "it shows I am a Derive_1(virtual)" << endl;
}
virtual void virtual_show_1() {}
virtual void virtual_show_2() {
cout << "successfully call virtual_show_2 in derive_1" << endl;
}
};
int main() {
//PART 1 without pointer
Base b1;
Derive_1 d1;
b1.non_virtual_show();
b1.virtual_show();
d1.non_virtual_show();
d1.virtual_show();
cout << endl;
//PART 2 with pointer
Base *p1 = new Base;
Base *p2 = new Derive_1;
Derive_1 *p3 = new Derive_1;
//Derive_1 *p4 = new Base; invalid conversion
p1->non_virtual_show();
p1->virtual_show();
p2->non_virtual_show();
p2->virtual_show();
cout << endl;
//PART 3 address
void (Base::*B_pointer)();
B_pointer = &Base::non_virtual_show;
printf("%p\n", B_pointer);
B_pointer = &Base::virtual_show;
printf("%p\n", B_pointer);
B_pointer = &Base::virtual_show_1;
printf("%p\n", B_pointer);
B_pointer = &Base::virtual_show_2;
printf("%p\n", B_pointer);
cout << endl;
void (Derive_1::*D_pointer)();
D_pointer = &Derive_1::non_virtual_show;
printf("%p\n", D_pointer);
D_pointer = &Derive_1::virtual_show;
printf("%p\n", D_pointer);
D_pointer = &Derive_1::virtual_show_1;
printf("%p\n", D_pointer);
D_pointer = &Derive_1::virtual_show_2;
printf("%p\n", D_pointer);
cout << endl;
//PART 4 call the virtual function through their address
void (Base::*pointer1)();
Base *p4 = new Derive_1;
pointer1 = &Base::non_virtual_show;
(p4->*pointer1)();
pointer1 = &Base::virtual_show;
printf("%p\n", pointer1);
pointer1 = &Base::virtual_show_1;
printf("%p\n", pointer1);
pointer1 = &Base::virtual_show_2;
printf("%p\n", pointer1);
(p4->*pointer1)();
(p4->*(&Base::virtual_show_2))();
(p4->Base::virtual_show_2)();
p4->Base::virtual_show_2();
//pointer1 = &Derive_1::non_virtual_show; cannot convert 'void (Derive_1::*)' to 'void (Base::*)()' in assignment
//pointer1 = &Derive_1::virtual_show; cannot convert 'void (Derive_1::*)' to 'void (Base::*)()' in assignment
cout << endl;
void (Base::*pointer2)();
Derive_1 *p5 = new Derive_1;
pointer2 = &Base::non_virtual_show;
(p5->*pointer2)();
pointer2 = &Base::virtual_show;
printf("%p\n", pointer2);
pointer2 = &Base::virtual_show_1;
printf("%p\n", pointer2);
pointer2 = &Base::virtual_show_2;
printf("%p\n", pointer2);
(p5->*pointer2)();
(p5->*(&Base::virtual_show_2))();
(p5->Base::virtual_show_2)();
p5->Base::virtual_show_2();
cout << endl;
void (Derive_1::*pointer3)();
Derive_1 *p6 = new Derive_1;
pointer3 = &Base::non_virtual_show;
(p6->*pointer3)();
pointer3 = &Base::virtual_show;
printf("%p\n", pointer3);
pointer3 = &Base::virtual_show_1;
printf("%p\n", pointer3);
pointer3 = &Base::virtual_show_2;
printf("%p\n", pointer3);
(p6->*pointer3)();
(p6->*(&Base::virtual_show_2))();
(p6->Base::virtual_show_2)();
p6->Base::virtual_show_2();
return 0;
}
输出结果:
首先是基本的解释虚函数:
看part 1 和 part 2
p2->non_virtual_show();
p2->virtual_show();
这两行代码说明了虚函数的不同之处,上一行在静态编联(编译阶段)中已经确定调用的函数,而下一行是动态编联,在程序运行阶段才确定调用哪个类中的函数,虽说p2指向基类,但是类对象在程序运行过程中才确定是派生类,这样通过虚函数就会调用派生类中的函数。
摘抄个内容吧:
简单地说,那些被virtual关键字修饰的
成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现
多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。
定义虚函数的限制:
(1)非
类的成员函数不能定义为虚函数,类的成员函数中
静态成员函数和
构造函数也不能定义为虚函数,但可以将
析构函数定义为虚函数。实际上,优秀的程序员常常把
基类的
析构函数定义为虚函数。因为,将
基类的
析构函数定义为虚函数后,当利用delete删除一个指向
派生类定义的
对象指针时,系统会调用相应的类的析构函数。而不将
析构函数定义为虚函数时,只调用
基类的析构函数。
(2)只需要在声明函数的类体中使用
关键字“virtual”将函数声明为虚函数,而定义函数时不需要使用关键字“virtual”。
其实part3part4就是这个问题:
为什么会输出不一样的结果呢?
百度百科上提到了
virtual table,参考
http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/
于是写了part3part4研究看看,
运行结果:
首先是part3的部分,发现不是虚函数的地址和虚函数的地址差得很多 ,而且,当我把基类的析构函数的virtual去掉之后的输出结果:
这似乎说明,虚函数的所谓地址只是virtual table中的相对地址(猜测是下标),因为间隔都是一样的,而且基类和派生类的相对地址是一样的(但这并不能说明地址一样,只是相对于virtual table起始距离一样)。不知道这种情况应该怎么解释。