C++ 基础与深度分析 Chapter12 类的细节(类的继承)

概述

在这里插入图片描述
通过类的继承(派生)来引入 是一个 的关系
被继承的类——基类,继承的类——继承类。

#include <iostream>
using namespace std;

struct Base
{
};

struct Base2 : Base
{
};


// 这是类的声明,下面继承部分不是类的声明
struct Drive;

// Drive派生自Base基类
// struct不写public,默认也是public继承
// 但是如果是class,那么默认是private继承,所以class经常看到public继承
struct Drive : public Base2
{

};

int main()
{
    Drive d;
    // 使用基类的指针或引用可以指向派生类对象
    // Base2继承自Base,Drive继承自Base2,还是可以的
    Base& ref = d;
    Base* ptr = &d;
}

使用基类的指针或引用可以指向派生类对象
在这里插入图片描述
静态类型 V.S. 动态类型
静态类型:编译器就可以确定的类型。
动态类型:在运行期为ref或者指针赋予的实际的类型。
在这里插入图片描述

#include <iostream>
using namespace std;

struct Base
{
    void fun() {}
};

struct Base2 : public Base
{
};

struct Drive : public Base2
{
    void fun2() {}
};

struct Drive2 : public Base2
{
};

int main()
{
    Drive d;
    Base& ref = d;
    ref.fun(); // 编译器只看静态类型是否可以接收
    // ref.fun2(); // fun2是Drive的函数,是ref的动态类型,ref不能调用动态类型的函数
    Drive d2;
    Base* ptr = &d;
    ptr = &d2; // ptr对应的静态类型没有发生改变,还是Base。但是动态类型从Drive到Drive2了
}

protected 限定符:派生类可访问

#include <iostream>
using namespace std;

struct Base
{
protected: // 如果是protected,那么Derive的fun2,就可以调用了
    void fun() {}
    int x;
};


struct Derive : public Base
{
public:
    void fun2() 
    {
        fun(); // fun如果是private,无法访问
        x; // 因为proteted,所以可以访问fun和x
    }
};


int main()
{
    Derive d;
    Base b;
    // b.fun(); // fun private不行
    d.fun2();
    // b.fun2(); // b不能访问Derive的函数
}

类的派生会形成嵌套域

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
派生类中的名称定义会覆盖基类

#include <iostream>
using namespace std;

struct Base
{
    int val = 2;
};


struct Derive : public Base
{
public:
    void fun2() 
    {
        cout <<  val << endl;
    }
    // int val = 3;
};


int main()
{
    Derive d;
    Base b;
    d.fun2(); // 3 因为Derive里的val覆盖了Base的val
    // 根本原因是Derive是嵌套在Base里的

    cout << d.val << endl; // 如果把Derive的val注释掉,那么会去找Base的val。因为他们是嵌套的关系
}

使用域操作符显式访问基类成员

#include <iostream>
using namespace std;

struct Base
{
    int val = 2;
};


struct Derive : public Base
{
public:
    void fun2() 
    {
        cout <<  val << endl;
        cout <<  Base::val << endl; // 域操作符
    }
    int val = 3;
};


int main()
{
    Derive d;
    Base b;
    d.fun2(); // 3 因为Derive里的val覆盖了Base的val

}

在派生类中调用基类的构造函数

#include <iostream>
using namespace std;

struct Base
{
    Base ()
    {
        cout << "Base constructor is called" << endl;
    }
};


struct Derive : public Base
{
public:
    Derive ()
    {
        cout << "Derive constructor is called" << endl;
    }
};


int main()
{
    Derive d; // Base构造函数先调用,再调用Derive构造函数

}
#include <iostream>
using namespace std;

struct Base
{
    Base (int)
    {
        cout << "Base constructor is called" << endl;
    }
};


struct Derive : public Base
{
public:
    Derive (int a)
        : Base(a) // 通过初始化列表来去调用基类Base的构造函数
    {
        cout << "Derive constructor is called" << endl;
    }
};


int main()
{
    Derive d(100); // Base构造函数先调用,再调用Derive构造函数

}

虚函数

在这里插入图片描述
通过虚函数与引用(指针)实现动态绑定
使用关键字 virtual 引入
非静态、非构造函数可声明为虚函数
虚函数会引入vtable结构
在这里插入图片描述

#include <iostream>
using namespace std;

struct Base
{
    virtual void baseMethod() {} // 虚函数
    int baseMember;

};


struct myClassDerive : public Base
{
    virtual void deriveMethod() {} // 虚函数
    int deriveMember;
};

struct myClassDerive2 : public myClassDerive
{
    virtual void deriveMethod2() {} // 虚函数
    int deriveMember2;
};

int main()
{
    myClassDerive2 d;
    Base& b = d;
    Base* ptr = &d;
    myClassDerive2& d2 = dynamic_cast<myClassDerive2&>(b);
    myClassDerive2* ptr2 = dynamic_cast<myClassDerive2*>(ptr);
}

如果我们想把一个基类的指针转成一个派生类的指针,如果基类的指针确实指向派生类的地址,那么这个转换就是成功的,否则指向空指针。基类的引用转换成派生类的引用,如果基类引用确实绑定在派生类的引用,就是成功的,否则抛出异常。

static_cast是在编译期处理的,但是dynamic_cast是在运行期发生的,判断动态类型能不能发生转变。如果要编写高性能的程序,dynamic_cast慎用。

#include <iostream>
using namespace std;

struct Base
{
    virtual void fun() 
    {
        cout << "Base :fun() is called" << endl;
    } 

};

struct Derive : Base
{
    void fun()
    {
        cout << "Derive :fun() is called" << endl;
    }
};

int main()
{
    Derive d;
    d.fun();
    Base& b = d;
    b.fun(); // 注意,使用虚函数,那么就会调用Derive的fun,但是如果没有使用虚函数,就会使用Base里的fun
    // 没有了虚函数,就没有个vtable。所以对象的绑定都会在编译期确定,而编译期只看静态类型,b就是Base,所以只能调用Base中的fun
}

虚函数在派生类中的重写( override )

#include <iostream>
using namespace std;

struct Base
{
    virtual void fun() 
    {
        cout << "Base :fun() is called" << endl;
    } 

};

struct Derive : Base
{
    // override 重写
    void fun()
    {
        cout << "Derive :fun() is called" << endl;
    }
};

void proc(Base& b)
{
    b.fun();
}

int main()
{
    // 动态类型实现的运行期的一个多态,也叫运行期多态
    Derive d;
    proc(d); // 这个参数的动态类型是derive,所以调用Derive的fun

    Base b;
    proc(b); // 这个参数动态、静态类型都是Base,所以调用Base的fun
}

函数签名保持不变(返回类型可以是原始返回指针 / 引用类型的派生指针 / 引用类型)

#include <iostream>
using namespace std;

struct Base2 {};

struct Derive2 : Base2 {};

struct Base
{
    virtual Base2& fun() 
    {
        cout << "Base :fun() is called" << endl;
        static Base2 b;
        return b;
    } 

};

struct Derive : Base
{
    // override 重写
    Derive2& fun()
    {
        cout << "Derive :fun() is called" << endl;
        static Derive2 inst;
        return inst;
    }
};

void proc(Base& b)
{
    b.fun();
}

int main()
{
    // 动态类型实现的运行期的一个多态,也叫运行期多态
    Derive d;
    proc(d); // 这个参数的动态类型是derive,所以调用Derive的fun

    Base b;
    proc(b); // 这个参数动态、静态类型都是Base,所以调用Base的fun
}

可以通过 = 0 声明纯虚函数,相应地构造抽象基类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
虚函数特性保持不变
在这里插入图片描述
override 关键字
在这里插入图片描述

继承与特殊成员函数

补充知识

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值