您的位置:首页 > 编程语言 > C语言/C++

C++中多态特性深入探究---虚函数

2017-08-26 15:48 585 查看

首先,多态是什么?

摘抄自 多态(维基百科)

—— 多态(英语:polymorphism),是指计算机程序运行时,相同的消息可能会送给多个不同的类别之对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。简单来说,所谓多态意指相同的消息给予不同的对象会引发不同的动作称之。

cplusclpus中有这么一句话:

One of the key features of class inheritance is that a pointer to a derived class is type-compatible with a pointer to its base class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature.

即,在C++中指向子类的指针和指向基类的指针是兼容的。

所以,在C++中,我们可以通过指针指向不同的对象来实现多态(引用和指针类似,虽然有些差别,但是这里不展开讨论)

虚函数机制

参考《Effective C++》条款07中对虚表指针以及虚函数表的讲解,这里阐述下虚函数机制的原理:

—- 在C++中,若在类中定义了虚函数,则编译器会为改类生成一个虚函数表,这个表中保存了该类以及父类所有的虚函数,并在实例化该类时,会在类对象中添加一个虚表指针,这个指针就只向该虚表指针,当使用指针或引用调用函数时,C++会根据对象中保存的虚表指针查找需要调用的函数,具体的细节下面慢慢分析。

class Fa
{
int Fa_a;
public:
void foo()
{
cout << "父类的foo函数 \n";
}
};

class childA:public Fa
{
int CA_a;
public:
void foo()
{
cout << "子类A的foo函数 \n";
}
};




在没有定义虚函数时,内存布局中只存在父类和子类的数据成员对象

class Fa
{
int Fa_a;
public:
virtual void foo()
{
cout << "父类的foo函数 \n";
}
};

class childA:public Fa
{
int CA_a;
public:
void foo()
{
cout << "子类A的foo函数 \n";
}
};




在添加了虚函数后,内存布局中增加了一个vfptr,并且生成了一个vftable,分别是虚表指针和虚函数表。

class Fa
{
int Fa_a;
public:
virtual void foo()
{
cout << "父类的foo函数 \n";
}
};

class childA:public Fa
{
int CA_a;
};




class Fa
{
int Fa_a;
};

class childA:public Fa
{
int CA_a;
public:
virtual void foo()
{
cout << "子类A的foo函数 \n";
}
};




在这两个例子中需要注意两点:

当在父类中先定义虚函数时,vfptr出现在父类下面,当子类先定义虚函数时,vfptr出现在子类下面

当在子类中定义了和父类中虚函数相同的函数时,虚函数表中保存的是子类的虚函数,当子类中没有定义与父类虚函数相同的函数时,虚函数表中保存的是父类的虚函数。

—- 这里不难得出一下两点结论:

子类会继承父类的虚函数表,并且当子类定义了与父类相同的函数(返回值,函数签名都相同)时,子类的虚函数会覆盖父类的虚函数保存到虚函数表中。

当子类出现父类中没有的虚函数时,子类的虚函数会被添加到虚函数表中,并且偏移量大于任意父类的虚函数。

— 上面的例子还不是很清晰的说明了第二点,下面在放一个清晰一点的例子:

class Fa
{
int Fa_a;
public:
virtual void foo()
{
cout << "父类的foo函数 \n";
}
virtual  void FFF()
{
cout << "";
}
};

class childA:public Fa
{
int CA_a;
public:
virtual void foo()
{
cout << "子类A的foo函数 \n";
}

virtual void funcA()
{
cout << "子类A的func函数\n";
}
};




这个例子可以让我们清楚的理解上面两点结论,父类中先定义了虚函数,于是vfptr在Fa下面,子类定义了与父类相同的函数,于是子类的虚函数覆盖了父类的虚函数,子类新增了虚函数,于是虚函数表中添加了该虚函数,并且在父类虚函数的后面。

理解了原理后我们就可以使用虚函数来实现多态了:

#include<iostream>

using namespace std;

class Fa
{
int Fa_a;
public:
virtual void foo()
{
cout << "父类的foo函数 \n";
}
};

class childA:public Fa
{
int CA_a;
public:
virtual void foo()
{
cout << "子类A的foo函数 \n";
}
};

class childB :public Fa
{
int CB_a;
public:
virtual void foo()
{
cout << "子类B的foo函数\n";
}
};

void func()
{
Fa * ff = new Fa();
ff->foo();
ff =new childA();
ff->foo();
ff = new childB();
ff->foo();
}

int main()
{
func();
system("pause");
return 0;
}




可以看到,我们使用了同一个ff指针,却调用了三个不同的函数,这就是多态了。

下一篇会讲解虚函数的注意事项

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 多态