C++ 继承

1. 概念

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

2. 继承方式

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

                        

                                                           图1   继承方式与成员访问属性

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

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

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类的内存布局:

Student类的内存布局:

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

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)
{
    派生类新增成员的初始化语句;
}

 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值