1. 相同函数名具有多态性:
重载 | 覆盖 | 隐藏 | |
共同点: | 函数名相同 | 函数名相同 | 函数名相同 |
不同点: |
同类、参数类型、数量不同 或 有无const |
不同类,同参,有基virtual |
不同类,同参,且无virtual 不同类,不同参(不论有无virtual) |
体现: | 由函数调用(静态联编) | 由函数调用取决于object(动态联编) | 取决于pointer(不能体现多态性) |
① 译时的多态(由函数名来调用时体现):重载:同类,不同参
② 运行时的多态(用指向不同类的指针来调用):
覆盖: 不同类,同参,基类有virtual(由指针指向的类型来决定,体现了多态性)
隐藏:①不同类,同参,基类无virtual②不同类,不同参(不论有无virtual)(由指针来决定,不能体现多态性)
1. 为什么要使用多重继承
多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数。C++多态性是通过虚函数(virtual)来实现的。
2. 在派生类中增加函数
RTTI: ①typeid返回指针或引用所指的实际类型
②dynamic_cast 将基类类型的指针或引用安全的转换成派生类类型的指针或引用
dynamic_cast<son*>pf->beautiful();
注意:①硬性转换尽量少用②必须使用虚函数
3. 使用多重继承
使用多重继承可以避免:①在基类放接口函数②用RTTI将父类的指针转换成派生类的
调用多态性,必须要用虚析构函数
4. 模拟抽象类
抽象类的作用:
为了实现一个统一的指针,我们可以定义一个类,再由这个类派生出父类和母类
5. 纯虚函数与抽象类
☆抽象类起到接口的作用,便于父类指针调用子类的对象
virtual viod A()=0;包含一个或者多个纯虚函数的类叫抽象类;
☆纯虚函数只起到接口的作用,要且必须在子类中重新定义
纯虚基类只能申明抽象类的指针,不能开辟抽象类的空间
6. 抽象类实例
7. 复杂的抽象结构
8. 慎用多重继承
在用单一继承可以实现的情况下不要使用多重继承
1 #include <iostream> 2 using namespace std; 3 class animal 4 { 5 public: 6 animal(int); 7 virtual ~animal(){cout<<"析构动物.. ";} 8 virtual int getage() {return itsage;} 9 virtual void sleep()=0; //声明6个纯虚函数 10 virtual void eat()=0; 11 virtual void propagate()=0; 12 virtual void move()=0; 13 virtual void body()=0; 14 virtual void show()=0; 15 private: 16 int itsage; 17 }; 18 animal::animal(int age):itsage(age) 19 { 20 cout<<"创建动物... "; 21 } 22 class Mammalia:public animal //派生了另一个抽象类哺乳动物类 23 { 24 public: 25 Mammalia(int age):animal(age){cout<<"创建哺乳类... ";} //子类在构造自己的同时也要构造基类部分 26 virtual ~Mammalia(){cout<<"析构哺乳类... ";} 27 virtual void propagate(){cout<<"哺乳类是胎生动物,通过胚胎来繁殖后代。 ";} 28 //该类仅仅覆盖了基类的繁殖方法propagate() 29 }; 30 class bird:public animal //鸟类将动物类的6个纯虚函数全部覆盖,因此该类不是抽象类 31 { 32 public: 33 //子类在构造自己的同时也要构造基类部分 34 bird(int age):animal(age){cout<<"创建鸟类... ";} 35 virtual ~bird(){cout<<"析构鸟类... ";} 36 virtual void sleep(){cout<<"鸟类喜欢站着睡觉。 ";} 37 virtual void eat(){cout<<"极个别鸟类吃肉,其他都是吃素。 ";} 38 virtual void propagate(){cout<<"鸟类是卵生动物,通过排卵来繁殖后代.";} 39 virtual void move(){cout<<"鸟类可以飞... ";} 40 virtual void body(){cout<<"鸟类体表被覆羽毛,前肢变为翼!";} 41 virtual void show(){cout<<"鸟类的一般寿命为:"<<getage()<<endl;} 42 }; 43 class human:public Mammalia //人类从抽象类--哺乳动物类派生而来 44 { 45 public: 46 human(int age):Mammalia(age){cout<<"创建人类... ";} //子类在构造自己的同时也要构造基类部分 47 virtual ~human(){cout<<"析构人类... ";} 48 //由于基类是个抽象类,因此如果要使该类起作用,那么就要将5个纯虚函数全部覆盖,这里覆盖了6个 49 virtual void body(){cout<<"人类体表无毛... ";} 50 virtual void sleep(){cout<<"人类睡觉也很讲究,要在床上睡觉。 ";} 51 virtual void eat(){cout<<"人类吃饭很讲究,不吃生食。 ";} 52 virtual void move(){cout<<"人类靠两条腿走路。 ";} 53 virtual void propagate(){cout<<"人类通过胚胎繁殖后代. ";} 54 virtual void show(){cout<<"人类的一般寿命为:"<<getage()<<endl;} 55 }; 56 class pig:public Mammalia //猪类也是从抽象类--哺乳动物类派生而来 57 { 58 public: 59 //子类在构造自己的同时也要构造基类部分 60 pig(int age):Mammalia(age){cout<<"创建猪类... ";} 61 virtual ~pig(){cout<<"析构猪类... ";} 62 //这里也将抽象类Mammalia的6个方法全部覆盖 63 virtual void body(){cout<<"猪体表被毛... ";} 64 virtual void sleep(){cout<<"猪喜欢在烂泥里睡觉。 ";} 65 virtual void eat(){cout<<"猪虽然嘴谗,但是吃饭却不讲究。 ";} 66 virtual void move(){cout<<"猪用四肢走路。 ";} 67 virtual void propagate(){cout<<"猪类也通过胚胎来繁殖后代. ";} 68 virtual void show(){cout<<"猪类因为要被人宰了吃,所以一般寿命为:"<<getage()<<"年。"<<endl;} 69 }; 70 int main() 71 { 72 animal* ph=0; //声明一个指向动物类的指针ph 73 int choice=0; //定义一个选择变量choice,并将其值赋为0,假如不赋值,那么后面的if语句将无法对其进行判断。 74 bool quit=false; //声明一个布尔变量quit,将其值赋为false 75 while(choice<4) //假如输入的数值小于4,循环开始 76 { 77 choice=0; //由于switch无法对字符进行判断,所以每循环一次后要将choice的值归0,否则的话上一次输入的字符会在witch语句块中进行无休止的重复检测 78 cout<<"(1)猪类(2)人类(3)鸟类(0)退出:"; 79 cin>>choice; 80 switch(choice) 81 { 82 case 1:ph=new pig(1); //选择1,创建猪类对象,并初始化猪类的私有变量itsage的值 83 break; 84 case 2:ph=new human(80); //选择2,创建人类对象,并初始化人类的私有变量itsage的值 85 break; 86 case 3:ph=new bird(50); //选择3,创建鸟类对象,并初始化鸟类的私有变量itsage的值 87 break; //由于哺乳动物是个抽象类,不能实例化对象,因此没有设置该类的选项 88 default:quit=true; //假如选择了其他,那么默认将quit的值赋为true 89 break; 90 } 91 if(quit) //假如quit的值为真 92 break; //退出while循环 93 ph->show(); //用ph指针访问虚函数show,要注意,这里的show()不再是纯虚函数 94 ph->eat(); //用ph指针访问虚函数eat 95 ph->propagate(); //用ph指针访问虚函数propagate 96 ph->move(); //用ph指针访问虚函数move 97 ph->sleep(); //用ph指针访问虚函数sleep 98 ph->body(); //用ph指针访问虚函数body 99 cout<<" "; 100 } 101 return 0; 102 }
本章总结:
/** ************重载,重写(覆盖),隐藏的识别************* 重载:如何调用取决于参数 覆盖:如何调用取决于object(有virtual 同名 同参) 隐藏:如何调用取决于pointer a、编译时多态性:通过重载函数实现 b、运行时多态性:通过虚函数实现。 包含纯虚函数(virtual void funtion()=0 )的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。 小结:1、有virtual才可能发生多态现象 2、不发生多态(无virtual)调用就按原类型调用 */ #include<iostream> using namespace std; class Base { public: virtual void f(float x) { cout<<"Base::f(float)"<< x <<endl; } void g(float x) { cout<<"Base::g(float)"<< x <<endl; } void h(float x) { cout<<"Base::h(float)"<< x <<endl; } private: float x; }; class Derived : public Base { public: virtual void f(float x) { cout<<"Derived::f(float)"<< x <<endl; //多态(覆盖)必须不同类 } void g(int x) { cout<<"Derived::g(int)"<< x <<endl; //隐藏(参数不同。此时,不论有无virtual关键字, //基类的函数将被隐藏(注意别与重载混淆)。) } //重载必须要在同一个类中定义 void h(float x) { cout<<"Derived::h(float)"<< x <<endl; //隐藏(参数也相同,但是基类函数没有virtual关键字。 } //此时,基类的函数被隐藏(注意别与覆盖混淆)。) }; int main(void) { Derived d; Base *pb = &d; Derived *pd = &d; // Good : behavior depends solely on type of the object pb->f(3.14f); // Derived::f(float) 3.14 pd->f(3.14f); // Derived::f(float) 3.14 // Bad : behavior depends on type of the pointer pb->g(3.14f); // Base::g(float) 3.14 pd->g(3.14f); // Derived::g(int) 3 // Bad : behavior depends on type of the pointer pb->h(3.14f); // Base::h(float) 3.14 pd->h(3.14f); // Derived::h(float) 3.14 return 0; }