继承和派生

来源:互联网 发布:ec20 linux 编辑:程序博客网 时间:2024/05/16 14:25

继承中的基本语法

通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。
#include <iostream>using namespace std;class Parent{public:void print(){a = 0;b = 0;cout<<"a"<<a<<endl;cout<<"b"<<b<<endl;}int b;protected:private:int a;};//class Child : private Parent//class Child :  protected Parent publicclass Child :  public Parent{public:protected:private:int c;};void main11(){Child c1;//c1.c = 1;//c1.a = 2;c1.b = 3;c1.print();cout<<"hello..."<<endl;system("pause");return ;}



不同的继承方式会改变继承成员的访问属性

1)C++中的继承方式会影响子类的对外访问属性

public继承:父类成员在子类中保持原有访问级别

private继承:父类成员在子类中变为private成员

protected继承:父类中public成员会变成protected

                              父类中protected成员仍然为protected

                              父类中private成员仍然为private

2)private成员在子类中依然存在,但是却无法访问到。不论种方式继承基类,派生类都不能直接使用基类的私有成员。

3)继承中的访问控制






类型兼容性原则

类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。类型兼容规则中所指的替代包括以下情况:

子类对象可以当作父类对象使用

子类对象可以直接赋值给父类对象

子类对象可以直接初始化父类对象

父类指针可以直接指向子类对象

父类引用可以直接引用子类对象

在替代之后,派生类对象就可以作为基类的对象使用,但是只能使用从基类继承的成员。

类型兼容规则是多态性的重要基础之一。

#include <iostream>using namespace std;class Parent{public:void printP(){cout<<"我是爹..."<<endl;}Parent(){cout<<"parent构造函数"<<endl;}Parent(const Parent &obj){cout<<"copy构造函数"<<endl;}private:int a;};class child : public Parent{public:void printC(){cout<<"我是儿子"<<endl;}protected:private:int c;};/*兼容规则中所指的替代包括以下情况:子类对象可以当作父类对象使用子类对象可以直接赋值给父类对象子类对象可以直接初始化父类对象父类指针可以直接指向子类对象父类引用可以直接引用子类对象*///C++编译器 是不会报错的 .....void howToPrint(Parent *base){base->printP(); //父类的 成员函数 }void howToPrint2(Parent &base){base.printP(); //父类的 成员函数 }void main(){//Parent p1;                //Parent构造函数p1.printP();              //我是爹child c1;                 //Parent构造函数                   //自动调用了父类的无参构造函数c1.printC();              //我是儿子c1.printP();               //我是爹//赋值兼容性原则 //1-1 基类指针 (引用) 指向 子类对象Parent *p = NULL;p = &c1;p->printP();              //我是爹
//1-2 指针做函数参数howToPrint(&p1); //我是爹
howToPrint(&c1); //我是爹//1-3引用做函数参数howToPrint2(p1); //我是爹howToPrint2(c1); //我是爹//第二层含义//可以让子类对象 初始化 父类对象//子类就是一种特殊的父类 Parent p3 = c1; //copy构造函数 cout<<"hello..."<<endl;system("pause");return ;}





继承中的对象模型








继承中构造和析构


问题:如何初始化父类成员?父类与子类的构造函数有什么关系

         在子类对象构造时,需要调用父类构造函数对其继承得来的成员进行初始化

         在子类对象析构时,需要调用父类析构函数对其继承得来的成员进行清理


#include <iostream>using namespace std;//结论//先 调用父类构造函数 在调用 子类构造函数//析构的顺序  和构造相反/*1、子类对象在创建时会首先调用父类的构造函数2、父类构造函数执行结束后,执行子类的构造函数3、当父类的构造函数有参数时,需要在子类的初始化列表中显示调用4、析构函数调用的先后顺序与构造函数相反*/class Parent{public:Parent(int a, int b){this->a = a;this->b = b;cout<<"父类构造函数..."<<endl;}~Parent(){cout<<"析构函数..."<<endl;}void printP(int a, int b){this->a = a;this->b = b;cout<<"我是爹..."<<endl;}private:int a;int b;};class child : public Parent{public:child(int a, int b, int c) : Parent(a, b){this->c = c;cout<<"子类的构造函数"<<endl;}~child(){cout<<"子类的析构"<<endl;}void printC(){cout<<"我是儿子"<<endl;}protected:private:int c;};void playObj(){child c1(1, 2, 3);}void main(){//Parent p(1, 2);playObj();cout<<"hello..."<<endl;system("pause");return ;}





继承与组合混搭情况下,构造和析构调用原则

(类中的数据成员变成另外一个类的对象的时候,就是类的组合,也就是说用一个类的对象作为另一个类的成员的时候,就是类的组合)

原则:     先构造父类,再构造成员变量、最后构造自己

                 先析构自己,在析构成员变量、最后析构父类

                  //先构造的对象,后释放

#include <iostream>using namespace std;class Object{public:Object(int a, int b){this->a = a;this->b = b;cout<<"object构造函数 执行 "<<"a"<<a<<" b "<<b<<endl;}~Object(){cout<<"object析构函数 \n";}protected:int a;int b;};class Parent : public Object{public:Parent(char *p) : Object(1, 2){this->p = p;cout<<"父类构造函数..."<<p<<endl;}~Parent(){cout<<"析构函数..."<<p<<endl;}void printP(int a, int b){cout<<"我是爹..."<<endl;}protected:char *p;};class child : public Parent{public:child(char *p) : Parent(p) , obj1(3, 4), obj2(5, 6){this->myp = p;cout<<"子类的构造函数"<<myp<<endl;}~child(){cout<<"子类的析构"<<myp<<endl;}void printC(){cout<<"我是儿子"<<endl;}protected:char *myp;Object obj1;Object obj2;};void objplay(){child c1("继承测试");}void main(){objplay();cout<<"hello..."<<endl;system("pause");return ;}



继承中的同名成员变量处理方法


1、当子类成员变量与父类成员变量同名时

2、子类依然从父类继承同名成员

3、在子类中通过作用域分辨符::进行同名成员区分(在派生类中使用基类的同名成员,显式地使用类名限定符

4、同名成员存储在内存中的不同位置







派生类中的static关键字



理论知识

Ø  基类定义的静态成员,将被所有派生类共享

Ø  根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质 (遵守派生类的访问控制)

Ø   派生类中访问静态成员,用以下形式显式说明:

                            类名 :: 成员

    或通过对象访问     对象名 . 成员


理解:在int B::i=0这句话中,并不是简单的变量赋值,而是告诉编译器给我分配内存,所以必须要进行初始化。因为静态成员变量遵守派生类的访问控制

            所以private继承时,其变成了私有的继承,则y.i不能对其进行访问





多继承

多继承概念

Ø    一个类有多个直接基类的继承关系称为多继承

Ø    多继承声明语法

class  派生类名 : 访问控制 基类名1访问控制 基类名2 ,  … , 访问控制 基类名n

    {

         数据成员和成员函数声明

    };

Ø    类 C 可以根据访问控制同时继承类 A 和类B 的成员,并添加

     自己的成员




多继承的二义性

如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性




总结:

Ø    如果一个派生类从多个基类派生,而这些基类又有一个共同

     的基类,则在对该基类中声明的名字进行访问时,可能产生

     二义性

Ø    如果在多条继承路径上有一个公共的基类,那么在继承路径的某处

     汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象

Ø    要使这个公共基类在派生类中只产生一个子对象,必须对这个基类

     声明为虚继承,使这个基类成为虚基类。

Ø    虚继承声明使用关键字           virtual


虚继承



 



虚继承原理详解

//虚继承原理抛砖                                             
#include <iostream>using namespace std;class B{public:B(){cout<<"B构造函数执行\n";}int b;protected:private:};class B1 : virtual public B //12{public:int b1;};class B2 :    public B //8{public:int b2;};class C : public B1, public B2{public:int c;};void main(){cout<<sizeof(B)<<endl; //4cout<<sizeof(B1)<<endl; //12 //加上virtual以后 , C++编译器会在给变量偷偷增加属性cout<<sizeof(B2)<<endl;  //8//cout<<sizeof(B)<<endl;system("pause");}void main1101(){C  c1;c1.b1 = 100;c1.b2 = 200;c1.c = 300;//c1.b = 500; //继承的二义性 和 虚继承解决方案//c1.B1::b = 500;//c1.B2::b = 500;cout<<"hello..."<<endl;system("pause");return ;}class D1 {public:int k;protected:private:};class D2 {public:int k;protected:private:};class E :  public D1,  public D2{public:protected:private:};void main1202(){ E e1; e1.D1::k = 100; e1.D2::k = 200;system("pause");}



继承总结

Ø  继承是面向对象程序设计实现软件重用的重要方法。程序员可以在已有基类的基础上定义新的派生类。

Ø   单继承的派生类只有一个基类。多继承的派生类有多个基类。

Ø   派生类对基类成员的访问由继承方式和成员性质决定。

Ø   创建派生类对象时,先调用基类构造函数初始化派生类中的基类成员。调用析构函数的次序和调用构造函数的次序相反。

Ø   C++提供虚继承机制,防止类继承关系中成员访问的二义性。

Ø   多继承提供了软件重用的强大功能,也增加了程序的复杂性。



















原创粉丝点击