一、 封装和继承
1.1 封装的概念
封装:通过访问控制属性对类类型中的属性和行为进行打包和限制.
1.2 继承:通过一种机制表达出类型之间的共性和特性的方式.
继承基本语法:
class 子类名 : 继承方式1 父类1,继承方式2 父类2...
{
类的定义
};
继承方式:
public 公有方式
private 私有方式
protected 保护方式
1.3 继承的特性
(1) 一个子类类型的对象在任何时候都可以看作基类类型的对象(心里始终要记着).
(2) 一个父类类型的对象作为子类对象去使用,有可能引发段错误.
(3) 在子类中可以访问父类的公有和保护的成员,不可以访问父类中私有成员.
(4) 名字的隐藏,在子类可以定义一个与父类中同名的标识符,子类隐藏父类.(不能构成重载,不在同一作用域下,函数参数也不一定一致.)
(5) 如果非要调用父类中的同名函数,那么可以显示指定 父类::函数名;
1.4 继承方式和访问控制
限定符 访控属性 本类 子类 外部 友元
---------------------------------------------------------
public 公开的 ok ok
ok ok
protected 保护的 ok ok
no ok
private
私有的 ok no no ok
---------------------------------------------------------
1.5继承方式对访控属性的影响
基类访控
公有子类 保护子类
私有子类
public
public protected
private
protected protected protected
private
private private
private private
注意:规则优先选择保护性最强的。一般都采用公开的继承方式,其他两种继承方式很少。
继承例子代码如下:
class CPerson { private: string m_name; int m_age; public: CPerson(const string& name,int age):m_name(name),m_age(age) { cout<<"CPerson类==》"<<"我是 "<<m_name<<"今年 "<<m_age<<"岁了"<<endl; } void eat(const string& str) { cout<<"这是父类的eat函数"<<endl; } protected: string getName() { return m_name; } int getAge() { return m_age; } }; class CStudent : public CPerson { private: int m_num; public: CStudent(const string& name,int age,int num):CPerson(name,age),m_num(num) { cout<<"我是学生, 我的学号是: "<<m_num<<endl; } void lean(const string& strlesson) { cout<<"我在学习 "<<strlesson<<endl; } void print(void) { cout<<"学习学号: "<<m_num<<endl; } void show(void) { cout<<"我是"<<getName()<<"今年"<<getAge()<<"学习学号"<<m_num<<endl; } void eat(const string& foot) { cout<<"这是子类中的eat函数"<<endl; } }; class CTeacher : public CPerson { private: string m_job; public: CTeacher(const string& name,int age,const string& job):CPerson(name,age),m_job(job) { cout<<"我是教师,我的职称是: "<<m_job<<endl; } void tech(const string& lesson) { cout<<"我在教"<<lesson<<endl; } };
1.6子类的构造函数和析构函数
(1)在子类的构造函数中可以显示的指定其基类的构造方式(:基类(参数)),如果没有显示的指定,那么系统就会以无参的形式去构造基类的部分。
(2)子类的析构函数会自动的调用父类的析构函数,但是基类的析构函数不会自动调用子类的析构函数。(这个是指子类对象析构的时候哦)
(3)如果父类指针指向子类对象,那么delete指针时,被调用的仅仅是父类的析构函数,子类的析构函数不会被调用,为了调用子类的析构函数,采用虚析构
class A { public: A() { cout<<"A is create"<<endl; } virtual ~A() { cout<<"A is delete"<<endl; } }; class B :public A { public: B() { cout<<"B is create"<<endl; } ~B() { cout<<"B is delete"<<endl; } };
B* pb = new B();
delete pb;
pb = NULL;
调用输出顺序为:
A is create
<span style="font-family: Arial, Helvetica, sans-serif;">B is create</span>
<span style="font-family: Arial, Helvetica, sans-serif;">B is delete</span>
<pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-family: Arial, Helvetica, sans-serif;">A is delete</span>
可以看构造和析构的顺序严格相反
1.7子类的拷贝构造和拷贝赋值函数
(1)如果在子类中需要自定义拷贝构造函数,那么需要在拷贝构造函数中显式的指明调用父类的拷贝构造函数,拷贝赋值也是一样。
class Teacher; //类的前项声明 class Person { private: string m_name; int m_age; public: Person(const string& name,int age):m_name(name),m_age(age) { cout<<"Person类==》"<<"我是 "<<m_name<<"今年 "<<m_age<<"岁了"<<endl; } Person(const Person& p):m_name(p.m_name),m_age(p.m_age) { cout<<"Person类中的自定义拷贝构造函数被调用"<<endl; } void eat(const string& str) { cout<<"这是父类的eat函数"<<endl; } protected: string getName() { return m_name; } int getAge() { return m_age; } }; class Teacher : public Person { private: string m_job; public: Teacher(const string& name,int age,const string& job):Person(name,age),m_job(job) { cout<<"我是教师,我的职称是: "<<m_job<<endl; } Teacher(const Teacher& t):Person(t),m_job(t.m_job) //<span style="color:#ff6666;">子类类型对象可以作为父类类型对象使用哦,显示调用其拷贝构造函数</span> { cout<<"Teacher类中的自定义的拷贝构造函数被调用"<<endl; } void tech(const string& lesson) { cout<<"我在教"<<lesson<<endl; } };
二、多重继承(钻石继承)
如:钻石继承
员工类(姓名)
/ \
领导类 销售人员类
\ /
销售人员领导类(保证只有一个名字.)
钻石继承注意:
(1)在对公共继承的继承方式之前加virtual关键字.(虚继承)
(2)可能需要在最终子类中显示指明公共基类的构造方式(可能缺省构造也可以实现)
(3)目标:公共基类在最后子类中只有一份,以避免由不同继承路径访问公共基类时发生数据不一致.
注意:在多继承中,如果多个父类都有函数名相同,参数表不同的函数,继承到子类中不会构成重载.
说明对钻石继承我在下面运行的结果我使用一个图示表示:
<span style="white-space:pre"> </span>DD dd(100); cout<<"m_data = "<<dd.GetValue()<<endl; dd.SetValue(1000); cout<<"m_data = "<<dd.GetValue()<<endl;
<span style="white-space:pre"> </span>没有使用虚继承时输出结果如下:100,100
<span style="white-space:pre"> </span>使用虚继承时输出结果如下:100,1000
class AA { public: int m_data; public: AA(int data):m_data(data) { cout<<"AA is create"<<endl; } ~AA() { cout<<"AA is delete"<<endl; } }; class BB : virtual public AA { public: BB(int n):AA(n) { cout<<"BB is create"<<endl; } void SetValue(int nValue) { m_data = nValue; } void show() { cout<<"B类中show函数"<<endl; } ~BB() { cout<<"BB is delete"<<endl; } }; class CC : virtual public AA { public: CC(int n):AA(n) { cout<<"CC is create"<<endl; } int GetValue() { return m_data; } void show(int i) { cout<<"C类中的一个show函数"<<endl; } ~CC() { cout<<"CC is delete"<<endl; } }; class DD : public BB,public CC { public: DD(int n):CC(n),BB(n),AA(n)/*显示指定调用AA的构造*/ { cout<<"DD is create"<<endl; } ~DD() { cout<<"DD is delete"<<endl; } void show(char c,int n) { cout<<"DD 类中的show函数"<<endl; } };
int main(void) { DD dd(100); cout<<"m_data = "<<dd.GetValue()<<endl; dd.SetValue(1000); cout<<"m_data = "<<dd.GetValue()<<endl; cout<<"-------------------------------------"<<endl; dd.BB::show(); //指定调用B类中的show函数. dd.show('c',10); cout<<"--------------------------------"<<endl; Teacher t1("weikangc",25,"教授"); t1.eat("包子"); t1.tech("Java"); cout<<"---------------------------------"<<endl; Teacher t2(t1); t2.tech("C++"); B b; A& a = b; A* pA = new B; delete pA; pA = NULL; B* pB = new B; delete pB; pB = NULL; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// CStudent s1("weikangc",25,6888); s1.lean("C++"); s1.CPerson::eat("拉面"); s1.show(); //s1.getName(); //保护类的成员不可以在类的外部使用. //s1.getAge(); cout<<"-------------------------------------"<<endl; CTeacher c1("WeiKangC",28,"Java讲师"); c1.eat("包子"); c1.tech("Java"); cout<<"--------------------------------------"<<endl; CPerson* person = new CStudent("程伟康",25,99999); person->eat("羊肉汤"); //person->lean("C/C++,JAVA"); //编译报错,没有此函数,编译器只关注数据类型,发现此类型中没有该函数. CStudent* pS = static_cast<CStudent*>(person); pS->lean("C/C++"); pS->print(); delete person; person = NULL; cout<<"---------------------------------------"<<endl; CStudent* ppS = static_cast<CStudent*>(new CPerson("伟康程",26)); ppS->lean("Win32"); ppS->CPerson::eat("炒鸡"); ppS->print(); //缺少一个成员比较危险. delete ppS; ppS = NULL; return 0; }