虚函数进行方法重定义的写法及其调用关系
1、虚函数进行方法重定义的写法
虚函数的作用是在派生类中进行函数的重写,或者说是把基类中继承来的同名同形参同返回类型的虚函数在派生类中进行重写,其中定义虚函数的返回类型、函数名和形参列表必须相同,否则会认为是函数的重载。但有唯一例外的写法:
class Object
{
public:
virtual Object* get(){return this;}
};
class Test:public Object
{
public:
virtual Test* get(){return this;}
};
其中,静态函数、友元函数、内联函数、构造函数、拷贝构造函数、移动构造函数都不能定义成虚函数,因为静态、友元函数没有this指针、内联函数不能调用、构造函数是用来放置虚表指针。析构函数可以定义成虚函数,因为虚函数是用来重置虚表指针。
只有类的成员函数才能定义成虚函数,因为虚函数只适用于有继承关系的类对象。
如果定义的虚函数要实现动态多态性时,必须使用类的对象指针或引用来调用。
当在类体中声明、在类外定义虚函数时,要在类体中声明,在类外定义时不能定义再加virtual。
2、虚函数的调用
在调用虚函数时,系统会生成一个对象对应的所有虚函数的函数地址构成的虚表,在调用时会检查指针的来源对象,找到对应的虚表,找出要调用的函数并调用。
虚表的构建方法:
#include<iostream>
using namespace std;
class Object
{
private:
int val;
public:
Object(int x=0):val(x){}
virtual void fun()
{
cout<<"Object::fun"<<endl;
}
virtual void show()
{
cout<<"Object::show"<<endl;
}
virtual void print()
{
cout<<"Object::print"<<endl;
}
};
class Base:public Object
{
private:
int num;
public :
Base(int x=0):num(x),Object(x+10) {}
virtual void fun()
{
cout<<"Base::fun"<<endl;
}
virtual void show()
{
cout<<"Base::show"<<endl;
}
virtual void menu()
{
cout<<"Base::menu"<<endl;
}
};
class Test:public Base
{
private:
int count;
public:
Test(int x=0):count(x),Base(x+10) {}
virtual void fun()
{
cout<<"Test::fun"<<endl;
}
virtual void menu()
{
cout<<"Test::menu"<<endl;
}
virtual void print()
{
cout<<"Test::print"<<endl;
}
};
void Vshow(Object *obj)
{
obj->fun();
obj->show();
obj->print();
}
int main()
{
Test t;
Base b;
Object o;
Vshow(&t);
Vshow(&b);
Vshow(&o);
return 0;
}
Object类没有继承其他类,它的虚表就是类体里的虚函数
|Object vtable |
|–|–|
| void Object::fun |
|void Object::show|
|void Object::print|
Base类继承了Object类,虚表也会继承过来,如果有同名虚函数,就要把Object的函数换成Base的函数
| Base vtable |
|–|–|
| void Base::fun |
|void Base::show|
|void Object::print|
|void Base::menu|
Test类继承了Base类,虚表也继承过来,如果有同名虚函数,就要把Base的函数换成Test的函数
| Test vtable |
|–|–|
| void Test::fun |
|void Base::show|
|void Test::print|
|void Test::menu|
当取Test类的对象t的地址传到Vshow,调用虚函数时系统会查找指针来源对象的虚表,找到对应的函数地址,就能调用对应的虚函数。Vshow函数的形参类型是Object类型的指针,所以函数体内通过(Object*)能调用的虚函数只能是Object虚表里的函数,当调用不属于Object虚表里的虚函数时编译器会报错。
当把以上代码作出一点小改动,在Test类内定义一个exam函数,exam用this指针调用虚函数show时,系统会查虚表调用Base类里的虚函数show,再在Base类里的show调用menu函数时,不会调用Base类里的show,而是取调Test类的show。由此可见,只有不管指针如何传递,系统调用虚函数时只会调用指针来源的那个对象的虚表。
#include<iostream>
using namespace std;
class Object
{
private:
int val;
public:
Object(int x=0):val(x){}
virtual void fun()
{
cout<<"Object::fun"<<endl;
}
virtual void show()
{
cout<<"Object::show"<<endl;
}
virtual void print()
{
cout<<"Object::print"<<endl;
}
};
class Base:public Object
{
private:
int num;
public :
Base(int x=0):num(x),Object(x+10) {}
virtual void fun()
{
cout<<"Base::fun"<<endl;
}
virtual void show()
{
cout<<"Base::show"<<endl;
this->menu();
}
virtual void menu()
{
cout<<"Base::menu"<<endl;
}
};
class Test:public Base
{
private:
int count;
public:
Test(int x=0):count(x),Base(x+10) {}
virtual void fun()
{
cout<<"Test::fun"<<endl;
}
virtual void menu()
{
cout<<"Test::menu"<<endl;
}
virtual void print()
{
cout<<"Test::print"<<endl;
}
void exam(){this->show();}
};
int main()
{
Test t(10);
t.exam();
return 0;
}