多态中的虚函数
- 向上类型转换
1 enum note {middleC,Csharp,Cflat}; 2 class Instrument { 3 public: 4 void play(note) const { 5 cout << "Instrument::play" << endl; 6 } 7 }; 8 9 class Wind: public Instrument { 10 public: 11 void play(note) const { 12 cout << "Wind::play" << endl; 13 } 14 }; 15 16 void ture(Instrument& i) { 17 18 i.play(middleC); 19 } 20 int main() 21 { 22 Wind flute; 23 tune(flute); 24 //最终打印结果为Instrument::play,向上类型转换虽然对象属于Wind, 25 //但是Wind继承于Instrument,这样ture(Instrument& i)即使传入的是Wind类,也能向上转换为Instrument类的对象 26 27 }
变为虚函数后的结果就不同了
1 enum note {middleC,Csharp,Cflat}; 2 class Instrument { 3 public: 4 void play(note) const { 5 cout << "Instrument::play" << endl; 6 } 7 }; 8 9 class Wind: public Instrument { 10 public: 11 virtual void play(note) const { 12 cout << "Wind::play" << endl; 13 } 14 }; 15 16 void ture(Instrument& i) { 17 18 i.play(middleC); 19 } 20 int main() 21 { 22 Wind flute; 23 tune(flute);//最终打印结果为Wind::play ,加上virtual关键字后,就可以保证继承的可扩展性,保证派生类自已拥有不同于基类的功能,实现函数的重新定义。 24 }
- 抽象基类和纯虚函数
在类的设计中,通常将基类作为派生类的一个标准接口,一般不创建基类的对象,建立基础的模板,它的派生类去重新定义函数的功能。纯虚函数禁止对抽象类的函数以传值方式调用,会发生对象切片,通过引用或指针可以避免。
1 enum note {middleC,Csharp,Cflat}; 2 class Instrument { 3 public: 4 virtual void play(note) const = 0; 5 virtual char* what() const = 0; 6 virtual void adjust(int) = 0; 7 }; 8 9 class Wind: public Instrument { 10 public: 11 void play(note) const 12 { 13 cout << "Wind::play" << endl; 14 } 15 char* what() const 16 { 17 return "Wind"; 18 19 } 20 void adjust(int) {} 21 }; 22 class Percussion : public Instrument { 23 public: 24 void play(note) const 25 { 26 cout << "Percussion::play" << endl; 27 } 28 char* what() const 29 { 30 return "Percussion"; 31 32 } 33 void adjust(int) {} 34 }; 35 class Stringed : public Instrument { 36 public: 37 void play(note) const 38 { 39 cout << "Stringed ::play" << endl; 40 } 41 char* what() const 42 { 43 return "Stringed"; 44 45 } 46 void adjust(int) {} 47 }; 48 class Brass : public Wind { 49 public: 50 void play(note) const 51 { 52 cout << "Brass ::play" << endl; 53 } 54 char* what() const 55 { 56 return "Brass"; 57 58 } 59 }; 60 class Woodwind : public Wind { 61 public: 62 void play(note) const 63 { 64 cout << "Woodwind ::play" << endl; 65 } 66 char* what() const 67 { 68 return "Woodwind"; 69 70 } 71 }; 72 void ture(Instrument& i) { 73 74 i.play(middleC); 75 } 76 void f(Instrument& i) 77 { 78 i.adjust(1); 79 } 80 int main() 81 { 82 Wind flute; 83 Percussion drum; 84 Stringed violin; 85 Brass fluge; 86 Woodwind recorder; 87 tune(flute); 88 tune(drum); 89 tune(violin); 90 tune(fluge); 91 tune(recorder); 92 f(fluge); 93 }
- 对象切片
解决多态中,基类对象操作派生类对象的问题,对象切片会使基类访问不到派生类,不能使多态有意义。
1 class Pet { 2 string pname; 3 public: 4 Pet(const string& name) :pname(name) {} 5 virtual string name() const { return pname; } 6 virtual string description()const 7 { 8 return pname; 9 } 10 }; 11 12 class Dog : public Pet { 13 string factive; 14 public: 15 Dog(const string& name, const string& active) :Pet(name), factive(active) {} 16 string description() { 17 return factive; 18 } 19 }; 20 void description(Pet p) //传入的使值,不是引用不是指针地址 21 { 22 cout << p.description() << endl; 23 } 24 25 int main() 26 { 27 Pet p("Alfred"); 28 Dog d("Fluffy","sleep"); 29 description(p); 30 description(d); 31 //我们希望description(p)出现Alfred结果,description(d)出现sleep结果, 32 //但是实际最终的结果是description(d)出现Fluffy,调用的同样是基类的函数, 33 //而不是派生类的。因为传值的过程不能改变地址。 34 }
- 重载和重新定义
重新定义一个基类的重载函数时,将会隐藏所有该重载函数的其他基类函数,但是当对虚函数进行操作重新定义时会不太一样。下面的d4和引用是其中的区别.
1 class Base { 2 public: 3 virtual int f()const { 4 cout << "Base::f()"; 5 return 1; 6 } 7 virtual void f(string) const {} 8 virtual void g() const {} 9 }; 10 11 class Derived1 :public Base { 12 public: 13 void g() cont {} 14 }; 15 class Derived2 :public Base { 16 public: 17 int f() cont { 18 cout << "Derived2::f()"; 19 return 2; 20 } 21 }; 22 class Derived3 :public Base { 23 public: 24 /*void f() const { 25 cout << "Derived3::f()"; 26 return 3; 27 }*///非法,不能改变返回类型 28 }; 29 class Derived4 :public Base { 30 public: 31 int f(int) cont { 32 cout << "Derived4::f()"; 33 return 3; 34 } 35 }; 36 int main() 37 { 38 srting s("hello"); 39 Derived1 d1; 40 int x = d1.f(); 41 d1.f(s); 42 Derived2 d2; 43 x = d2.f(); 44 //d2.f(s);隐藏 45 Derived4 d4; 46 x = d4.f(1); 47 //x=d4.f();隐藏 48 //d4.f(s);隐藏 49 Base& br = d4; 50 //br.f(1); 51 br.f(); 52 br.f(s);//与Derived4 d4相反 53 }