C++继承

来源:互联网 发布:rgb转16进制java 编辑:程序博客网 时间:2024/06/01 07:29

继承的概念:
通过继承机制,可以利用已有的数据来定义新的数据类型。新的数据类型不仅拥有新的成员,同时也拥有旧的成员。我们称已经存在的用来派生新类的类为基类,也称父类。由已存在的的类派生出的新类为派生类,也称子类。是面向对象复用的重要手段。

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

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

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 = 1;    //公有成员        _month = 1;  //保护成员只能在父类和子类内部使用        _year = 1;    //在父类中是私有成员,在子类中无法访问。    }};int main(){    Day d1;    d1._day = 10;    d1._month = 10;  //无法在类外访问    d1._year = 10;   //无法访问保护成员    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 = 10;    b1.A::b = 20;}

对象模型为
这里写图片描述
可以看出里面的有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 = 1;    d1._b2 = 2;    d1._d = 3;    d1.B1::_a = 4;    d1.B2::_a = 5;}
原创粉丝点击