多态:使用相同的调用方法,调用不同对象,自己类里面函数
人类用手吃饭是本能;
英国人用刀叉吃饭;
中国人用筷子吃饭;
我们问“这个人怎么吃饭的?”,
应该根据其国别来回答,而不是简单地说“用手吃”。
这就是多态。
1 #include <iostream> 2 #include <string.h> 3 #include <unistd.h> 4 5 using namespace std; 6 7 class Human{ 8 9 private: 10 int a; 11 public: 12 virtual void eating(void){cout<<"use hand to eat"<<endl;}//用到时会被替换成当前的 13 14 15 }; 16 17 class Englishhuman : public Human{ 18 public: 19 void eating(void){cout<<"use knife to eat"<<endl;} 20 21 }; 22 23 class Chinese : public Human{ 24 public: 25 void eating(void){cout<<"use chopsticks to eat"<<endl;} 26 27 }; 28 29 void test_eating (Human &h) 30 { 31 h.eating(); 32 } 33 34 int main(int argc,char **argv) 35 { 36 Human h; 37 Englishhuman e; 38 Chinese c; 39 40 test_eating(h); 41 test_eating(e); 42 test_eating(c); 43 44 cout<<"sizeof(Human) = "<<sizeof(h)<<endl; 45 cout<<"sizeof(Englishhuan) = "<<sizeof(e)<<endl; 46 cout<<"sizeof(Chinese) = "<<sizeof(c)<<endl; 47 48 return 0; 49 }
原理:
对于虚函数,采用动态联编:有虚函数的对象里有一个指针,指向虚函数表;
调用虚函数时,会根据对象里的指针找到表,从表中取出函数来执行
对于非虚函数,采用静态联编:编译时就确定调用哪个函数
差别:
静态联编效率高,动态联编支持多态
注意:由于继承了Human所以对应的与Human中函数相同的函数也为虚函数
1 #include <iostream> 2 #include <string.h> 3 #include <unistd.h> 4 5 using namespace std; 6 7 class Human{ 8 public: 9 void eating(void){cout<<"use hand to eat"<<endl;} 10 11 12 }; 13 14 class Englishhuman : public Human{ 15 public: 16 void eating(void){cout<<"use knife to eat"<<endl;} 17 18 }; 19 20 class Chinese : public Human{ 21 public: 22 void eating(void){cout<<"use chopsticks to eat"<<endl;} 23 24 }; 25 26 void test_eating (Human &p) 27 { 28 h.eating(); 29 } 30 31 int main(int argc,char **argv) 32 { 33 Human h; 34 Englishhuman e; 35 Chinese c; 36 37 test_eating(h);/* Human &p = h里面的Person部分; 38 * p引用的是"h里面的Person部分" 39 */ 40 test_eating(e); 41 test_eating(c); 42 43 return 0; 44 }
多态的限制:
1.test_func(Human* h):
test_func(Human& h):使用指针或引用来使用对象时,才有多态
test_func(Human h):传值时,无多态
2.只有类的成员函数才能声明为虚函数
3.静态成员函数不能是虚函数
4.内联函数不能是虚函数(使用inline关键字的函数)
5.构造函数不能是虚函数
6.析构函数一般都声明为虚函数
7.重载:函数参数不同,不可设为虚函数
覆盖:函数参数、返回值相同,可以设为虚函数
8.返回值例外:
函数参数相同,但是返回值是当前对象的指针或引用时,
也可以设为虚函数(除此之外不可以设置为虚函数,否则会出现错误)
1 #include <iostream> 2 #include <string.h> 3 #include <unistd.h> 4 5 using namespace std; 6 7 class Human{ 8 9 private: 10 int a; 11 public: 12 virtual void eating(void){cout<<"use hand to eat"<<endl;}//用到时会被替换成当前的 13 14 15 }; 16 17 class Englishhuman : public Human{ 18 public: 19 void eating(void){cout<<"use knife to eat"<<endl;} 20 21 }; 22 23 class Chinese : public Human{ 24 public: 25 void eating(void){cout<<"use chopsticks to eat"<<endl;} 26 27 }; 28 29 void test_eating (Human h)//传值时没有对态 30 { 31 h.eating(); 32 } 33 34 int main(int argc,char **argv) 35 { 36 Human h; 37 Englishhuman e; 38 Chinese c; 39 40 test_eating(h); 41 test_eating(e); 42 test_eating(c); 43 44 cout<<"sizeof(Human) = "<<sizeof(h)<<endl; 45 cout<<"sizeof(Englishhuan) = "<<sizeof(e)<<endl; 46 cout<<"sizeof(Chinese) = "<<sizeof(c)<<endl; 47 48 return 0; 49 }
对应第六点
1 #include <iostream> 2 #include <string.h> 3 #include <unistd.h> 4 5 using namespace std; 6 7 class Human{ 8 9 private: 10 int a; 11 public: 12 virtual void eating(void){cout<<"use hand to eat"<<endl;}//用到时会被替换成当前的 13 virtual ~Human(){cout<<"~Human()"<<endl;} 14 15 16 }; 17 18 class Englishhuman : public Human{ 19 public: 20 void eating(void){cout<<"use knife to eat"<<endl;} 21 virtual ~Englishhuman(){cout<<"~Englishman()"<<endl;} 22 }; 23 24 class Chinese : public Human{ 25 public: 26 void eating(void){cout<<"use chopsticks to eat"<<endl;} 27 virtual ~Chinese(){cout<<"~Chinese()"<<endl;} 28 }; 29 30 void test_eating (Human h) 31 { 32 h.eating(); 33 } 34 35 int main(int argc,char **argv) 36 { 37 Human* h = new Human; 38 Englishhuman* e = new Englishhuman; 39 Chinese *c = new Chinese; 40 41 Human *p[3] = {h,e,c}; 42 int i; 43 44 for (i = 0; i < 3; i++) 45 { 46 p[i]->eating(); 47 delete p[i]; 48 } 49 50 return 0; 51 }
对应第八点
1 #include <iostream> 2 #include <string.h> 3 #include <unistd.h> 4 5 using namespace std; 6 7 class Human{ 8 9 private: 10 int a; 11 public: 12 virtual void eating(void){cout<<"use hand to eat"<<endl;} 13 virtual ~Human(){cout<<"~Human()"<<endl;} 14 virtual Human* test(void){cout <<"Human\'s test"<<endl;return this;} 15 16 17 }; 18 19 class Englishhuman : public Human{ 20 public: 21 void eating(void){cout<<"use knife to eat"<<endl;}//由于继承了Human所以次函数也为虚函数 22 virtual ~Englishhuman(){cout<<"~Englishman()"<<endl;} 23 virtual Englishhuman* test(void){cout<<"Englishman\'s test"<<endl;return this;} 24 }; 25 26 class Chinese : public Human{ 27 public: 28 void eating(void){cout<<"use chopsticks to eat"<<endl;}////由于继承了Human所以次函数也为虚函数 29 virtual ~Chinese(){cout<<"~Chinese()"<<endl;} 30 virtual Chinese* test(void){cout<<"Chinese\'s test"<<endl;return this;} 31 }; 32 33 void test_eating (Human &h) 34 { 35 h.eating(); 36 } 37 38 void test_return(Human &h) 39 { 40 h.test(); 41 } 42 43 int main(int argc,char **argv) 44 { 45 Human h; 46 Englishhuman e; 47 Chinese c; 48 49 test_return(h); 50 test_return(e); 51 test_return(c); 52 53 return 0; 54 }
类型转换:
C++中的类型转换:
dynamic_cast:
格式:dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。
Type-id必须是类的指针、类的引用或者void *;
如果type-id是类指针类型,那么expression也必须是一个指针;
如果type-id是一个引用,那么expression也必须是一个引用。
1. 用于多态场合,即:必须有虚函数
2. 主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换
3. 在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
static_cast:
格式:static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,
但没有运行类型检查来保证转换的安全性。
1. 用于类层次结构中基类和子类之间指针或引用的转换。
2. 进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
3. 进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。
4. 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum:这种转换的安全性也要开发人员来保证。
5. 把void指针转换成目标类型的指针(不安全!!)
6. 把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
reinterpret_cast:
格式:reinterpret_cast<type-id> (expression)
相当于C风格的用小括号"(type-id)"实现的强制类型转换
1. type-id必须是一个指针、引用、算术类型、函数指针或者成员指针
2. 它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针
3. 跟C风格的强制转换类似,没有安全性检查
const_cast:
格式:const_cast<type_id> (expression)
该运算符用来去除原来类型的const或volatile属性。
除了const 或volatile修饰之外, type_id和expression的类型是一样的。
对应第十四天的文件夹