C++基础之继承

1、类的继承与类的派生

  • 继承和派生是人们认识客观世界的过程。在程序设计方法中,人们追求代码复用(这是提升软件开发效率的重要手段),将继承和派生用于程序设计方法中,从而有了面向对象程序设计的重要特色。C++对代码复用有很强的支持,
  • “继承”就是支持代码复用的机制之一。
  • 经过已有的类创建新类的过程,叫做类的派生。原来的类称为基类,也称为父类或通常类;新类称为派生类,也称为子类或特殊类。派生类派生自基类,或继承于基类,也能够说基类派生了派生类。派生机制是C++语言及面向对象程序设计方法的重要特征之一。派生类能够再做为基类派生新的派生类,由此基类和派生类的集合称做类继承层次结构。

继承:ios

  • 使用基类派生新类时,除构造函数和析构函数外,基类的全部成员自动成为派生类的成员,包括基类的成员变量和成员函数。同时,派生类能够增长基类中没有的成员,这一样是指成员变量和成员函数。能够从新定义或修改基类中已有的成员,包括能够改变基类中成员的访问权限。固然派生类须要定义本身的构造函数和析构函数。
  • 使用基类成员是一个重用的过程,在基类之上进行调整,不管是添加新成员仍是改造己有的,都是扩充的过程。

覆盖:函数

  若派生类中定义了一个与基类中同名的成员,则会出现基类与派生类有同名成员的状况,这是容许的。同名的成员既能够是成员变量,也能够是成员函数。这种状况下,若在派生类的成员函数中访问这个同名成员,或经过派生类对象访问这个同名成员时,除非特别指明,访问的就是派生类中的成员,这种状况叫“覆盖”,即派生类的成员覆盖基类的同名成员。覆盖也称为重定义或是重写。对于成员函数来讲,派生类既继承了基类的同名成员函数,又在派生类中重写了这个成员函数。这称为函数重定义,也称为同名隐藏。“隐藏”的意思是指,使用派生类对象调用这个名字的成员函数时,调用的是派生类中定义的成员函数,即隐藏了基类中的成员函数。spa

派生类能够改变基类中成员的访问权限
空类也能够做为基类,也就是说,空类能够派生子类:设计

class emptyClass{ }; //空基类
class subemptyClass : public emptyClass{ }; //派生类

类的大小指针

  • 派生类对象中包含基类成员变量,并且基类成员变量的存储位置位于派生类对象新增的成员变量以前。派生类对象占用的存储空间大小,等于基类成员变量占用的存储空间大小加上派生类对象自身成员变量占用的存储空间大小。
  • 对象占用的存储空间包含对象中各成员变量占用的存储空间。出于计算机内部处理效率的考虑,为变量分配内存时,会根据其对应的数据类型,在存储空间内对变量的起始地址进行边界对齐。可使用sizeof( )函数计算对象占用的字节数。
  • 对象的大小与普通成员变量有关,与成员函数和类中的静态成员变量无关,即普通成员函数、静态成员函数、静态成员变量、静态常量成员变量等均对类对象的大小没有影响。

2、继承关系的特殊性

一、友元code

若是基类有友元类或友元函数,则其派生类不会因继承关系而也有此友元类或友元函数。若是基类是某类的友元,则这种友元关系是被继承的。即被派生类继承过来的成员函数,若是原来是某类的友元函数,那么它做为派生类的成员函数仍然是某类的友元函数。总之,基类的友元不必定是派生类的友元;基类的成员函数是某类的友元函数,则其做为派生类继承的成员函数还是某类的友元函数。对象

#include<iostream>
using namespace std;
class another; //前向引用声明
//基类
class Base {
    private:
      float x;
    public:
      void print(const another &K);
};
//派生类
class Derived:public Base {
    private:
        float y;
};
 //其余类
class another{private:
    int aaa;
public:
    another(){
        aaa=100;
    }
friend void Base::print(const another &K);//基类的成员函数声明为本类的友元
};
void Base::print(const another &K){
    cout<<"Base:"<<K.aaa<<endl; //能够访问私有成员变量
}
int main(){
    Base a;
    Derived d;
    another ano; //aaa 初始化为100
    a.print(ano); //输出为:Base:100
    d.print(ano); //输出为:Base:100
    return 0;
}

二、静态属性blog

若是基类中的成员是静态的,则在其派生类中,被继承的成员也是静态的,即其静态属性随静态成员被继承。
若是基类的静态成员是公有的或是保护的,则它们被其派生类继承为派生类的静态成员。访问这些成员时,一般用“<类名>::<成员名>”的方式引用或调用。不管有多少个对象被建立,这些成员都只有一个拷贝,它为基类和派生类的全部对象所共享。继承

#include<iostream>
using namespace std;
// 基类
class Base {
    private:
        float x;
    public:
        static int staV;
    Base(){ 
        staV++;
    }
};
int Base::staV=0;
//派生类
class Derived: public Base {
    private:
        float y;
    public:
        Derived( ){ 
            staV++;
        }
};
int main(){
    Base a;
    cout<< "Base:" <<a.staV<<endl; //输出1
    Derived d;
    cout<< "Derived:" << d.staV<<endl; //输出3(建立子类对象会调用父类构造器,而后调用自身的构造器)
    return 0;
}

 

 

 三、继承之间的访问关系内存

  • 派生类和基类中均可以定义本身的成员变量和成员函数,派生类中的成员函数能够访问基类中的公有成员变量,但不能直接访问基类中的私有成员变量。也就是说,不能在派生类的函数中,使用“基类对象名.基类私有成员函数(实参)”,或是“基类对象名.基类私有成员变量”,或是“基类名::基类私有成员”的形式访问基类中的私有成员。
  • 在类的派生层次结构中,基类的成员和派生类新增的成员都具备类做用域。两者的做用范围不一样,是相互包含的两个层,派生类在内层,基类在外层。若是派生类声明了一个和基类某个成员同名的新成员,派生的新成员就隐藏了外层同名成员,直接使用成员名只能访问到派生类的成员。若是派生类中声明了与基类成员函数同名的新函数,即便函数的参数表不一样,从基类继承的同名函数的全部重载形式也都会被隐藏。若是要访问被隐藏的成员,就须要使用基类名和做用域分辨符来限定。
#include<iostream>
using namespace std;
class CB{
    public:
        int a;
        CB(int x){
            a=x;
        }
        void showa(){
            cout<<"Class CB--a="<<a<<endl;
        }
};
class CD:public CB{
    public:
        int a; //与基类a同名
        //x用来初始化基类的成员变量a
        CD(int x,int y):CB(x) { 
            a=y; 
        }
        //与基类showa同名        
        void showa() {
            cout<<"Class CD--a="<<a<<endl; 
        }
        //访问派生类a            
        void print2a(){ 
            cout<<"a=" << a<<endl; 
            //访问基类a
            cout<<"CB::a="<<CB::a<<endl;
        }
};
int main(){
    CB CBobj(12);
    CBobj.showa();//Class CB--a=12
    CD CDobj(48,999);
    CDobj.showa(); //访问派生类的showa ()  Class CD--a=999
    CDobj.CB::showa(); //访问基类的showa ()  Class CB--a=48
    cout<<"CDobj.a="<<CDobj.a<<endl; // CDobj.a=999
    cout<<"CDobj.CB::a="<<CDobj.CB::a<<endl;//CDobj.CB::a=48
}

四、protected访问范围说明符

  • 定义类时,类成员可使用protected访问范围说明符进行修饰,从而成为“保护成员”。保护成员的访问范围比私有成员的访问范围大,能访问私有成员的地方都能访问保护成员。此外,基类中的保护成员能够在派生类的成员函数中被访问。
  • 在基类中,通常都将须要隐藏的成员说明为保护成员而非私有成员。将基类中成员变量的访问方式修改成protected后,在派生类中能够直接访问。

3、多重继承

  • C++容许从多个类派生一个类,即一个派生类能够同时有多个基类。这称为多重继承。相应地,从一个基类派生一个派生类的状况,称为单继承或单重继承
  • 派生类继承了基类名一、基类名二、……、基类名n的全部成员变量和成员函数,各基类名前面的继承方式说明符用于限制派生类中的成员对该基类名中成员的访问权限,其规则与单继承状况同样。
  • 多重继承状况下若是多个基类间成员重名时,按以下方式进行处理:对派生类而言,不加类名限定时默认访问的是派生类的成员;而要访问基类重名成员时,要经过类名加以限定。
#include<iostream>
using namespace std;
class CB1{
    public:
        int a; 
        CB1 (int x){ a=x; }
        void showa(){ cout<<"ClassCB1==>a="<<a<<endl; }
};
class CB2{
    public:
        int a; 
        CB2 (int x){ a=x; }
        void showa(){ cout<<"ClassCB1==>a="<<a<<endl; }
};
//多重继承,两个基类
class CD:public CB1,public CB2 {
public:
    int a; //与两个基类成员变量a重名
    CD(int x,int y,int z):CB1(x) ,CB2(y){ a=z; }
    //与两个基类成员函数showa()重名
    void showa() { 
        cout<<"Class CD==>a="<<a<<endl; 
    }
    void print3a(){ 
        cout<<"a="<<a<<endl;
        cout<<"CB1::a="<<CB1::a<<endl;
        cout<<"CB2::a="<<CB2::a<<endl;
    }
};
int main(){
    CB1 CB1obj(11);
    CB1obj.showa();//ClassCB1==>a=11
    CD CDobj(101,202,909);
    CDobj.showa(); //调用派生类的showa()  //Class CD==>a=909
    CDobj.CB1::showa(); //调用基类的showa() //ClassCB1==>a=101
    cout<<"CDobj.a="<<CDobj.a<<endl;//访问派生类成员a  //CDobj.a=909
    cout<<"CDobj.CB2::a="<<CDobj.CB2::a<<endl; //CDobj.CB2::a=202
}

二、多重继承的二义性

⚫ 若是派生类中新增了同名成员,则派生类成员将隐藏全部基类的同名成员。使用“派生类对象名.成员名”或“派生类对象指针->成员名”的方式能够惟一标识和访问派生类新增成员。这种状况下,不会产生二义性。
⚫ 若是派生类中没有新增同名成员,当知足访问权限时,使用“派生类对象名.成员名”或“派生类对象指针->成员名”方式时,系统没法判断究竟是调用哪一个基类的成员,从而产生二义性。为了不二义性,必须经过基类名和做用域分辨符来标识成员。
⚫ 当要访问派生类对象中的某个变量时,添加“基类::”做为前缀,指明须要访问从哪一个基类继承来的,从而能够排除二义性。

三、继承权限控制

设计继承类时,须要使用继承方式说明符指明派生类的继承方式。继承方式说明符能够是public(公有继承)、private(私有继承)或protected(保护继承)

 

 

 4、类型兼容

⚫类型兼容规则是指在须要基类对象的任何地方,均可以使用公有派生类的对象来替代,也称为赋值兼容规则。在公有派生的状况下,有如下3条类型兼容规则。

  • 1)派生类的对象能够赋值给基类对象。
  • 2)派生类对象能够用来初始化基类引用。
  • 3)派生类对象的地址能够赋值给基类指针,即派生类的指针能够赋值给基类的指针。

⚫上述3条规则反过来是不成立的。例如,不能把基类对象赋值给派生类对象。在进行替代以后,派生类对象就能够做为基类的对象使用了,但只能使用从基类继承的成员。

⚫若是类B为基类,类D为类B的公有派生类,则类D中包含了基类B中除构造函数、析构函数以外的全部成员。这时,根据类型兼容规则,在基类B的对象能够出现的任何地方,均可以用派生类D的对象来替代。假设有如下的声明:

class B{…}
class D : public B{…}
B b1, *pb1;
D d1;

⚫这时,派生类对象能够隐含转换为基类对象,即用派生类对象中从基类继承来的成员变量的值,逐个为基类对象的成员变量的值进行赋值。b1=d1;
⚫派生类的对象也能够用来初始化基类对象的引用,即:B &rb=d1;
⚫派生类对象的地址能够隐含转换为指向基类的指针,即派生类对象的地址赋给基类指针:pb1=&d1;
⚫因为类型兼容规则的引入,对于基类及其公有派生类的对象,可使用相同的函数统一进行处理。由于当函数的形参为基类的对象(或引用、指针)时,实参能够是派生类的对象(或指针),从而没有必要为每个类设计单独的模块,大大提升了程序的效率。

5、私有继承

 

 6、保护继承

  保护继承中,基类的公有成员和保护成员都以保护成员的身份出如今派生类中,而基类的私有成员不能够直接访问。这样,派生类的其余成员能够直接访问从基类继承来的公有和保护成员,但在类外经过派生类的对象没法直接访问它们。

class A{};
class B:private A{};//私有继承
class C:protected A{};//保护继承
相关文章
相关标签/搜索