<C++> 类(3):初始化列表 常函数和常量对象 虚函数与多态(包括纯虚函数)
一.初始化列表(初始化列表中必须有的两个内容)
1.类中const的成员变量:
①特点:不能修改 必须初始化 在构造函数后面加冒号 格式为:“:变量名(值)”
也就是说 常量必须在初始化列表中初始化
②执行顺序:构造函数先执行初始化列表 然后执行函数中的内容
1 #include<iostream> 2 using namespace std; 3 4 class CPerson 5 { 6 public: 7 const int a; 8 public: 9 CPerson():a(100) 10 { 11 12 } 13 void Show() 14 { 15 cout << a << endl; 16 } 17 }; 18 19 int main() 20 { 21 CPerson ps; 22 ps.Show(); 23 24 return 0; 25 }
2.组合关系的类中的构造函数有参数:
①例:包含了另一个类的对象要执行指定的构造函数
1 #include<iostream> 2 using namespace std; 3 4 class CHead 5 { 6 public: 7 CHead(int a) 8 { 9 10 } 11 }; 12 13 class CPerson 14 { 15 public: 16 CHead head; 17 public: 18 CPerson():head(100) 19 { 20 21 } 22 }; 23 24 int main() 25 { 26 CPerson ps; 27 28 return 0; 29 }
二.const常函数和常量对象
1.常函数:
①基本格式:void Show() const{ .. }
②特点:不能修改类中的成员变量
可以理解为 常函数参数括号中传的指针已经变成const CPerson* this这种指针了(拿CPerson为例)
也就是说 this已经变成const类型 这种指针的指向是不可以修改的 但是可以使用
2.常量对象:
①格式:const CPerson ps;
②特点:常量对象只能调用常函数 不能使用普通函数
可以理解为ps.Show(const CPerson*); 传入对象的地址类型不可改变
下面是一个常函数和常量函数的简单例子:
1 #include<iostream> 2 using namespace std; 3 4 class CPerson 5 { 6 public: 7 int m_nAge; 8 public: 9 CPerson() 10 { 11 m_nAge = 100; 12 } 13 public: 14 void Show() const 15 { 16 cout << m_nAge << endl; 17 } 18 void Show1() 19 { 20 m_nAge = 200; 21 cout << m_nAge << endl; 22 } 23 }; 24 25 int main() 26 { 27 const CPerson ps; 28 CPerson ps1; 29 ps.Show(); 30 ps1.Show1(); 31 32 return 0; 33 }
三.虚函数与多态
1.引入:
①重载:同名 参数列表不同 调用父类的内容时要加作用域
②重写:同名 参数列表相同 全一致
③隐藏:是纵向重载 只要同名就好
2.父类的指针指向子类的对象:
①例如:CFather* pFather = new CSon;
②注意:这类指针只能看到父类的东西 但是子类的指针不能指向父类的对象
3.虚函数与多态:利用父类的复用性 充当接口类
①虚函数:virtual
作用:通过父类的指针调用子类的函数
注意:虚函数实现多态是基于重写来实现的 这使父类的指针具有多种形态
如果子类中重写了父类的函数 那么在虚函数列表中父类就被子类所覆盖
覆盖基于虚函数的重写 重写在子类中进行重写
1 #include<iostream> 2 using namespace std; 3 4 class CWater 5 { 6 public: 7 virtual void Show() 8 { 9 cout << "CWater::Show()" << endl; 10 } 11 }; 12 13 class CMilk : public CWater 14 { 15 public: 16 void Show() 17 { 18 cout << "CMilk::Show()" << endl; 19 } 20 }; 21 22 class CBeer : public CWater 23 { 24 public: 25 void Show() 26 { 27 cout << "CBeer::Show()" << endl; 28 } 29 }; 30 31 void Bottle(CWater* cw) 32 { 33 cw -> Show(); 34 } 35 36 int main() 37 { 38 CMilk cm; 39 CBeer cb; 40 41 Bottle(&cm); 42 Bottle(&cb); 43 44 return 0; 45 }
②多态:
用父类的指针指向子类 通过父类的指针调用子类成员函数
1 #include<iostream> 2 using namespace std; 3 4 class CFather 5 { 6 public: 7 virtual void AA() 8 { 9 cout << "CFather::AA()" << endl; 10 } 11 virtual void BB() 12 { 13 cout << "CFather::BB()" << endl; 14 } 15 void CC() 16 { 17 cout << "CFather::CC()" << endl; 18 } 19 }; 20 21 class CSon : public CFather 22 { 23 public: 24 virtual void AA() 25 { 26 cout << "CSon::AA()" << endl; 27 } 28 void CC() 29 { 30 cout << "CSon::CC()" << endl; 31 } 32 void DD() 33 { 34 cout << "CSon::DD()" << endl; 35 } 36 }; 37 38 int main() 39 { 40 CFather* cf1 = new CSon; 41 42 cf1 -> AA(); 43 cf1 -> BB(); 44 cf1 -> CC(); 45 46 return 0; 47 }
4.(感觉上面整理的好乱)总结虚函数与多态:
①什么是多态:
父类的指针指向一个子类的对象 通过父类的指针调用实际子类的成员函数 这样才会使父类指针具有多种形态
②多态基于什么实现:
多态基于虚函数 虚函数基于重写
③多态的实现原理:虚函数列表和虚指针
在虚函数列表中 表中的每个元素都是一个函数指针 指向虚函数 或重写的虚函数
虚函数列表是在编译的时候就存在的 每一个类都有一个(对于普通的单继承来说)
虚指针就是记录使用哪一个列表的指针 在构造函数中被初始化 指向子类虚函数列表 这个指针是一个类成员
虚指针在创建对象的时候存在 在这个对象的首地址的前4个字节就是这个指针
那么我们就可以把调用一个虚函数理解为:通过虚指针拿到虚函数列表中的函数指针来实现调用的
④优缺点:
优点就是提高了复用性 扩展性
缺点就是安全性比较低 空间和效率问题也比较差
最后再放一个典型的例子加深理解:这段代码的输出足以说明这个问题
1 #include<iostream> 2 using namespace std; 3 4 class CFather 5 { 6 public: 7 void AA() 8 { 9 cout << "CFather::AA()" << endl; 10 } 11 virtual void BB() 12 { 13 cout << "CFather::BB()" << endl; 14 } 15 }; 16 17 class CSon : public CFather 18 { 19 public: 20 void AA() 21 { 22 cout << "CSon::AA()" << endl; 23 } 24 virtual void BB() 25 { 26 cout << "CSon::BB()" << endl; 27 } 28 }; 29 30 int main() 31 { 32 CFather* cf = new CSon; 33 cf -> AA(); 34 cf -> BB(); 35 36 CFather* cf1 = new CFather; 37 cf1 -> AA(); 38 cf1 -> BB(); 39 40 CSon* cs = new CSon; 41 cs -> AA(); 42 cs -> BB(); 43 44 return 0; 45 }
5.调用函数的区别:
①调用普通函数 调用哪一个函数 跟指针类型有关
②调用虚函数 看当前使用的虚函数列表是哪一个类的
③有作用域的话代表已经确定了 作用域的优先级别是最高的
6.纯虚函数:
①格式:virtual void 函数名() = 0;
②注意:
如果一个类包含纯虚函数 那这个类叫抽象类 抽象类是不可以定义对象的
在派生类中一定要实现纯虚函数 那么这个派生类 也叫具体类
如果一个类中的所有函数都是纯虚函数 那么这个类叫接口类
1 #include<iostream> 2 using namespace std; 3 4 class CPerson 5 { 6 public: 7 virtual void Eat() = 0; 8 }; 9 10 class CChina : public CPerson 11 { 12 virtual void Eat() 13 { 14 cout << "CChina::Eat()" << endl; 15 } 16 }; 17 18 int main() 19 { 20 CPerson* cp = new CChina; 21 cp -> Eat(); 22 23 return 0; 24 }