目录
- C++多态
- C语言模拟多态
C++多态
C++中的多态,从使用的角度上来概括,就是在父类的某函数前加上关键字virtual,然后在子类中重写这个函数。在调用的过程中,会根据对象的类型来决定调用哪个函数,如果对象类是父类就调用父类的函数,如果对象类是子类就调用子类中的函数。
首先,来看一组代码:
class One {
public:
void xion() {
std::cout << "father"<<endl;
}
};
class Us :public One {
public:
void xion() {
std::cout << "son"<<endl;
}
};
int main() {
One one;
one.xion();
Us us;
One *s = &us;
s->xion();
one = us;
one.xion();
system("pause");
}
输出是:
当把父类One加上virtual后:
virtual void xion() {
std::cout << "father"<<endl;
}
输出为:
接下来关于第一种情况出现的原因:
-
从编译的角度来看
C++在编译的时候,要确定除了虚函数之外的每个对象调用的函数地址,这称为早期绑定。当我们如第一段程序中,把子类对象us的地址赋给父类对象s之后,这是C++的编译器依旧认为s中保存的就是父类的对象地址,所以调用的xion()还是父类的函数。 -
从内存的角度来看
可以看出来,如果像第一段代码那样构造子类对象s,s会被认为只有上图中的上半部分,所以调用xion()的时候还是会调用父类的函数。
虚函数
所以要解决这个问题就要解除早期绑定,当编译器使用晚绑定之后,就会在运行的时候再确定对象类型从而正确的调用函数。使用虚函数,即在父类申明函数的时候在开始加上virtual。一旦父类加上了这个关键字,子类默认都是virtual虚函数,不需要再特别申明了。
任何有虚函数的类都会比一般的类要稍微大一些,因为虚函数的实现需要一个格外的/“虚表”和“虚指针”。
虚指针会影藏存在所以有virtual的类对象的最上面,由它指向虚表(vtable)。虚表中有所以virtual函数的地址。
其中虚指针和虚表都是在类的构造函数中初始化的,在构造子类对象中,需要先调用父类的构造函数,那么父类的虚指针和虚表就会被初始化;然后再执行子类的构造函数,子类的虚指针和虚表就会被初始化。
其中父类虚表的虚函数的排列顺序和基类中的虚函数排列顺序是一样的。并且虚表也是可以继承的,即子类如果没有重写虚函数,子类的虚表中仍会有这个函数的地址。
总结:
多态就是使用晚绑定的方法,把确认调用函数的步骤放在运行的时候而非编译的时候做。这样就可以达到使用一个父类的指针,通过将它指向不同的子类可以调用不同的函数的效果。
C语言模拟多态
C语言实现继承和多态感觉没有什么太大的意义在里面,只是觉得有点意思所以想试试看。
因为C语言中没有class的概念,只能struct来实现,但是结构体里面又不能定义函数,只能使用函数指针来完成。
typedef void(*OR)();//函数指针
struct One {
OR rave;
};
struct Us
{
One lee;//表示继承
};
void XionR() {
printf("%s\n", "father");
}
void XionL() {
printf("%s\n", "son");
}
int main() {
One one;
Us us;
one.rave = XionR;//对两个结构体的函数指针赋值
us.lee.rave = XionL;
One *s = &one;
s->rave();
s = (One*)&us;
s->rave();
system("pause");
}
结果: