c++基础 五 继承 与kotlin中一样,类的继承使用 : 表示。 不同的是,c++继承分为私有继承、公开继承,默认为私有继承
1.默认是 隐式代码: : private Person
2.私有继承:在子类里面是可以访问父类的成员,但是在类的外面不行
3.必须公开继承,才可以访问父类的成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class Father { private : int age; public : char * name; }; class Son : Father { public : void setName () { this ->name = "son" ; } char * getName () { return this ->name; } }; class Daugther : public Father { public : void setName () { this ->name = "dauther" ; } char * getName () { return this ->name; } }; int main () { cout << "HELLO WORLD!" << endl ; Son son; son.setName(); Daugther daugther; daugther.setName(); cout << "daugther.name : " << daugther.name << endl ; return 0 ; }
总结:
子类不能访问父类的私有属性
子类直接继承为私有继承
私有继承的子类对象不能访问父类的属性,只能在类中访问
公开继承的子类对象可以访问父类的属性,在类中同样可以
多继承
C++ 是允许多继承的
Java语言不允许多继承,多继承有歧义,如果Java语言多继承 就会导致代码不健壮,(二义性)
Java多实现:做的非常棒,严格避免出现 二义性问题(歧义)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Mother {public : void raisChild () { cout << "Mother raisChild" << endl ; } void work () { cout << "Mother work" << endl ; } void play () { cout << "Mother play" << endl ; } void playWithChild () { cout << "Mother playWithChild" << endl ; } }; class Father {public : void smoking () { cout << "Father smoking" << endl ; } void work () { cout << "Father work" << endl ; } void play () { cout << "Father play" << endl ; } void playWithChild () { cout << "Father playWithChild" << endl ; } }; class Son : public Mother, public Father {public : void work () { cout << "Son work" << endl ; } void play () { cout << "Father play" << endl ; } }; int main () { cout << "HELLO WORLD!" << endl ; Son son; son.work(); son.play(); son.raisChild(); son.playWithChild(); return 0 ; }
如上述代码所示,Son类同时继承自Father、Mother类,这两个类有一些相同的函数work、play、playWithChild 但子类Son也实现了work、play时,执行这些函数不会报错, 但调用playWithChild则会报错,因为两个父类都有该函数、但子类未实现,此时就存在二义性问题。 解决这样的问题,存在两个办法:
1、指定调用哪个父类的该函数 使用 ::父类.函数名son.Father::playWithChild();
2、在子类也实现该函数 在Son类中添加playWithChild函数。
总结:
1、c++可以多继承
2、多继承时,如果多个父类间存在同名同参函数,子类指针使用时,会存在二义性问题,需要子类也实现该函数,或者指定父类调用
3、真实开发时,一般时子类也会定义同名成员,覆盖掉多个父类同名成员。
虚继承 二义性问题出现在菱形继承。 关于二义性问题,还有一种解决办法,就是虚继承。 第三种解决方案: 【虚基类】 属于 虚继承的范畴 真实C++开始,是很少出现,二义性(歧义) 如果出现, 系统源码(系统用 第三种解决方案)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 // 祖父类 class Object{ public: int number; void show() { cout << "Object show run..." << endl; } }; // 等下讲 virtual 的原理是什么 ... // 父类1 class BaseActivity1 : virtual public Object { // public:int number; // 人为制作二义性 error: request for member 'number' is ambiguous }; // 父类2 class BaseActivity2 : virtual public Object { // public:int number; }; // 子类 class Son : public BaseActivity1, public BaseActivity2 { }; int main() { cout << "HELLO WORLD!!!!" << endl; Object object; BaseActivity1 baseActivity1; BaseActivity2 baseActivity2; Son son; object.number = 100; baseActivity1.number = 200; baseActivity2.number = 300; son.number = 400; object.show(); baseActivity1.show(); baseActivity2.show(); son.show(); cout << object.number << endl; cout << baseActivity1.number << endl; cout << baseActivity2.number << endl; cout << son.number << endl; return 0; }
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class)
上面的例子中:Object就是虚基类,BaseActivity1、BaseActivity2都是虚继承于Object,然后Son都公开继承于BaseActivity1、BaseActivity2 在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。
虚函数 多态(虚函数)。 动态多态(程序的角度上:程序在运行期间才能确定调用哪个类的函数 == 动态多态的范畴) Java语言默认支持多态 C++默认关闭多态,怎么开启多态? 虚函数 在父类上给函数增加 virtual关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class BaseActivity { public : virtual void onStart () { cout << "BaseActivity onStart" << endl ; } }; class HomeActivity : public BaseActivity { public : void onStart () { cout << "HomeActivity onStart" << endl ; } }; class MyActivity : public BaseActivity {public : void onStart () { cout << "MyActivity onStart" << endl ; } }; void onActivityStart (BaseActivity * baseActivity) { baseActivity->onStart(); } int main () { cout << "HELLO WORLD!!" << endl ; HomeActivity * homeActivity = new HomeActivity(); MyActivity * myActivity = new MyActivity(); onActivityStart(homeActivity); onActivityStart(myActivity); if (myActivity && homeActivity) { delete homeActivity; homeActivity = NULL ; delete myActivity; myActivity = NULL ; } return 0 ; }
多态的定义 * 父类的引用指向之类的对象,同一个方法有不同的实现,重写(动态多态)和 重载(静态多态)
函数重载 函数重载即为 静态多态 重载:函数名相同,但入参不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int add (int a, int b) { cout << "int: " << a + b << endl ; return a + b; } float add (float a, float b) { cout << "float: " << a + b << endl ; return a + b; } double add (double a, double b) { cout << "double: " << a + b << endl ; return a + b; } int main () { cout << "HELLO WORLD!!" << endl ; add(1 , 2 ); add(1.3f , 4.5f ); add(1.45 , 1.67 ); return 0 ; }
纯虚函数 c++纯虚函数类似于java中的抽象类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class BaseActivity { private : void setContentView (int layoutId) { cout << "XmlResourceParser解析布局文件信息... 反射" << endl ; } public : void onCreate () { setContentView(getLayoutId()); initData(); initView(); } virtual int getLayoutId () = 0 ; virtual void initData () = 0 ; virtual void initView () = 0 ; }; class HomeActivity : public BaseActivity {public : int getLayoutId () { return 100 ; } void initData () { cout << "HomeActivity initData" << endl ; } void initView () { cout << "HomeActivity initView" << endl ; } }; int main () { cout << "Hello world!" << endl ; HomeActivity home; home.onCreate(); return 0 ; }
注意纯虚函数记得在函数后面写 = 0 如果不写 = 0 则会运行报错
全纯虚函数 如果类中的函数都是虚函数,则这个类可以被成为全纯虚函数, 相当于java中的接口
回调 与java中的函数回调类似1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 class User { public : int id; string name; string mobile; User(int id, string name, string mobile): id(id), name(name), mobile(mobile){ this -> toString(); } void toString () { cout << "用户名:" << this ->name << " 用户id:" << this ->id << " 用户手机号:" << this ->mobile << endl ; } }; class ILognResult {public : virtual void success (int code, User user) = 0 ; virtual void fail (int code, string message) = 0 ; }; void loginAction (string userName, string password, ILognResult & loginResult) { if (userName.empty() || password.empty()) { cout << "登录的账号与用户名都不能为空" << endl ; return ; } if ("justin" == userName && "123321qQ" == password) { loginResult.success(200 , User(1121 , "justin" , "13163396276" )); } else { loginResult.fail(404 , "用户名或密码错误" ); } } class LoginResultImpl : public ILognResult {public : void success (int code, User user) { cout << "登录成功:userName:" << user.name << " mobile: " << user.mobile << endl ; } void fail (int code, string message) { cout << "登录失败:错误状态码:" << code << " 错误信息: " << message << endl ; } }; int main () { cout << "HELLO WORLD!" << endl ; string userName; cout << "请输入用户名" << endl ; cin >> userName; string password; cout << "请输入密码" << endl ; cin >> password; LoginResultImpl loginResult; loginAction(userName, password, loginResult); return 0 ; }
如上代码就是一个登录操作的回调 其实是非常类似于 java中的接口回调的
模版函数 c++ 中没有范型概念,但是有模版函数,与java中的范型类似
像我们在之前的 静态多态中对于重载时的举例,加法 当我们针对不同的数据类型都需要进行一次重载,但当我们使用函数模版时,写一次即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 template <typename T>T add (T a, T b) { cout << "result:" << a + b << endl ; return a + b; } int main () { cout << "HELLO WORLD!" << endl ; int a = add(1 , 3 ); float f = add(1.4f , 66.8f ); double d = add(3.6 , 88.99 ); cout << "a:" << a << endl ; cout << "f:" << f << endl ; cout << "d:" << d << endl ; return 0 ; }
如上所示,使用模版函数完成了各个类型的加法计算,使用也与java中的范型及其类似