文章标题

    xiaoxiao2021-03-25  185

    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个字节

    转载请注明原文地址: https://ju.6miu.com/read-4925.html

    最新回复(0)