继承

继承的概念:

       首先我们举一个例子:

       

     如图, “公马”继承了“马”的全部特征,增加了“雄性”的新特征。“白公马”继承了“公马”的全部特征,再增加“白色”的特征。“公马”是“马”派生出来的一个分支,“白公马”是“公马”派生出来的一个分支。   

       继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。

       C++中的所谓的“继承”就是在一个已存在的类的基础上建立一个新的类。已存在的类(例如“马”)成为“基类”或“父类”。新建的类(例如“公马”)称为“派生类”或“子类”。

       基类:负责定义各层次关系中所有类的共同成员。

       派生类:负责定义各自特有的成员。

class Person     //父类/基类
{
private:
    int _age;
    string _name;
};
class Student/*子类/派生类*/:public/*访问限定符*/ Person   
{
private:
    int _num;
};


继承方式:
1、public 公有继承
基类的公有成员和保护成员作为派生类的成员,保持原有状态和属性,私有成员不能被这个派生类的子类访问。

2、protected 保护继承
基类的所有公有成员和保护成员都成为派生类的保护成员,并且他们只能被派生类成员函数或友元访问,基类的私有成员依然是私有的。

如果一些基类成员不想被基类对象直接访问,但需要在派生类中访问,就应该将其声明为protected成员

3、private 私有继承
基类的公有成员和保护成员都称为派生类的私有成员,并且不能被这个派生类的子类所访问。

4、使用class时,默认继承方式为private,使用struct时,默认继承方式为pbulic。

不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,但是基类的私有成员存在但不可以访问。

下面对pbulic 继承方式进行演示:

class Date
{
public:
    int _day;
protected:
    int _month;
private:
    int _year;
};
class Day:public Date
{
public:
    void Fun()
    {
        _day = 9;    //公有成员
        _month = 9;  //保护成员只能在父类和子类内部使用
        _year = 2017;    //在父类中是私有成员,在子类中无法访问。
    }
};
int main()
{
    Day d1;
    d1._day = 10;
    d1._month = 9;  //无法在类外访问
    d1._year = 2017;   //无法访问保护成员
    return 0;
}

继承与转换:
1.赋值兼容规则:public继承
在需要基类对象的时候,可以使用共有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数,析构函数以外的所有成员,而且所有成员的访问控制属性与基类完全相同。则该公有派生类就具备了基类的所有功能。
(1).派生类的对象可以赋值给基类。
(2).派生类的对象可以初始化基类的引用。
(3).派生类对象的地址可以赋给指向基类的指针/引用。
(4).用公有派生类代替基类。


class Person
{
public:
    void Display()
    {
        cout << _name << endl;
    }
private:
    string _name;
};
class Student:public Person
{
public:
    int _num;
};
void test()
{
    Person p;
    Student s;
    p = s;     //子类可以初始化父类  
    //Person *p1 = &s;
    //Person &r1 = s;//父类的指针和引用可以指向子类对象
}

继承中的构造与析构:

class Person
{
public:
    Person(const char *name = "", int id = 0)
        :_name(name)
        ,_number(id)
    {}
protected:
    string _name;  //姓名
    int _number;       //身份证
};
class Student :public Person
{
public:
    Student(const char*name, int id, int stuNum)
        :Person(name,id)
        ,_num(stuNum)
    {}
    void Display()
    {
        cout << "学号" << _num << endl;
        cout << "身份证" << Person::_number << endl;
        cout << "姓名" << _name << endl;
    }
protected:
    int _num;  //学号
};

该代码的对象模型为:

     


可以看出如果想要构造一个Student类型的对象,需要调用父类的构造函数对子类中的父类成员进行初始化。
同样,在析构一个子类对象时,需要调用父类的析构函数对子类中的父类成员进行析构。
注意:如果父类的构造函数中有参数时,需要在子类的初始化列表中显示调用。(因为编译器无法提供一个默认构造函数)

调用顺序:
派生类的构造函数:父类的构造函数—成员对象的构造函数—子类的构造函数。
析构函数则与构造函数相反。
测试函数:

class Date
{
public:
    Date() {
        cout << "Date()" << endl;
    }
    ~Date() {
        cout << "~Date()" << endl;
    }
};
class Base
{
public:
    Base(){
        cout << "Base()" << endl;
    }
    ~Base() {
        cout << "Base()" << endl;
    }
};
class Derived:public Base
{
public:
    Derived(){
        cout << "Derived()" << endl;
    }
    ~Derived() {
        cout << "Derived()" << endl;
    }
private:
    Date d1;
};
void test() {
    Derived d2;
}
int main()
{
    test();
    system("pause");
    return 0;
}



继承中的重名情况:
1.成员变量重名:

class A
{
public:
    int a;
    int b;
};
class B :public A
{
public:
    int b;
    int c;
};
void test() {
    B b1;
    b1.b = 1;
    b1.A::b = 2;
}
对象模型为:


可以看出里面的有2分相同名字的成员,在进行直接访问b成员时,会直接访问派生类中的b,如果想要访问A中的b,则需要加上作用域。

2.成员函数重名:
成员函数重名与成员变量重名基本相同,在不声明作用域的情况下,默认调用子类中的成员。

class A
{
public:
    void DisPlay()
    {
        cout << "A" << endl;
    }
};
class B :public A
{
public:
    void DisPlay()
    {
        cout << "B" << endl;
    }
};
void test() {
    B b1;
    b1.DisPlay();
    b1.A::DisPlay();
}
单继承与多继承:
单继承: 一个子类只有一个直接父类。 


多继承: 一个子类有两个或两个以上的直接父类称为多继承关系。 


菱形继承:


class A
{
public:
    int _a;
};
class B1 :public A
{
public:
    int _b1;
};
class B2 :public A
{
public:
    int _b2;
};
class D :public B1, public B2
{
public:
    int _d;
};
void test()
{
    D d1;
    d1._a = 1;
}

但是这里会出现D::a,不明确的错误提示。

在创建出的d1对象中,_a有两份。所以当你直接访问时,编译器不知道该给哪一个赋值。
所以菱形继承存在二义性问题。 对于base class的调用时,要说明作用域。

void test()
{
    D d1;
    d1._b1 = 10;
    d1._b2 = 11;
    d1._d = 12;
    d1.B1::_a = 13;
    d1.B2::_a = 14;
}


 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值