C++继承

继承语法

class 子类名 :继承方式 父类

继承方式

  • 公共继承 (public) 子类继承到父类的成员后,依然保持原有的成员访问权限,例如父类是public还是保持public,父类是protected依然保持是protected。
  • 保护继承 (protected) 子类继承到父类成员后,会将public权限的成员修改为protected权限。
  • 私有继承 (private) 子类继承到父类成员后,会将public和protected权限的成员修改为private权限。
#include <iostream>
#include <string>
using namespace std;

class father {
   public:
    int value1;

   protected:
    int value2;

   private:
    int value3;
};

class sonPublic : public father {};
class sonProtected : protected father {};
class sonPrivate : private father {};

int main(int argc, const char** argv) {
    /* sonPublic继承后, value1、value2、value3的访问权限是保持不变的 */
    sonPublic s1;
    s1.value1 = 10;
    cout << s1.value1 << endl; /* 输出10 */
    // s1.value2 = 10; /* 保护权限,类外无法访问 */
    // s1.value3 = 10; /* 私有权限,类外无法访问 */

    sonProtected s2;
    // s2.value1 = 10; /* 保护权限,类外无法访问 */
    // s2.value2 = 10; /* 保护权限,类外无法访问 */
    // s2.value3 = 10; /* 私有权限,类外无法访问 */

    sonPrivate s3;
    // s3.value1 = 10; /* 私有权限,类外无法访问 */
    // s3.value2 = 10; /* 私有权限,类外无法访问 */
    // s3.value3 = 10; /* 私有权限,类外无法访问 */
    return 0;
}

父类的private权限成员的继承

访问权限说明中,private的权限是私有的,在子类中也是无法访问到父类的private权限的,但是private权限的成员还是会被子类继承下来,只是被隐藏了。友元函数可以访问类的私有成员,但是是无法通过子类的友元函数访问的父类的私有成员的。

#include <iostream>
#include <string>
using namespace std;

class father {
   public:
    father() : value1(1), value2(2), value3(3) {}

   public:
    int value1; /* 初始值 1 */

   protected:
    int value2; /* 初始值 2 */

   private:
    int value3; /* 初始值 3 */
};

class son : public father {
    friend void sonFriendFunc();

   public:
    son() : sonValue(11) {}

   private:
    int sonValue; /* 初始值 11 */
};

void sonFriendFunc() {
    son s;

    cout << sizeof(s) << endl; /* 输出:16,sizeof(int) * 4 = 16*/

    cout << s.sonValue << endl; /* 输出:11,友元函数,可以访问到son中的private成员 */
    cout << s.value1 << endl;   /* 输出:1 */
    cout << s.value2 << endl;   /* 输出:2 */
    // cout << s.value3 << endl;   /* 报错 */
}

int main(int argc, const char** argv) {
    sonFriendFunc();

    return 0;
}

父子构造、析构的顺序

一句话:有父才有子,但是白发人送黑发人

子类访问父类中的成员

  • 访问的成员子类中没有同名的成员: 直接访问即可。
  • 访问的成员子类中有同名的成员: 需要加父类作用域用来标识,如果直接访问则是访问的子类成员。
  • 注意:如果子类中有和父类同名的成员函数,会自动隐藏父类中所有的同名函数,就算是重载的成员函数也无法直接访问。
  • 静态的成员也符合上述标准,静态成员的类名访问方式需要加上作用域即可。
#include <iostream>
#include <string>
using namespace std;

class father {
   public:
    father() {
        this->father_value = 1;
        this->value = 10;
    }

    void func() {
        cout << "father func()" << endl;
    }

    /* 父类重载func */
    void func(int value) {
        cout << "father func(int value). value:" << value << endl;
    }

    int father_value; /* 父类特有的成员 */
    int value;
    static int s_value;
};

int father::s_value = 888;

class son : public father {
   public:
    son() {
        this->value = 20;
    }

    void func() {
        cout << "son func()" << endl;
    }

    int value;
    static int s_value;
};

int son::s_value = 777;

int main(int argc, const char** argv) {
    son s;
    cout << s.father_value << endl;  /* 输出:1, 子类中无同名成员,直接访问到了父类成员 */
    cout << s.value << endl;         /* 输出:20, 子类中有父类同名成员,直接访问的是子类成员 */
    cout << s.father::value << endl; /* 输出:10,, 子类中有父类同名成员,作用域访问父类成员 */
    s.func();                        /* 输出:son func(), 子类中有父类同名成员,直接访问的是子类成员 */
    s.father::func();                /* 输出:father func(),, 子类中有父类同名成员,作用域访问父类成员 */
    // s.func(999);                  /* 报错,因为父类中所有的func名称的函数都无法访问,子类中没有func(int value)函数 */
    s.father::func(999);                  /* 输出:father func(int value). value:999 */
    cout << son::s_value << endl;         /* 输出:777, 通过类名来访问子类静态成员 */
    cout << son::father::s_value << endl; /* 输出:888, 通过类名来访问父类的静态成员 */
    return 0;
}

菱形继承(采用虚继承方式进行处理)

在这里插入图片描述
出现上述图示的类1和类2继承自类0,类3继承自类1和类2的情况叫做菱形继承。因为类1和类2都继承自类0,所以类1和类2会有部分是一样的,会导致资源的浪费和定义的不明确,如果想确定定义的话,只能通过类名来区分,如下:

#include <iostream>
using namespace std;

class test0 {
   public:
    test0() {
        value = 1;
    }

    int value;
};

class test1 : public test0 {};
class test2 : public test0 {};
class test3 : public test1, public test2 {};

int main(int argc, const char** argv) {
    test3 t;
    // cout << t.value << endl; /* 报错:因为value在test1和test2中都存在 */
    cout << t.test1::value << endl; /* 输出:1,通过类名来访问 */
    cout << t.test2::value << endl; /* 输出:1,通过类名来访问 */
    return 0;
}

为了处理这种问题,就得采用虚继承的方式来完成继承,采用了虚继承之后,共同的数据就会只有一份,定义就会明确,实现如下:

#include <iostream>
using namespace std;

class test0 {
   public:
    test0() {
        value = 1;
    }

    int value;
};

/* 使用virtual关键字来开启虚继承,开启虚继承之后,value数据就会只有一份了 */
class test1 : virtual public test0 {};
class test2 : virtual public test0 {};
class test3 : public test1, public test2 {};

int main(int argc, const char** argv) {
    test3 t;
    cout << t.value << endl; /* 输出:1,采用了虚继承,value数据只有一份,直接访问可以找到value了 */
    return 0;
}

虚继承说明
开启虚继承之后,将不会直接继承父类的属性,而是采用了vbptr (虚拟基类指针)的方式进行的继承,类1和类2中都会有一个vbptr都会指向各自的vbtable (虚拟基类表)vbtable中记录了类0中的value基于当前位置的偏移量,通过这个偏移量可以找到类0中value。虽然不对但是好记的说法就是虚继承之后,就是记录了一个指针,这个指针指向父类的元素,类1和类2的指针都指向一个地址,所以value就只有一个了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值