继承:程序员
派生类:数组
程序猿种类有不少种,如 C/C++ 程序猿,Java 程序猿,Python 程序猿等等。那么咱们要把程序猿设计成一个基类, 咱们则须要抽出其特有的属性和方法。函数
全部程序猿的共同属性(成员变量):微服务
全部的程序猿都有的共同方法(成员函数):post
而不一样的程序猿,又有各自不一样的属性和方法:大数据
继承的格式以下:人工智能
class 派生类名:public 基类名 { };
程序猿 Coder
基类:spa
class Coder { public: bool isWorkOvertime(){} // 是否要加班 bool isReward(){} // 是否有奖励 void Set(const string & name) // 设置名字 { m_name = name; } ... private: string m_name; // 姓名 string m_post; // 职位 int m_sex; // 性别 };
Python 程序猿 PythonCoder
派生类:设计
class PythonCoder : public Coder { public: bool isAIField(){} // 是不是人工智能领域 bool isBigDataField(){} // 是不是大数据领域 };
派生类对象的大小 = 基类对象成员变量的大小 + 派生类对象本身的成员变量的大小。在派生类对象中,包含着基类对象,并且基类对象的存储位置位于派生类对象新增的成员变量以前,至关于基类对象是头部。3d
class CBase { int a1; int a2; }; class CDerived : public CBase { int a3; };
继承的关系是「是」的关系:
继承的关系是「有」的关系:
假设已经存在了 Man 类表示男人,后面须要些一个 Women 类来表示女人。Man 类和 Women 类确实是有共同之处,那么就让 Women 类继承 Man 类,是否合适?
咱们先想一想继承的逻辑要求,假设 Women 类继承 Man 类后的逻辑就是:一个女人也是一个男人。很明显,这显然不成立!
因此,好的作法是归纳男人和女人的共同特色,抽象出一个 Human 类表示人,而后 Man 和 Woman 都继承 Human 类。
假设要写一个小区养狗管理系统:
假定狗只有一个主人,可是一个主人能够最多有 10 条狗,应该如何设计和使用「主人」类 和「狗」类呢?咱们先看看下面几个例子:
class CDog; class CMaster // 主人类 { CDog dogs[10]; // 狗类的成员对象数组 }; class CDog // 狗类 { CMaster m; // 主人类的成员对象 };
例子一能够发现是:
至关于人中有狗,狗中有人:
这样是很差的,由于会产生循环不断的构造,主人类构造狗对象,狗类又构造主人对象....
class CDog; class CMaster // 主人类 { CDog * pDogs[10]; // 狗类的对象指针数组 }; class CDog // 狗类 { CMaster m; // 主人类的成员对象 };
这样又变成狗中有人,人去指向「狗中有人」的狗,关系就会显得很错乱,以下图:
class CDog; class CMaster // 主人类 { CDog dogs[10]; // 狗类的对象数组 }; class CDog // 狗类 { CMaster * pm; // 主人类的对象指针 };
这样就会变成,人中有狗,人里面的狗又会指向主人,虽然关系相对好了一点,可是一样仍是会绕晕,效果以下图:
class CDog; class CMaster // 主人类 { CDog * pDogs[10]; // 狗类的对象指针数组 }; class CDog // 狗类 { CMaster * pm; // 主人类的对象指针 };
这个是正确的例子,由于至关于人和主人是独立的,而后经过指针的做用,使得狗是能够指向一个主人,主人也能够同时指向属于本身的 10 个狗,这样会更灵活。
若是不用指针对象,生成 A 对象的同时也会构造 B 对象。用指针就不会这样,效率和内存都是有好处的。
好比:
class Car { Engine engine; // 成员对象 Wing * wing; // 成员指针对象 };
定义一辆汽车,全部的汽车都有 engine,但不必定都有 wing
这样对于没有 wing 的汽车,wing 只占一个指针,判断起来也很方便。
派生类(子类)能够定义一个和基类(父类)成员同名的成员,这叫「覆盖」。在派生类(子类)中访问这类成员时,默认的状况是访问派生类中定义的成员。要在派生类中访问由基类定义的同名成员时,要使用做用域符号::
。
下面看具体的例子:
// 基类 class Father { public: int money; void func(); }; // 派生类 class Son : public Father // 继承 { public: int money; // 与基类同名成员变量 void func(); // 与基类同名成员函数 void myFunc(); }; void Son::myFunc() { money = 100; // 引用的是派生类的money Father::money = 100; // 引用的是基类的money func(); // 引用的是派生类的 Father::func(); // 引用的是基类的 }
至关于 Son 对象占用的存储空间:
咱们都知道基类的 public 成员,都是能够被派生类成员访问的,那么基类的 protected、private 成员,分别能够被派生类成员访问吗?带着这个问题,咱们能够先看下面的栗子:
class Father { public: int nPublic; // 公有成员 protected: int nProtected; // 保护成员 private: int nPrivate; // 私有成员 }; class Son : public Father { void func() { nPublic = 1; // OK nProtected = 1; // error nPrivate =1; // ok,访问从基类继承的protected成员 Son a; a.nProtected = 1; // error,a不是当前对象 } }; int main() { Father f; Son s; f.nPublic; // OK s.nPublic; // OK f.nProtected; // error s.nProtected; // error f.nPrivate; // error s.nPrivate; // error }
基类的 protected、private 成员对于派生类成员的权限说明:
基类的 protected 成员 | 基类的 private 成员 |
---|---|
派生类的成员函数能够访问当前对象的基类的保护成员 | 不能被派生类成员访问 |
一般在初始化派生类构造函数时,派生类构造函数是要实现初始化基类构造函数的。那么如何在派生类构造函数里初始化基类构造函数呢?
class Bug { private : int nLegs; int nColor; public: int nType; Bug (int legs, int color); void PrintBug (){ }; }; class FlyBug : public Bug // FlyBug 是Bug 的派生类 { int nWings; public: FlyBug( int legs,int color, int wings); }; Bug::Bug( int legs, int color) { nLegs = legs; nColor = color; } // 错误的FlyBug 构造函数 FlyBug::FlyBug ( int legs,int color, int wings) { nLegs = legs; // 不能访问 nColor = color; // 不能访问 nType = 1; // ok nWings = wings; } // 正确的FlyBug 构造函数: FlyBug::FlyBug ( int legs, int color, int wings):Bug( legs, color) { nWings = wings; } int main() { FlyBug fb ( 2,3,4); fb.PrintBug(); fb.nType = 1; fb.nLegs = 2 ; // error. nLegs is private return 0; }
在上面代码例子中:
第24-30行的派生类构造函数初始化基类是错误的方式,由于基类的私有成员是没法被派生类访问的,也就没法初始化。
第33-36行代码是正确派生类构造函数初始化基类构造函数的方式,经过调用基类构造函数来初始化基类,在执行一个派生类的构造函数
以前,老是先执行基类的构造函数。
从上面的例子中咱们也得知构造派生对象前,是先构造基类对象,那么在析构的时候依然依据“先构造,后初始化”的原则,因此派生类析构时,会先执行派生类析构函数,再执行基类析构函数。
以下栗子:
class Base { public: int n; Base(int i) : n(i) { cout << "Base " << n << " constructed" << endl; } ~Base() { cout << "Base " << n << " destructed" << endl; } }; class Derived : public Base { public: Derived(int i) : Base(i) { cout << "Derived constructed" << endl; } ~Derived() { cout << "Derived destructed" << endl; } }; int main() { Derived Obj(3); return 0; }
输出结果:
Base 3 constructed Derived constructed Derived destructed Base 3 destructed
// 基类 class Base {}; // 派生类 class Derived : public Base {}; Base b; // 基类对象 Derived d; // 派生类对象
b = d;
Base & br = d;
Base * pb = & d;
==注意:若是派生方式是 private 或 protected,则上述三条不可行==
// 基类 class Base {}; // 派生类 class Derived : protected Base {}; Base b; // 基类对象 Derived d; // 派生类对象
为派生类的不可访问成员;
因此派生方式是 private 或 protected,则是没法像 public 派生承方式同样把派生类对象赋值、引用、指针给基类对象。
public 派生方式的状况下,派生类对象的指针能够直接赋值给基类指针
Base *ptrBase = & objDerived;
经过强制指针类型转换,能够把 ptrBase 转换成 Derived 类的指针
Base * ptrBase = &objDerived; Derived *ptrDerived = ( Derived * ) ptrBase;
程序员要保证 ptrBase 指向的是一个 Derived 类的对象,不然很容易会出错。