继承简介

C++继承语法非常简单,在class声明之后,我们使用冒号,单词public以及我们希望继承的类的名称。这称为公共继承。

#include <iostream>
 
class Base
{
public:
    int m_id;
 
    Base(int id=0)
        : m_id(id)
    {
        std::cout << "Base\n";
    }
 
    int getId() const { return m_id; }
};
 
class Derived: public Base
{
public:
    double m_cost;
 
    Derived(double cost=0.0)
        : m_cost(cost)
    {
        std::cout << "Derived\n";
    }
 
    double getCost() const { return m_cost; }
};

子类会继承父类的所有属性和方法。比如上面示例中Derived也具备id属性。

11.0 构造函数

实例化时候需要初始化里面的参数,初始化参数不需要调用一个专门的函数,C++里面默认了一个跟类同名的函数,可以用来初始化参数。如上面的Base(int id=0)Derived(double cost=0.0)分别是父类和子类的构造函数。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。

实例化某个类时候,很显然就是单纯地在内存里分配地址就行了。但是子类在实例化时候呢?父类的创建顺序如何?
事实上,子类实例化之前会先实例化父类。

class A
{
public:
    A()
    {
        std::cout << "A\n";
    }
};
 
class B: public A
{
public:
    B()
    {
        std::cout << "B\n";
    }
};
 
class C: public B
{
public:
    C()
    {
        std::cout << "C\n";
    }
};
 
class D: public C
{
public:
    D()
    {
        std::cout << "D\n";
    }
};

实例化D时候,会依次实例化A,B,C。

11.1 子类实例化与构造函数

子类初始化时候,父类初始化时怎么对父类的参数进行赋值初始化呢?
思路:因为子类可以继承父类,所以可以在子类的构造函数里对父类的参数进行赋值。
结论:不可行。因为父类的参数有可能是const,无法对常数进行赋值。

C++为了解决这个问题,让我们可以显示地调用父类的构造函数。因为父类本来就要调用构造函数,干脆让使用者可以显示调用,这样就解决了父类参数的赋值问题。

class Derived: public Base
{
public:
    double m_cost;
 
    Derived(double cost=0.0, int id=0)
        : Base{ id }, // Call Base(int) constructor with value id!
            m_cost{ cost }
    {
    }
 
    double getCost() const { return m_cost; }
};

执行顺序:父类的构造函数先执行,子类的构造函数后执行。

如果有>=3层的继承呢?
对于一个子类它只有一个父类,所以子类里只调用它上一级的父类,然后父类在调用它的父类的构造函数。
如下所示:

class A
{
public:
    A(int a)
    {
        std::cout << "A: " << a << '\n';
    }
};
 
class B: public A
{
public:
    B(int a, double b)
    : A{ a }
    {
        std::cout << "B: " << b << '\n';
    }
};
 
class C: public B
{
public:
    C(int a , double b , char c)
    : B{ a, b }
    {
        std::cout << "C: " << c << '\n';
    }
};
 
int main()
{
    C c{ 5, 4.3, 'R' };
 
    return 0;
}

11.2 继承的三种类成员访问说明符:public,private,protected

因为有3种成员访问说明符:公共,私有和受保护。
对应的,子类继承父类也有这三种关系:公共,私有和受保护。

类成员里面的访问说明符含义:

  • public:可以被任意访问
  • protected:能被子类、friends、类本身函数访问
  • private:能被friend、类本身函数访问,不能被子类访问。
class Base
{
public:
    int m_public; // can be accessed by anybody
private:
    int m_private; // can only be accessed by Base members and friends (but not derived classes)
protected:
    int m_protected; // can be accessed by Base members, friends, and derived classes
};
 
class Derived: public Base
{
public:
    Derived()
    {
        m_public = 1; // allowed: can access public base members from derived class
        m_private = 2; // not allowed: can not access private base members from derived class
        m_protected = 3; // allowed: can access protected base members from derived class
    }
};
 
int main()
{
    Base base;
    base.m_public = 1; // allowed: can access public members from outside class
    base.m_private = 2; // not allowed: can not access private members from outside class
    base.m_protected = 3; // not allowed: can not access protected members from outside class
}

这个例子充分说明了,访问说明符对外部调用和派生类的影响。

11.3关于类继承的三种访问说明符:

类继承的访问说明符不会影响派生类对基类的访问,主要是影响外部对类成员的访问。

public继承:

这是用的最多的继承,使用该继承,父类里的说明符的作用范围不会被改变。
公共成员保持公开状态,继承的受保护成员保持受保护状态。由于继承的私有成员在基类中是私有的,因此仍然无法访问。

class Base
{
public:
    int m_public;
private:
    int m_private;
protected:
    int m_protected;
};
 
class Pub: public Base // note: public inheritance
{
    // Public inheritance means:
    // Public inherited members stay public (so m_public is treated as public)
    // Protected inherited members stay protected (so m_protected is treated as protected)
    // Private inherited members stay inaccessible (so m_private is inaccessible)
public:
    Pub()
    {
        m_public = 1; // okay: m_public was inherited as public
        m_private = 2; // not okay: m_private is inaccessible from derived class
        m_protected = 3; // okay: m_protected was inherited as protected
    }
};
 
int main()
{
    // Outside access uses the access specifiers of the class being accessed.
    Base base;
    base.m_public = 1; // okay: m_public is public in Base
    base.m_private = 2; // not okay: m_private is private in Base
    base.m_protected = 3; // not okay: m_protected is protected in Base
 
    Pub pub;
    pub.m_public = 1; // okay: m_public is public in Pub
    pub.m_private = 2; // not okay: m_private is inaccessible in Pub
    pub.m_protected = 3; // not okay: m_protected is protected in Pub
private继承

private继承会让基类里的成员全部变成private,不可访问。

class Base
{
public:
    int m_public;
private:
    int m_private;
protected:
    int m_protected;
};
 
class Pri: private Base // note: private inheritance
{
    // Private inheritance means:
    // Public inherited members become private (so m_public is treated as private)
    // Protected inherited members become private (so m_protected is treated as private)
    // Private inherited members stay inaccessible (so m_private is inaccessible)
public:
    Pri()
    {
        m_public = 1; // okay: m_public is now private in Pri
        m_private = 2; // not okay: derived classes can't access private members in the base class
        m_protected = 3; // okay: m_protected is now private in Pri
    }
};
 
int main()
{
    // Outside access uses the access specifiers of the class being accessed.
    // In this case, the access specifiers of base.
    Base base;
    base.m_public = 1; // okay: m_public is public in Base
    base.m_private = 2; // not okay: m_private is private in Base
    base.m_protected = 3; // not okay: m_protected is protected in Base
 
    Pri pri;
    pri.m_public = 1; // not okay: m_public is now private in Pri
    pri.m_private = 2; // not okay: m_private is inaccessible in Pri
    pri.m_protected = 3; // not okay: m_protected is now private in Pri
protected继承

这种用的非常少。

如果没有关键字,默认的是protected 继承

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐