多态
多态的服务对象主要是存在层次结构且是通过继承关联的类。C++的多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
例如:
using namespace std;
class Goust
{
public:
double i;
double j;
Goust(int a, int b)
{
i = a;
j = b;
}
void put()
{
cout <<"you are"<<i << endl;
}
};
class Goust1:public Goust
{
public:
Goust1(int a,int b):Goust(a,b){}
void put()
{
cout<<"make a"<<j<<endl;
}
};
class Goust2:public Goust
{
public:
Goust2(int a,int b):Goust(a,b){}
void put()
{
cout<<"do a"<<i<<endl;
}
};
int main()
{
Goust *g;
Goust1 h(5,5);
Goust2 k(6,6);
g=&h;
g->put();
g=&k;
g->put();
return 0;
}
输出:
根据结果可知,两次的输出均未达到想要的效果,输出错误的原因为
调用函数 area() 被编译器设置为基类中的版本,这就是静态多态,也叫静态链接。函数调用在程序执行前就准备好了。
修改方式也就是用多态,关键字为virtual。
需修改的部分如下:
class Goust
{
public:
double i;
double j;
Goust(int a, int b)
{
i = a;
j = b;
}
virtual void put()
{
cout <<"you are"<<i << endl;
}
};
输出:
这时候,编译器看的就不再是指针的类型,而是指针的内容。
简单来说,有了多态,就可以在自己的不同类中用相同的函数名称实现不同的功能,甚至于参数也可以是相同的。其实也可以完全避免,就直接在不同的类使用不同的函数名称就好了,主要是为了在复杂的程序函数功能里简单明了。
虚函数
虚函数就是上述关键字virtual声明的函数,也就是在派生类中定义基类中定义的虚函数时,告诉编译器不要静态链接该函数。这种操作官方定义名称为动态链接,也叫后期绑定。
纯虚函数
在一个类中如果存在未定义的虚函数,那么不能直接使用该类的实例,可以理解因为未定义 virtual 函数,其类是抽象的,无法实例化。将报错误:
undefined reference to `vtable for xxx'
在基类并不能给出有意义的实现时,需要用到纯虚函数
表达形式
class Goust
{
public:
double i;
double j;
Goust(int a, int b)
{
i = a;
j = b;
}
virtual void put()=0;
};
多态形成的三个必要条件:
- 继承关系
- 同名虚函数
- 存在基类类型的指针或者引用,通过该指针或引用调用函数
父类的虚函数或纯虚函数在子类中依然是虚函数。有时我们并不希望父类的某个函数在子类中被重写,这时就要用到关键字final来避免该函数再次被重写。
using namespace std;
class Goust
{
public:
double i;
double j;
Goust(int a, int b)
{
i = a;
j = b;
}
void put()
{
cout <<"you are"<<i << endl;
}
};
class Goust1:public Goust
{
public:
Goust1(int a,int b):Goust(a,b){}
void put() final
{
cout<<"make a"<<j<<endl;
}
};
/*class Goust2:public Goust1
{
public:
void put() //错误用法,put在Goust1中已经不是虚函数了,因此不能再被重写。
{
cout<<"do a"<<i<<endl;
}
};*/
int main()
{
Goust *g;
Goust1 h(5,5);
Goust2 k(6,6);
g=&h;
g->put();
g=&k;
g->put();
return 0;
}
同时,不希望一个类被继承也可以使用final。
C++中, 虚函数可以为private, 并且可以被子类覆盖(因为虚函数表的传递),但子类不能调用父类的private虚函数。虚函数的重载性和它声明的权限无关。
编译器不检查虚函数的各类属性。被virtual修饰的成员函数,不论他们是private、protect或是public的,都会被统一的放置到虚函数表中。对父类进行派生时,子类会继承到拥有相同偏移地址的虚函数表(相同偏移地址指,各虚函数相对于VPTR指针的偏移),则子类就会被允许对这些虚函数进行重载。且重载时可以给重载函数定义新的属性,例如public,其只标志着该重载函数在该子类中的访问属性为public,和父类的private属性没有任何关系!
纯虚函数可以设计成私有的,不过这样不允许在本类之外的非友元函数中直接调用它,子类中只有覆盖这种纯虚函数的义务,却没有调用它的权利。