1.函数联编
将代码中的函数调用解释为执行特定的函数代码块,叫做函数联编。
函数联编分为静态联编和动态联编:
静态联编:在编译过程中进行联编,又称早期联编;
动态联编:在程序运行时进行联编,又称晚期联编;
在C++中,动态联编与通过指针和引用调用方法有关,其实可以说,这是由继承控制的。通常,C++不允许将一种类型的地址赋给另一种类型的指针,也不允许一种类型的引用指向另一种类型。
但是,这也有例外,指向基类的引用或指针可以引用派生类对象,而不必进行显式类型转换(将派生类指针或引用转换为基类引用或指针被称为向上强制类型转换);反过来,将基类指针或引用转换为派生类指针或引用被称为向下强制类型转换。注意,如果不使用显式类型转换,则向下强制类型转换是不允许的。
2.结合代码理解
class Animal{ //动物基类
private:
//......
public:
virtual void eat() const{
cout<<"动物吃方法!!"<<endl;
}
//......
};
class Dog : public Animal{ //狗类派生于动物类
private:
//......
public:
void eat() const{
cout<<"狗吃骨头!!!"<<endl;
}
//......
};
上面定义了一个基类,即动物类,并派生出一个狗类;
void main()
{
Dog firstDog;
Animal * ap;
ap = &firstDog;
ap->eat();
}
如果在基类Animal中没有将eat()声明为虚的,则ap->eat()将根据指针类型(Animal *)调用Animal::eat()。指针类型在编译时已知,因此编译器在编译时,可以将eat()关联到Animal::eat()。总之,编译器对非虚方法使用静态联编。
但是,如果在基类Animal中将eat()声明为虚的,则ap->eat()将根据对象类型(Dog)调用Dog::eat(),上述代码中,对象firstDog类型为Dog。但通常只有在运行程序时才能确定对象的类型。所以编译器生成的代码将在程序执行时,根据对象类型将eat()关联到Animal::eat()或Dog::eat()。总之,编译器对虚方法使用动态联编。
3.对比
虽然在大多数情况下,动态联编很好,因为它能让程序能够选择特定类型设计的方法。但是基于效率和概念模型两方面考虑,C++还是将静态联编设置为默认的。
在效率方面,为使程序能够在运行阶段进行决策,必须用一些方法来跟踪基类指针或引用指向的对象,这增加了额外的处理开销。例如,当类不会用作基类时,则不需要动态联编;同时,如果派生类不重新定义基类的任何方法,也不需要使用动态联编。在上述情况下,使用静态联编更合理,效率也更高。
在概念模型方面,在设计类时,可能包含一些不在派生类重新定义的成员函数,则不该将函数设置为虚函数,这样,不仅效率更高,也指出不要重新定义该函数。
总之,如果在派生类中重新定义基类的方法,则将它设置为虚方法;否则,设置为非虚方法。虽然非虚函数效率比虚函数稍高,但不具备动态联编功能。
来源:CSDN
作者:勤勉的一只洋
链接:https://blog.csdn.net/weixin_44438749/article/details/103911604