C++学习第四天

继承性

主要学习C++中继承如何使用
理解何为继承与派生

class <派生类名>:[继承方式] <基类名>
{
....——>增加特性
}
private public protected

默认继承私有继承
派生类从基类中继承:**除构造函数和析构函数之外的成员**
class CEmployee
{ protected:
    char *name;
    ind individualEmpNo;
  public:
     CEmplopyee();//构造函数
     ~CEmployee();//析构函数
     void pay();
     void promote(int);
     void displayStatus();
};
class CTechnician:public CEmployee
{
 float hourlyRate;//每小时酬金
 int workHours;//每月工作时数
 public:
    CTechnician();//构造函数
    void pay();//可重写
    void displayStatus();//可重写
};

权限:
在这里插入图片描述
多继承:

class <派生类名>:[继承方式] 基类名1,[继承方式] 基类2,...,[继承方式] 基类名n
{
.....
}
class CSofa
{
...
};
class CBed
{
...
};
class B:public CSofa,public CBed
{
...
};

继承方式

主要讲解从基类继承的成员,其访问属性由继承方式控制

①公有(public)继承
基类的public和protected成员的访问属性,在子类中保持不变,基类的private成员不可直接访问
<—>子类在类中能直接访问基类中public protected成员 不能访问private成员
(如果想访问的话 子类中定义一个public成员函数用于访问基类的私有成员 子类到时候调用基类这个成员函数就行了)
<—>子类在类外(对象)只能访问public成员

#include<iostream.h>
class CVehicle
{
private:
   int wheels;
protected:
   float weight;
public:
   CVehicle(int in_wheels,float in_weight)
   {wheels=in_wheels,weight=in_weight;}
   int get_wheels(){return wheels;}
   float get_weight(){return weight;}
};
class CCar:public CVehicle
{
  int passenger_load;
 public:
   CCar(int in_wheels,int in_weight,int people=5):
   成员初始化列表
   CVehicle(in_wheels,in_weight)
   自己数据的初始化
   {passenger_load=people;}
   int get_passengers(){return passenger_load;}   
};

void main()
{CCar myCar(4,1000);
cout<<"myCar(wheels,weight,passenger):"<<endl;
cout<<myCar.get_wheels()<<".";
cout<<myCar.get_weight()<<".";
cout<<myCar.get_passengers()<<endl;
}

 

②私有继承
基类的public和protected成员的访问属性,在子类中变成private,基类的private成员不可直接访问
<—>子类在类中能直接访问基类中public protected成员 不能访问private成员
(如果想访问的话 子类中定义一个public成员函数用于访问基类的私有成员 子类到时候调用基类这个成员函数就行了)
<—>子类在类外(对象)不能直接访问基类中的任何成员

③保护继承
基类的public和protected成员的访问属性,在子类中变成protected,基类的private成员不可直接访问
<—>子类在类中能直接访问基类中public protected成员 不能访问private成员
(如果想访问的话 子类中定义一个public成员函数用于访问基类的私有成员 子类到时候调用基类这个成员函数就行了)
<—>子类在类外(对象)不能直接访问基类中的任何成员

结:

(其实总结下来就这几句话)
不管怎么继承 子类中永远不能访问直接访问父类的私有成员

公有继承的话 子类在类外(生成对象)只能访问基类的public部分
私有继承的话 子类在类外(生成对象)不能访问基类中任何成员
保护继承的话 子类在类外(生成对象)不能访问基类中任何成员

外加:保护继承和私有继承其实差不太多
但通过下面例子理解保护成员的功能

.
class CA
{protected:
    int x;
 };
 class CB:public CA
 { public:
      int functon();
 };
 int CB:function()
 {
 x=5;//正确 H
 return x;
 }
 void main()
 {
 CB b;
 cout<<b.function();
 }
 
 //但是如果把x变成private型,H条语句就不正确了.
 class CA
{protected:
    int x;
 };
 class CB:public CA
 { public:
      int functon();
 };
  int CB:function()
 {
 x=5;//正确 H
 return x;
 }
  void main()
 {
 CB b;
 cout<<b.function();
 }

 总结:protected实现了信息的隐藏 
      但对于继承又可以实现了重用

定义一个类的注意事项
1.类中的任何成员数据不能使用关键字extern auto或register限定其存储类型
2.定义类时,并不为类分配存储空间,类中的数据成员,不能在类体内对其初始化

派生类的析构函数和构造函数

—.
派生类从基类中继承:
除构造函数和析构函数之外的成员
1.基类定义带参构造函数:子类构造函数中必须把基类构造函数得实现了 这么想 子类是继承基类的 父类有的东西子类也得有
2.基类定义了带参和不带参的构造函数:子类构造函数中不言定要写上基类构造函数,如果没写的话就是调用基类不带参的构造函数的。
~总之 不管怎么定义 子类一定要继承父类的构造函数(无论是带参还是不带参)~

扫描二维码关注公众号,回复: 10257719 查看本文章

派生类的构造函数定义
1.基类使用默认构造函数或不带惨的构造函数,可以不给出类名2
2.内嵌对象使用默认构造函数或不带惨的构造函数,可以不给出对象名

派生类构造函数执行顺序:
1.基类->内嵌对象->派生类本身
2.基类构造函数执行顺序:按照声明派生类时基类的执行顺序进行
3.内嵌对象成员构造函数的执行顺序:按照对象在派生类中声明语句中出现的次序执行

二.
派生类对象的清理工作需要加入新的析构函数
派生类的析构函数负责派生类新增的非对象成员的清理
派生类构造函数执行顺序:
1.派生类本身->内嵌对象->基类
析构函数和构造函数执行顺序相反

code1

#include <iostream>
using namespace std;
class C1
{
public:
    C1(int i){cout<<"C1 construct"<<i<<endl;}
    ~C1(){cout<<"C1 destruct"<<endl;}
};
class C2
{
public:
    C2(int j){cout<<"C2 construct"<<j<<endl;}
    ~C2(){cout<<"C2 destruct"<<endl;}
};
class C3
{
public:
    C3(){cout<<"C3 construct"<<endl;}
    ~C3(){cout<<"C3 destruct"<<endl;}
};
class CSon:public C2,public C1,public C3
{
    C1 c1;
    C2 c2;
    C3 c3;
public:
    CSon(int a,int b,int c,int d):C1(a),c2(d),c1(c),
    C2(b)
    {}
};
int main()
{
   CSon(2,4,6,8);
}
输出:
C2 construct4
C1 construct2
C3 construct
C1 construct6
C2 construct8
C3 construct
C3 destruct
C2 destruct
C1 destruct
C3 destruct
C1 destruct
C2 destruct

code2

#include <iostream>
using namespace std;
class C1
{
public:
    C1(int i){cout<<"C1 construct"<<i<<endl;}
    C1(){}
    ~C1(){cout<<"C1 destruct"<<endl;}
};
class C2
{
public:
    C2(int j){cout<<"C2 construct"<<j<<endl;}
    ~C2(){cout<<"C2 destruct"<<endl;}
};
class C3
{
public:
    C3(){cout<<"C3 construct"<<endl;}
    ~C3(){cout<<"C3 destruct"<<endl;}
};
class CSon:public C2,public C1,public C3
{
    C1 c1;
    C2 c2;
    C3 c3;
public:
    CSon(int b,int c,int d):c2(d),c1(c),
    C2(b)
    {}
};
int main()
{
   CSon(4,6,8);
}
输出:
C2 construct4
C3 construct
C1 construct6
C2 construct8
C3 construct
C3 destruct
C2 destruct
C1 destruct
C3 destruct
C1 destruct
C2 destruct

派生类中成员标识访问

一.
基类与派生类存在相同的成员 在内层派生类
里面的这个同名成员会把外层基类的这个同名成员覆盖掉.
作用域分辨符

#include <iostream>
using namespace std;
class C1
{
  public:
    int n;
    void fun(){cout<<"This is C1 n="<<n<<endl;}
};
class C2:public C1
{public:
   int n;
   void fun(){cout<<"This is C2 n="<<n<<endl;}
};
int main()
{
 C2 c;
 c.n=1;
 c.fun();
 c.C1::n=2; 
 作用域分辨标识符
 c.C1::fun();
}
输出
This is C2 n=1
This is C1 n=2

二.
多继承 基类与派生类之间 基类之间出现同名成员将出现访问时二义性
派生类从多个基类派生 基类又从同一个基类派生 此时访问此共同基类中的成员时,将产生二义性
1.

class CA
{ public:
   void f();
};
class CB
{ public:
   void f();
   void g();
};
class CSonL:public CA,public CB
{ public:
    void g();
    void h();
    解决2:
    CA::f();
    CB::f();
};
void main()
{
 CD c1;
 c1.f();
   二义性
   解决11.c1.CA::f()
   2.c1.CB::f()
 c1.g();
   正确
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述这样子在派生类的对象中 继承自共同基类的这些同名数据成员在内存中拥有多个副本 同一个函数名有多个映射 造成了内存的开销 使用虚基类解决

3.虚基类解决二:
虚基类主要用来解决多继承时可能发生的对同意基类继承多次而产生的二义性问题

最远的派生类 提供 唯一的基类成员不重复产生多次拷贝 同名数据成员在内存中拥有一个副本 同一个函数名有多个映射

在这里插入图片描述
在这里插入图片描述
可见CLevel1构造函数被执行了两次
通过虚基类~~
在这里插入图片描述
在这里插入图片描述
可见CLevel构造函数只被执行了一次

继承中的对象指针

对象指针复习:保存该对象在内存中存储空间首地址的指针变量
this指针复习:每个对象中隐藏的指针 当一个对象生成后 这个对象的this指针指向内存中保存该对象数据的存储空间的首地址

#include <iostream>
#include <cstring>
using namespace std;
class C1
{
   char *name;
   //给name指针变量只是分配了一个存储其地址的内存
   int  length;
   public:
       C1(char *str)
       {
           length=strlen(str);
           name=new char[length+1];
           //必须先给name一个内存分配工作 分配length+1个内存
           strcpy(name,str);
       }
  void show(){cout<<name<<endl;}
  //进行输出
};

code:

#include <iostream>
#include <cstring>
using namespace std;
class C1
{
   char *name;
   //给name指针变量只是分配了一个存储其地址的内存
   int  length;
   public:
       C1(char *str)
       {
           length=strlen(str);
           name=new char[length+1];
           //必须先给name一个内存分配工作 分配length+1个内存
           strcpy(name,str);
       }
  void show(){cout<<name<<endl;}
};
class C2:public C1
{
    int age;
    public:
        C2(char *str,int age):C1(str)
        {
            C2::age=age;
        }
    void show()
    {
        C1::show();
        cout<<"the age is:"<<age<<endl;
    }
};
int main()
{
   C1 s1("Smith"),*ptr1;
   C2 s2("Jean",20),*ptr2;
   ptr1=&s1;
   ptr1->show();
   //基类调用自己show函数
   ptr1=&s2;
   ptr1->show();
   //基类指向公有继承子类
   //指向基类对象的指针 指向派生类对象 这块只能访问c从基类中继承来的show函数
    ptr2=&s2;
    ptr2->show();
    //派生调用自己show函数
}
输出:
Smith
Jean
Jean
the age is:20

为什么派生类指针不能指向基类对象
(狗继承动物 狗类指针按理来说可以指向动物的啊 狗是动物可以 动物是狗是不行的啊!)

发布了19 篇原创文章 · 获赞 2 · 访问量 736

猜你喜欢

转载自blog.csdn.net/qq_45639157/article/details/104880097