C++ 继承

举报
Linux猿 发表于 2021/10/24 11:02:41 2021/10/24
【摘要】 🎈 作者:Linux猿 🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C++、面试、刷题、算法尽管咨询我,关注我,有问题私聊! 🎈 欢迎小伙伴们点赞👍、收藏⭐、留言💬

🎈 作者:Linux猿

🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C++、面试、刷题、算法尽管咨询我,关注我,有问题私聊!

🎈 欢迎小伙伴们点赞👍、收藏⭐、留言💬


1. 概念

    类的继承,是新的类从已有类那里得到已有的特性。或从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。

2. 继承方式

    继承方式规定了如何访问基类继承的成员。继承方式有public, protected, private。继承方式不影响派生类内原有数据成员的访问权限,影响了从基类继承来的成员的访问权限,包括派生类内的访问权限和派生类对象。

 派生类的组成:派生类中的成员,包含两大部分,一类是从基类继承过来的,一类是自己增加的成员。

从基类继承过过来的表现其共性,而新增的成员体现了其个性。除了构造器与析构器,派生类全盘接收。基类有可能会造成派生类的成员冗余,所以说基类是需设计的。派生类有了自己的个性,使派生类有了意义。

3. 派生类的构造

    派生类中由基类继承而来的成员的初始化工作还是由基类的构造函数完成,派生类中新增的成员在派生类的构造函数中初始化。

 格式:

派生类名::派生类名(参数总表)
    :基类名(参数表),内嵌子对象(参数表)
{
    派生类新增成员的初始化语句; //也可出现在参数列表中
}

规则:

构造函数的初始化顺序并不以上面的顺序进行,而是根据声明的顺序初始化;
如果基类中没有默认构造函数(无参),那么在派生类的构造函数中必须显示调用基类构造函数,以初始化基类成员。
派生类构造函数执行的次序:基类->成员(有可能也是类对象)->子类;
子类构造器中,要么显示的调用父类的构造器(传参),要么隐式的调用。发生隐式调用时,父类要有无参构造器或是可以包含无参构造器的默认参数函数;
4. 派生类的拷贝构造

格式:

派生类::派生类(const 派生类& another)
:基类(another),派生类新成员(another.新成员)
{
    函数体;
}

拷贝构造的例子:

#include <iostream>
using namespace std;
 
class Student {
    public:
        Student(int n,char s);
        Student(const Student &another);
        ~Student(){}
        void dis();
    private:
        int num;
        char sex;
};
 
Student::Student(int n, char s) //基类的构造函数
    :num(n),sex(s){
}
 
void Student::dis(){
    cout<<num<<endl;
    cout<<sex<<endl;
}
 
Student::Student(const Student &another){ //基类的拷贝构造函数
    num = another.num;
    sex = another.sex;
}
 
class Graduate: public Student {
    public:
        Graduate(int in,char cs,float fs);
        ~Graduate(){}
        Graduate(const Graduate & another);
        void dump() {
            dis();
            cout<<salary<<endl;
        }
    private:
        float salary;
};
 
Graduate::Graduate(int in, char cs, float fs)//派生类的构造函数
    :Student(in,cs),salary(fs) {
}
 
Graduate::Graduate(const Graduate & another)    //派生类的拷贝构造函数
    :Student(another),salary(another.salary) {
}
 
int main()
{
    Graduate g(2001,'x',2000);
    g.dump();
    cout<<"----------------------"<<endl;
    Graduate gg = g;
    gg.dump();
    return 0;
}

看一下Graduate类的内存布局:

AA.png

Student类的内存布局:

BB.png

   派生类中的默认拷贝构造器会调用父类中默认或自实现拷贝构造器(也就是说派生类如果没有显示的拷贝构造器,它的默认的拷贝构造器也会去调用基类的默认或自实现的拷贝构造器),若派生类中自实现拷贝构造器,则必须显示的调用父类的拷贝构造器。

5. 派生类的赋值运算符重载

    赋值运算符函数不是构造器,所以可以继承,语法上就没有构造器的严格一些。

格式:

子类& 子类::operator=(const 子类& another)
{
    if(this == &another)
        return *this;              //防止自赋值
    父类::operator =(another);     // 调用父类的赋值运算符重载
    //.......                      //子类成员初始化
    return * this;
}

例子:

#include <iostream>
using namespace std;
 
class Student {
    public:
        Student(string sn,int n,char s);
        ~Student() {}
        Student & operator=(const Student & another);
        void dis();
    private:
        string name;
        int num;
        char sex;
};
 
Student::Student(string sn, int n, char s) //基类的构造函数
    :name(sn),num(n),sex(s){
}
 
Student &Student::operator=(const Student & another) {
    cout<<"基类赋值运算符重载"<<endl;
    this->name = another.name;
    this->num = another.num;
    this->sex = another.sex;
    return * this;
}
 
void Student::dis() {
    cout<<name<<endl;
    cout<<num<<endl;
    cout<<sex<<endl;
}
 
class Graduate: public Student {
    public:
        Graduate(string sn,int in,char cs,float fs);
        ~Graduate() {}
        Graduate & operator=(const Graduate & another);
        void dump() {
            dis();
            cout<<salary<<endl;
        }
    private:
        float salary;
};
 
Graduate::Graduate(string sn, int in, char cs, float fs)//派生类的构造函数
    :Student(sn,in,cs),salary(fs) {
}
 
Graduate & Graduate::operator=(const Graduate & another) {
    cout<<"派生类赋值运算符重载"<<endl;
    if(this == &another)
        return *this;
    Student::operator =(another);    //基类的赋值运算符
    this->salary = another.salary;
    return * this;
}
 
int main() {
    Graduate g("liuneng",2001,'x',2000);
    g.dump();
    Graduate gg = g;    //调用默认拷贝构造函数
    gg.dump();
    cout<<"-----------"<<endl;
    Graduate ggg("gege",2001,'x',4000);
    ggg.dump();
    ggg = g;            //调用赋值运算符重载函数
    ggg.dump();
    return 0;
}

    派生类的默认赋值运算符重载函数会调用父类的默认或自实现函数。派生类若自实现,则不会发生调用行为,也不报错(区别拷贝),赋值错误,若要正确,需要显示的调用父类的构造器。

6. 派生类友元函数

    由于友元函数并非类成员,因引不能被继承,在某种需求下,可能希望派生类的友元函数能够使用基类中的友元函数。为此可以通过强制类型转换,将派生类的指针或是引用强转为其类的引用或是指针,然后使用转换后的引用或是指针来调用基类中的友元函数。

例子:

#include <iostream>
using namespace std;
class Student {
public:
    Student(int _a, int _b):a(_a),b(_b){}
    friend ostream &operator<<(ostream & out, Student & stu);
private:
    int a;
    int b;
};
ostream &operator<<(ostream & out, Student & stu) {
    out<<stu.a<<"--"<<stu.b<<endl;
}
class Graduate:public Student {
public:
    Graduate(int _a, int _b, int _c, int _d):Student(_a, _b),c(_c),d(_d){}
    friend ostream &operator<<(ostream & out, Graduate & gra);
private:
    int c;
    int d;
};
ostream &operator<<(ostream & out, Graduate & gra) {
    out<<(Student&)gra<<endl;
    out<<gra.c<<"++"<<gra.d<<endl;
}
int main() {
    Graduate g(3, 4, 5, 6);
    cout<<g<<endl;
    return 0;
}

7. 派生类析构函数的语法

    派生类的析构函数的功能是在该对象消亡之前进行一些必要的清理工作,析构函数没有类型,也没有参数。析构函数的执行顺序与构造函数相反。

析构顺序:子类 -> 成员 ->基类。

8. 多继承

    从继承类别上分,继承可分为单继承和多继承,上面总结的都是单继承。

格式:

派生类名::派生类名(参数总表)
    :基类名1(参数表1),基类名(参数名2)....基类名n(参数名n),
      内嵌子对象1(参数表1),内嵌子对象2(参数表2)....内嵌子对象n(参数表n)
{
    派生类新增成员的初始化语句;
}

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。