一、函数的扩展性
1. 函数调⽤时必须严格匹配类型
2. 如果类型不符,编译器会进⾏隐式类型转换,如果能转换成⽬标类型,则调⽤成功,如果⽆法转
换为⽬标类型,则调⽤失败。
3. C++中如果参数接收的是基类类型,那么派⽣类对象可以作为参数传递。
在开发中一定要注意函数的扩展性,也就是函数功能的单一化 确保一个函数只干一件事情
为什么要这么做,一是为了以后进行函数重载时,方便在原有的函数功能上去拓展新的功能
二是避免BUG的寻找难度 如果一个函数内封装多个功能,那么很有可能出问题时需要将整个函数重写。一个功能带来的bug会影响整个函数,所以开发时一定要保证函数功能的单一化
二、静态和动态绑定
先看一个错误代码
class Animal
{
public:
void speak(){
cout << "说话..." << endl;
}
};
class Cat : public Animal
{
public:
void speak(){
cout << "⼩猫说话..." << endl;
}
};
class Dog : public Animal
{
public:
void speak(){
cout << "⼩狗说话..." << endl;
}
};
void do_speak(Animal &obj){
obj.speak();
}
void test(){
Animal animal;
do_speak(animal);
Cat cat;
do_speak(cat);
Dog dog;
do_speak(dog);
}
输出结果是
说话...
说话...
说话...
没有出现我们想出现的情况 是因为绑定的问题
函数体与函数调⽤相联系称为绑定
1. 如果这个绑定发⽣在程序运⾏之前,也就是编译阶段,称作早绑定,静态绑定。
2. 如果这个绑定并不在编译阶段进⾏,⽽是在运⾏过程中,根据实际情况进⾏绑定,称作晚绑定,
动态绑定。
问题就是由于静态绑定引起的,编译器只有知道Animal 地址时并不知道调⽤的正确函数,所以根据对象指针、引⽤的类型 来选择函数调⽤。
解决办法:就是让编译器进⾏动态绑定,⽽不是在编译阶段进⾏函数调⽤绑定。在 C语⾔中所有的函数绑定都是静态绑定,在 C++通过虚函数 (virtual function)机制来实现动态绑定。
通过创建⼀个 virtual函数来告诉编译器该函数要进⾏动态绑定,编译器就会根据动态绑定机制来实现
我们的要求,不会再执⾏静态绑定。
实现动态绑定,步骤如下:
1. 将函数参数设置为基类指针、或者基类引⽤。
2. 将基类中希望进⾏动态绑定的函数声明为虚函数。
3. 在⼦类中将基类虚函数重写。
注意:
1. ⼀旦在基类中将函数声明为虚函数,在此后所有的派⽣类中该函数都将是虚函数。
2. virtual只能修改成员函数。
3. 构造函数不能声明使⽤ virtual声明。
class Animal
{
public:
virtual void speak(){
cout << "说话..." << endl;
}
};
class Cat : public Animal
{
public:
void speak(){
cout << "⼩猫说话..." << endl;
}
};
class Dog : public Animal
{
public:
void speak(){
cout << "⼩狗说话..." << endl;
}
};
void do_speak(Animal &obj){
obj.speak();
}
void test()
{
Animal animal;
do_speak(animal);
Cat cat;
do_speak(cat);
Dog dog;
do_speak(dog);
}
输出结果:
说话...
⼩猫说话...
⼩狗说话...