【C++总结】06:继承

继承


1.语法与方式:

继承的语法:class 子类:继承方式 父类

继承的3种方式:

  1. 公共继承
  2. 保护继承
  3. 私有继承

image-20220211223531294

#include<iostream>
using namespace std;

class Base1{
public:
    int a;
protected:
    int b;
private:
    int c;
};

class Son1:public Base1{
public:
    void func(){
        a = 10;//父类中public权限的成员,到子类中依然是public权限
        b = 10;//父类中protected权限的成员,到子类中依然是protected权限
        //c = 10;//父类中private权限的成员,子类中访问不到unreachable报错
    }
};
void test01(){
    Son1 s1;
    s1.a = 100;
    //s1.b = 100;//protected权限在子类继承中可以访问,在类外无法访问
    //s1.c = 100;//private权限在子类继承中无法访问,在类外无法访问
}

class Son2:protected Base1{
public:
    void func(){
        a = 20;//父类中public权限的成员,到子类中变为protected权限
        b = 20;//父类中protected权限的成员,到子类中依然是protected权限
        //c = 20;//父类中private权限的成员,子类中访问不到unreachable报错
    }
};
void test02(){
    Son2 s1;
    //s1.a = 200;//protected权限在子类继承中可以访问,在类外无法访问
    //s1.b = 200;//protected权限在子类继承中可以访问,在类外无法访问
    //s1.c = 200;//private权限在子类继承中无法访问,在类外无法访问
}

class Son3:private Base1{
public:
    void func(){
        a = 30;//父类中public权限的成员,到子类中变为private权限
        b = 30;//父类中protected权限的成员,到子类中变为private权限
        //c = 30;//父类中private权限的成员,子类中访问不到unreachable报错
    }
};
void test03(){
    Son3 s1;
    //s1.a = 200;//private权限在子类继承中可以访问,在类外无法访问
    //s1.b = 200;//private权限在子类继承中可以访问,在类外无法访问
    //s1.c = 200;//private权限在子类继承中无法访问,在类外无法访问
}

int main(){
    system("pause");
    return 0;
}

从父类继承过来的成员,哪些属于子类对象中?

答:父类中所有的非静态成员属性都会被子类继承,父类中private成员属性是被编译器隐藏而无法访问,但也继承下去了。

#include<iostream>
using namespace std;

class Base{
public:
    int a;
protected:
    int b;
private:
    int c;
};

class Son:public Base{
public:
    int d;
};

void test01(){
    //父类中所有的非静态成员属性都会被子类继承
    //父类中private成员属性是被编译器隐藏而无法访问,但也继承下去了
    cout << "size of Son: " << sizeof(Son) << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220212222650709

子类全部继承了父类Base的属性(包括私有属性)后的大小为16字节。

2.继承中构造与析构顺序:

子类继承父类后,当创建子类对象时也会调用父类的构造函数,父类与子类构造与析构的先后顺序?

#include<iostream>
using namespace std;

class Base{
public:
    Base(){
        cout << "Base的构造函数!" << endl;
    }
    ~Base(){
        cout << "Base的析构函数!" << endl;
    }
};

class Son:public Base{
public:
    Son(){
        cout << "Son的构造函数!" << endl;
    }
    ~Son(){
        cout << "Son的析构函数!" << endl;
    }
};

void test01(){
    Son s1;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220213211621765

总结:在继承中先调用父类构造函数,再调用子类的构造函数,析构顺序与构造相反。

3.继承同名成员处理方式:

当子类与父类出现同名的成员,如何通过子类对象,访问到子类 or 父类中同名的数据?

  1. 访问子类同名成员,直接访问即可
  2. 访问父类同名成员,需要添加作用域
#include<iostream>
using namespace std;

class Base{
public:
    Base(){
        a = 100;
    }
    void func(){
        cout << "Base作用域下的func()函数调用" << endl;
    }
    int a;
};

class Son:public Base{
public:
    Son(){
        a = 200;
    }
    void func(){
        cout << "Son作用域下的func()函数调用" << endl;
    }
    int a;
};

//1.同名成员属性
void test01(){
    Son s;
    cout << "s.a = " << s.a << endl;
    cout << "s.Son::a = " << s.Son::a << endl;
    cout << "s.Base::a = " << s.Base::a << endl;
}

//2.同名成员函数
void test02(){
    Son s;
    s.func();
    s.Son::func();
    s.Base::func();
}

int main(){
    test01();
    test02();
    system("pause");
    return 0;
}

image-20220213213441221

注意:如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数

4.继承同名静态成员处理方式:

继承中同名的静态成员在子类对象上如何进行访问?静态成员和非静态成员出现同名,处理方式一致:

  1. 访问子类同名成员,直接访问即可
  2. 访问父类同名成员,需要添加作用域
#include<iostream>
using namespace std;

class Base{
public:
    static int a;
    static void func(){
        cout << "Base下的func()函数调用" << endl;
    }
};
int Base::a = 100;

class Son:public Base{
public:
    static int a;
    static void func(){
        cout << "Son下的func()函数调用" << endl;
    }
};
int Son::a = 200;

//1.同名静态成员属性
void test01(){
    cout << "(1)通过对象访问静态成员属性" << endl;
    Son s;
    cout << "s.a = " << s.a << endl;
    cout << "s.Son::a = " << s.Son::a << endl;
    cout << "s.Base::a = " << s.Base::a << endl;
    cout << "(2)通过类名访问静态成员属性" << endl;
    cout << "Son作用域下的成员属性a = " << Son::a << endl;
    cout << "Base作用域下的成员属性a = " << Son::Base::a << endl;
    //第一个::代表通过类名的方式访问、第二个::代表访问父类作用域下
}

//2.同名静态成员函数
void test02(){
    cout << "(1)通过对象访问静态成员函数" << endl;
    Son s;
    s.func();
    s.Base::func();
    cout << "(2)通过类名访问静态成员函数" << endl;
    Son::func();
    Son::Base::func();
}

int main(){
    test01();
    test02();
    system("pause");
    return 0;
}

image-20220213221041820

总结:同名静态成员处理方式和非静态处理方式一样,不同之处在于有两种访问类内成员属性/函数的方式(通过对象&类名)。

5.多继承语法:

cpp中允许一个类继承多个类

语法:class 子类:继承方式 父类1, 继承方式 父类2...

多继承中可能引发父类中有同名成员出现,需要添加作用域进行区分。

注意:cpp实际开发中不建议使用多继承

#include<iostream>
using namespace std;

class Base1{
public:
    Base1(){
        a = 100;
        b = 100;
    }
    int a;
    int b;
};

class Base2{
public:
    Base2(){
        a = 200;
        b = 200;
    }
    int a;
    int b;
};

//子类需要继承Base1与Base2
class Son:public Base1, public Base2{
public:
    Son(){
        c = 300;
        d = 400;
    }
    int c;
    int d;
};

void test01(){
    Son s;
    cout << "sizeof Son = " << sizeof(s) << endl;
    //当父类中出现同名成员时,需要加作用域进行区分
    cout << "Base1::a = " << s.Base1::a << endl;
    cout << "Base2::a = " << s.Base2::a << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220213224935631

6.菱形继承(虚继承):

两个派生类继承同一个Base基类,又有某个类同时继承着这两个派生类,这种继承方式被称为菱形继承(砖石继承)。

image-20220213233159715

#include<iostream>
using namespace std;

class Animal{
public:
    int age;
};

class Tiger:public Animal{};

class Rabbit:public Animal{};

class Tibbit:public Tiger, public Rabbit{};

void test01(){
    Tibbit tibbit;
    //菱形继承时,两个父类拥有相同的数据,需要加上作用域进行区分
    tibbit.Tiger::age = 18;
    tibbit.Rabbit::age = 28;
    cout << "tibbit.Tiger::age" << tibbit.Tiger::age << endl;
    cout << "tibbit.Rabbit::age" << tibbit.Rabbit::age << endl;
    //这份数据我们知道,只要有一份即可(菱形继承导致数据冗余)
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220213235536579

image-20220213234934576

菱形继承导致数据重复冗余、浪费资源,继承基类Base时添加virtual关键字利用虚继承可以解决:

#include<iostream>
using namespace std;

class Animal{
public:
    int age;
};

class Tiger:virtual public Animal{};

class Rabbit:virtual public Animal{};

class Tibbit:public Tiger, public Rabbit{};

void test01(){
    Tibbit tibbit;
    //菱形继承时,两个父类拥有相同的数据,需要加上作用域进行区分
    tibbit.Tiger::age = 18;
    tibbit.Rabbit::age = 28;
    cout << "tibbit.Tiger::age = " << tibbit.Tiger::age << endl;
    cout << "tibbit.Rabbit::age = " << tibbit.Rabbit::age << endl;
    //这份数据我们知道,只要有一份即可(菱形继承导致数据冗余)
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220213235656676

image-20220214000425473

注意:vbptr(virtual base pointer)虚拟基类指针,指向vbtable(virtual base table)虚拟基类表格

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值