文章标题

C++类的继承与派生 ##1、继承与派生

一、继承的概念
1、继承与派生
类的继承,即一个新类从已有的类那里获得其已有特性。通过继承,一个新建子类从已有的父类那里获得父类的特性,或者说,从已有的类(父类)产生一个新的子类,称为类的派生。派生类继承了基类的所有数据成员和成员函数,并可以对成员作必要的增加或调整。
2、派生类的声明方式
class 派生类名:[继承方式] 基类名
{
派生类新增加的成员
} ;
例:先声明一个基类Base,在此基础上通过单继承建立一个派生类Derived

#include<stdlib.h>
#include<string>
#include<iostream>
using namespace std;
class Base
{
public:
    void Print(int a)
    {
        _pub=0;
        _pro=1;
        _pri=2;
       cout<<"_pub"<<_pub<<endl;
       cout<<"_pro"<<_pro<<endl;
       cout<<"_pri"<<_pri<<endl;
    }   
public:
    int _pub;

protected:
    int _pro;
private:
    int _pri;
};
//派生类
class Derived:public Base
{
public:
    void Print()
    {
        _pub=0;
        _pro=1;
        //_pri=2;
    }
public:
    int _pubD;
protected:
    int _proD;
private:
    int _priD;
};
int main()
{
    Derived d;
    cout<<sizeof(Derived)<<endl;
    system("pause");
    return 0;
}

3、派生类的构成

这里写图片描述
4、派生类成员的访问属性
这里写图片描述
*注:若继承方式省略,使用关键字class时默认的继承方式是private,使用struct时默认继承方式public.
—派生类的默认成员函数在继承关系里面,在派生类中如果没有显示定义6个成员函数(构造函数、拷贝构造函数、析构函数、赋值运算符重载、取地址操作符重载、const修饰的取地址操作符重载),编译系统则会默认合成这六个默认的成员函数
5、派生类的构造函数和析构函数
1)、继承关系中构造函数调用顺序
这里写图片描述
构造函数调用的代码:

#include<stdlib.h>
#include<string>
#include<iostream>
using namespace std;
//基类
class Base
{
public:
    Base()
    {
        cout<<"Base::Base()"<<endl;
    }
    void Print(int a)
    {
        _pub=0;
        _pro=1;
        _pri=2;
       cout<<"_pub"<<_pub<<endl;
       cout<<"_pro"<<_pro<<endl;
       cout<<"_pri"<<_pri<<endl;
    }   
public:
    int _pub;
protected:
    int _pro;
private:
    int _pri;
};
//派生类
class Derived:public Base
{
public:
    Derived()
    {
        cout<<"Derived::Derived()"<<endl;
    }
    void Print()
    {
        _pub=0;
        _pro=1;
    }
public:
    int _pubD;
protected:
    int _proD;
private:
    int _priD;
};
int main()
{
    Derived d;
    d.Print();
    cout<<sizeof(Derived)<<endl;
    system("pause");
    return 0;
}

构造函数,函数体执行顺序,基类在前,派生类在后
构造函数的调用顺序,如下图所示:
1、这里写图片描述
2、这里写图片描述
3、这里写图片描述
4、这里写图片描述
5、这里写图片描述
6、这里写图片描述
2)、继承关系中析构函数调用
在派生时,派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。在派生类中可以根据需要定义自己的析构函数,用来对派生类中所增加的成员进行清理工作。基类的清理工作仍然由基类的析构函数负责。在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。
6、继承体系中的作用域
1)、在继承体系中基类和派生类是俩个不同作用域
2)、子类和父类中有同名的成员或成员函数,子类成员将屏蔽父类对成员的直接访问—隐藏—重定义
*实际中在继承体系里最好不要定义同名的成员

#include<stdlib.h>
#include<string>
#include<iostream>
using namespace std;
class Base
{
public:
    Base()
    {}
    void Print()
    {
        _pub=0;
        _pro=1;
        _pri=2;
       cout<<"_pub"<<_pub<<endl;
       cout<<"_pro"<<_pro<<endl;
       cout<<"_pri"<<_pri<<endl;

    }   

public:
    int _pub;

protected:
    int _pro;
private:
    int _pri;
};

//派生类
class Derived:public Base
{
public:
    Derived()//派生类构造函数--在函数体中只对派生类新增的数据成员初始化
    {}
    void Print()
    {

        _pubD=4;
        _proD=5;
        cout<<"_pubD"<<_pubD<<endl;
        cout<<"_proD"<<_proD<<endl;

    }
public:
    int _pubD;
protected:
    int _proD;

private:
    int _priD;
};

int main()
{
    Derived d;
    d.Print();
    cout<<sizeof(Derived)<<endl;
    system("pause");
    return 0;
}

运行结果如图所示:
这里写图片描述
7、继承与转换—赋值兼容规则—public继承
1)、子类对象可以赋值给父类对象(切割、换片)—>赋值的部分是子类继承父类的部分
2)、父类对象不能赋值给子类对象—>程序会崩溃
3)、父类的指针/引用可以指向子类对象
4)、子类的指针/引用可以指向父类对象——-可以通过强制类型转换完成
这里写图片描述
程序运行代码:

#include<stdlib.h>
#include<string>
#include<iostream>
using namespace std;
class Base
{
public:
    Base()
    {}
    void Print()
    {
        _pub=0;
        _pro=1;
        _pri=2;
       cout<<"_pub"<<_pub<<endl;
       cout<<"_pro"<<_pro<<endl;
       cout<<"_pri"<<_pri<<endl;

    }   

public:
    int _pub;

protected:
    int _pro;
private:
    int _pri;
};

//派生类
class Derived:public Base
{
public:
    Derived()//派生类构造函数--在函数体中只对派生类新增的数据成员初始化
    {}
    void Show()
    {
        d1=6;
    }
public:
    int d1;
    int d2;


};
void FunTest()
{

    Base b;  //定义基类Base对象b
    Derived d1;  //定义公用派生类Derived对象d1
    Base &r=b;    //定义基类Base对象的引用变量r,并用b对其初始化
    r=d1;    //用派生类对象d1对Base的引用变量r赋值
    b=d1; //用派生类Derived对象d1对基类对象b赋值
    d1.d2=7;  //d1中包含派生类中增加的成员
}
void fun(Base &r)   //形参是类Base的对象的引用变量
{
    cout<<r._pub<<endl;    //输出该引用变量的数据成员_pri;
}
int main()
{
    Derived d;
    FunTest();
    d.Print();
    d.Show();
    fun(d);
    cout<<sizeof(Derived)<<endl;
    system("pause");
    return 0;
}

程序运行结果和分析:
这里写图片描述
这里写图片描述
8、继承与静态成员
基类定义了static成员(可以被继承),则整个继承体系里只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例。
二、单继承&多继承&菱形继承
1、单继承:一个子类只有一个直接父类的继承关系为单继承
2、多继承:一个子类有两个或以上直接父类时的继承关系 为多继承
如果已经声明了类B1和B2,则可以声明多重继承的派生类D
代码如下:

#include<stdlib.h>
#include<string>
#include<iostream>
using namespace std;
class B1
{
public:
    int _b1;
};
class B2
{
public:
    int _b2;
};
class D:public B1,public B2
{
public:
    int _d;
};
  int main()
  {
     cout<<sizeof(D)<<endl;
      D d;
      d._b1 =0;
      d._b2 =1;
      d._d =2; 
      system("pause");
      return 0;
  }

程序运行结果:
这里写图片描述
多重继承的先后:
这里写图片描述
子类对象赋值给父类对象:
这里写图片描述
3、菱形继承—派生类对象访问基类—程序代码

#include<stdlib.h>
#include<string>
#include<iostream>
using namespace std;
class B
{
public:
    int _b;
};
class C1:public B
{
public:
    int _c1;
};
class C2:public B
{
public:
    int _c2;
};
class D:public C1,public C2
{
public:
    int _d;
};
  int main()
  {
      cout<<sizeof(D)<<endl;
      D d;
      d.C1::_b =0;
      d._c1 =1;
      d.C2::_b =2;
      d._c2 =3;
      d._d =4;
      system("pause");
      return 0;
  }

程序运行结果:
这里写图片描述
菱形继承体系中子类对象包含多份父类对象,发生数据冗余&浪费空间的问题,派生类D的 大小中公有继承俩个B类,引发程序的二义性问题:分析结果如图所示:
这里写图片描述
这里写图片描述
4、虚拟继承—–虚基类—菱形虚拟继承
虚基类并不是在声明基类是声明的,而是在声明派生类是,指定继承方式时声明的。
*为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类,

#include<stdlib.h>
#include<string>
#include<iostream>
using namespace std;
 class B
{
public:
    int _b;
};
 class D:virtual public B
 {
 public:
     int _d;
 };
 int main()
 {
     cout<<sizeof(D)<<endl;
      D d;                        
     d._b =0;
     d._d =1;
     system("pause");
     return 0;                        
 }

程序运行结果:
这里写图片描述
结果分析:这里写图片描述
这里写图片描述
在内存中查看d的地址,会发现它的内存地址指向的也是一个地址,因此在内存c2中查看这个地址,发现它是一个指向偏移量表格的指针,编译器合成的派生类构造函数将偏移量表格指向前4个字节
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值