虚函数
作用:
在基类中定义了虚函数后,可以在派生类中重新定义该虚函数,但在重新定义时,其返回类型、函数名、参数个数、参数类型的顺序,都必须完全相同(即只有函数体可以不同)。
显示定义
格式:
virtual 返回类型 函数名(参数){函数体;}
代码示例:
#include <iostream>
#include <string>
using namespace std;
class say_basic
{
public:
string word;
say_basic(string default_word = "human") :word(default_word){}
virtual void say_something() {
cout << "hellow " << word << endl;
}
string get_type() {
return word;
}
};
class say_basic1: public say_basic
{
public:
say_basic1(string default_word = "man") : say_basic(default_word){}
void say_something()
{
cout << "hi " << word << endl;
}
};
int main()
{
say_basic *p;
say_basic f;
say_basic1 word1;
p = &f;
p -> say_something();
p = &word1;
p -> say_something();
}
运行结果:
虚函数的隐式定义
若在函数前没有virtual声明,系统会通过判断不同类中的同名函数的返回类型、参数个数、参数类型顺序、满足赋值兼容的指针来进行virtual虚函数的隐式定义。
说明
使用对象名和点运算符的方式也可以调用虚函数,如mom.like()可以调用虚函数Mother::like()。但是,这种调用是在编译时进行的静态连编,它没有充分利用虚函数的特性,只有通过基类指针访问虚函数时才能获得运行时的多态性。
虚析构函数
定义一个基类的指针,用new创建一个派生类的无名对象, 再将这个无名对象的地址赋给指针后,运用delete运算符撤销无名对象时,会执行基类的析构函数,而不是派生类的。
say_basic* p;
p = new say_basic1;
delete p;//此时会调用基类say_basic的析构函数进行删除
原因
撤销指针所指的派生类的无名对象,调用析构函数时,采用了静态连编方式,只调用了基类Base的析构函数。
此时虚析构函数就用于解决该问题,即先调用派生类的析构函数。
需要再基类中的析构函数定义时声明virtual,格式如下:
virtual ~派生类名(){}
示例:
#include <iostream>
#include <string>
using namespace std;
class say_basic
{
public:
virtual ~say_basic()
{
cout << "基类析构函数调用" << endl;
}
string word;
say_basic(string default_word = "human") :word(default_word){}
virtual void say_something() {
cout << "hellow " << word << endl;
}
string get_type() {
return word;
}
};
class say_basic1: public say_basic
{
public:
~say_basic1()
{
cout << "派生类析构函数调用" << endl;
}
say_basic1(string default_word = "man") : say_basic(default_word){}
void say_something()
{
cout << "hi " << word << endl;
}
};
int main()
{
say_basic* p;
p = new say_basic1;
delete p;//此时会先调用派生类say_basic1的析构函数
}
结果:
纯虚函数
在声明虚函数时被初始化为0的函数,定义格式:
virtual 函数类型 函数名(参数) = 0
作用:
是在基类中为其派生类保留一个函数的名字,以便派生类根据需要重新定义。
抽象类
定义:当一个类中至少有一个纯虚函数,则该类为抽象类。
抽象类相关规定:
1、抽象类只能作为基类来使用,不能建立抽象类对象。
2、不允许从具体类派生出抽象类。所谓具体类,就是不包含纯虚函数的普通类。
3、抽象类不能用作函数的参数类型、函数的返回类型或是显式转换的类型。
4、可以声明指向抽象类的指针或引用,此指针可以指向它的派生类,进而实现多态性。
5、如果派生类中没有定义纯虚函数的实现,而派生类中只是继承基类的纯虚函数,则这个派生类仍然是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体类了。
整合示例:求面积
#include <iostream>
using namespace std;
/*** 定义一个公共基类 ***/
class Figure{
protected:
double x, y;
public:
Figure(double a, double b): x(a), y(b) { }
virtual void getArea() //虚函数
{
cout << "No area computation defind for this class.\n";
}
};
class Triangle: public Figure{
public:
Triangle(double a, double b): Figure(a, b){ }
//虚函数重定义,用于求三角形的面积
void getArea(){
cout << "Triangle with height " << x << " and base " << y;
cout << " has an area of " << x * y * 0.5 << endl;
}
};
class Square: public Figure{
public:
Square(double a, double b): Figure(a, b){ }
//虚函数重定义,用于求矩形的面积
void getArea(){
cout << "Square with dimension " << x << " and " << y;
cout << " has an area of " << x * y << endl;
}
};
class Circle: public Figure{
public:
Circle(double a): Figure(a, a){ }
//虚函数重定义,用于求圆的面积
void getArea(){
cout << "Circle with radius " << x ;
cout << " has an area of " << x * x * 3.14 << endl;
}
};
int main(){
Figure *p;
Triangle t(10.0, 6.0);
Square s(10.0, 6.0);
Circle c(10.0);
p = &t;
p->getArea();
p = &s;
p->getArea();
p = &c;
p->getArea();
return 0;
}