C++继承

本文详细介绍了C++中的继承概念,包括派生类的对象内存空间布局、继承关系与复合关系的区别、成员覆盖、保护成员以及派生类构造函数的使用。此外,还探讨了public继承的赋值兼容规则,强调了正确理解和应用这些概念对于C++程序设计的重要性。
摘要由CSDN通过智能技术生成

概念

继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)
派生类具有基类的全部特点。而且派生类时通过基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。派生类一经定义后,可以独立使用,不依赖于基类。
派生类拥有基类的全部成员函数和成员变量,不论是private、protect、public。在派生类的各个成员函数中,不能访问基类中的private成员。
下面给一个例子

在这里插入图片描述

class Student
{
    private:
        string Name;
        int Age;

    public:
        int GPA;
        bool IsThreegood{};
        void PrintInfo(){
            cout << Name << ":" << Age << endl;
        }
};

class UndergraduateStudent:public Student{
    private:
        int Department;//扩充
    public:
        bool IsThreegood() { return GPA >= 3.6; }//覆盖
        bool canBaoyan() { return GPA >= 3.8; };   //扩充
        void PrintInfo(){
            Student::PrintInfo();//调用基类的PriintInfo
            cout << Department << endl;
        }
};//派生类的写法是  类名:public 基类名

像上面的程序,基类会有一个PrintInfo函数,然后如果派生类里面想要实现同样的功能,但是无法读取基类的Private成员,所以会调用基类的函数输出基类里面的成员变量,然后再将派生类里面的成员变量输出。如果有SetInfo函数也是同理。

派生类的对象的内存空间

派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前
在这里插入图片描述

继承关系和复合关系

继承:“”关系。如果有一个基类A,B是A的派生类,那么逻辑上要求:“一个B对象也一个A对象”。比如上面的从学生类派生出中学生类,这就是一个合理的派生;而比如从男人类派生出女人类,就是不合理的,因为“一个女人也是一个男人”在逻辑上是不对的。
复合:“”关系。类C中有成员变量k,k是类D的对象,则C和D是复合关系。一般逻辑上要求:“D对象是C对象的固有属性或组成部分”。比如点类和圆类:

class Point{
    private:
        double x, y;
};
class Circle:public Point{
    private:
        double r;
};

像上面就是虽然看起来是对的,但实际上使用的是继承关系,而圆并不是一个点,所以这种继承关系在逻辑上是不成立的,应该写成下面的复合关系:

class Point{
    private:
        double x, y;
    	friend class Circle;
};
class Circle:{
    private:
        Point center;
        double r;
};

因为圆是有圆心的,这就是一个特殊的点,所以复合关系在逻辑上是正确的。而为了Circle类便于操作center这个成员变量,所以需要在Point类中将Circle类声明为友元。接下来再举一个复合关系的例子:
假设有一个主人类,一个狗类,每个主人最多有10条狗,而每只狗只能有一个主人,我们很容易想到这样写:

class Dog;
class Master{
    Dog dogs[10];
};
class Dog{
    Master m;
};

然而实际上这样是错误的,其中出现了循环定义的问题,编译会出错。比如问你一个Master对象的大小是多少,其实是算不出来的。那如果写成指针数组的形式:

class Dog;
class Master{
    Dog *dogs[10];
};
class Dog{
    Master m;
};

这样也是不好的,因为可能多个狗指向同一个主人,那么怎么维护不同的狗中的主人的信息的一致性。比如一条狗里面的主人的信息修改了,那么其他同样主人的狗里面的信息都需要修改。
在这里插入图片描述
如果反过来:

class Master ;
class Dog{
    Master *m;
};
class Master{
    Dog d[10];
};

这样勉强可以,但一个问题是其实逻辑上还是有点别扭(狗并不是主人的固有属性),另一方面,所有的狗对象都包含在主人对象中,如果想要对它们进行修改,则需要通过主人对象,这是不太方便也不太好的。
在这里插入图片描述
正确的写法是:

class Master;
class Dog{
    Master *pm;
};
class Master{
    Dog *dogs[10];
};

在这里插入图片描述

覆盖

派生类可以定义一个和基类成员同名的成员,叫做覆盖。派生类中的访问缺省的情况是访问派生类中定义的成员。要在派生类中访问由基类定义的同名成员时,要使用作用域符号::

class Base{
    public:
        int i;
        void func();
};
class Derived:public Base{
    public:
        int i;
        void func();
        void access(){
            i = 5;//派生类的
            Base::i = 2;//基类的
            func();//派生类的
            Base::func();//基类的
        }
};

int main()
{
    Derived obj;
    cout << obj.i << endl;
    cout << obj.Base::i << endl;
    system("pause");
}

覆盖基类的成员函数是很常见的,但是不建议在派生类中覆盖基类的成员变量。

保护成员 protected

在这里插入图片描述

派生类的构造函数

在创建派生类对象时,需要调用基类的构造函数,来初始化派生类对象从基类继承的成员。
在执行一个派生类的构造函数之前,总是先执行基类的构造函数。
对象消亡的时候,会先调用派生类的析构函数,再调用基类的析构函数。

class Bug
{
private:
    int nLegs;
    int nColor;

public:
    Bug(int legs, int color) : nLegs(legs), nColor(color) {}
};

class FlyBugpublic Bug
{
private:
    int nWings;

public:
    FlyBug(int legs, int color, int wings)
    {
        nLegs = legs;
        nColor = color;
        nWings = wings;
    } //错误的构造函数,因为nLegs和nColor是基类的私有成员,不能在派生类中访问
    FlyBug(int legs, int color, int wings) : Bug(legs, color), nWings(wings) {}//正确的构造函数
};

派生类的构造函数中也可以省略基类的构造函数,这样会调用基类的默认构造函数

public 继承的赋值兼容规则

class Base{ };
class derived:public base{ };
base b;
derived d;

以下三条必须要在public继承下才能进行

(1)派生类的对象可以赋值给基类对象
因为我们说“一个派生类对象就是一个基类对象”

b=d'

就是把d中的Base对象拷贝到b里面去,如果d=b就是错误的
(2)派生类对象可以初始化基类引用

base &bd=d;

(3)派生类对象的地址可以赋值给基类指针

base *pb=&d;

这个指针就指向了派生类对象中的基类对象,因为基类对象的存储就在派生类对象存储地址的最前面,所以这个指针就指向了派生类对象的起始地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值