文章标题

来源:互联网 发布:小米床垫怎么样 知乎 编辑:程序博客网 时间:2024/06/05 07:36

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个字节
这里写图片描述

0 0
原创粉丝点击