类的继承

基类与派生类之间的构造行为

派生类可以调用基类的公共成员,但无法调用基类的私有成员。所以派生类无法直接初始化基类中的私有成员变量。在派生类中初始化基类的私有成员变量需要显式的调用基类的构造函数,并以委托构造的方式将其初始化。

// 基类头文件 basic.h
#ifndef __BASIC__
#define __BASIC__ class Basic
{
private:
int basic_pri_val;
void basicPriFuc() const;
public:
Basic(int _pri_val, int _pub_val):basic_pri_val(_pri_val), basic_pub_val(_pub_val){}
int basic_pub_val;
void basicPubFuc() const;
void showBasicPriVal() const;
};
#endif
// 基类源文件 basic.cpp
#include <iostream>
#include "basic.h"
using std::cout;
void Basic::basicPriFuc() const
{
cout<< "Basic private function.\n";
} void Basic::basicPubFuc() const
{
cout<< "Baisc public function.\n";
} void Basic::showBasicPriVal() const
{
cout<< "Basic private value: "<< basic_pri_val<< '\n';
}
// 派生类头文件 derive.h
#ifndef __DERIVE__
#define __DERIVE__ #include "basic.h" class Derive:public Basic // 从 Basic 类派生
{
public:
Derive(int _basic_pri_val, int _basic_pub_val):Basic(_basic_pri_val, _basic_pub_val){} // 委托基类构造函数初始化基类成员变量
}; #endif
// 派生类源文件 derive.cpp
#include "derive.h"
// 主程序源文件 main.cpp
#include "derive.h" int main()
{
Derive derive = {1, 2}; // derive.basicPriFuc(); // 不可访问基类私有成员函数
derive.basicPubFuc(); // 可以访问基类公共成员函数
// derive.basic_pri_val; // 不可访问基类私有成员变量
derive.showBasicPriVal(); // 基类私有成员变量可以通过基类公共成员函数访问
derive.basic_pub_val; // 可以访问基类公共成员变量 return 0;
}

Baisc public function.

Basic private value: 1

另外,基类的引用或指针可以指向派生类,但这样的引用和指针只能调用来自于基类的方法。

在派生类中使用基类方法

在派生类中使用基类方法只需要在方法前加上域解析运算符,如下:

// 假定类 Basic 中已经定义了 myPrint 方法
class Derive:: public Basic
{
public:
void myPrint()
{
Basic::myPrint(); // 调用类 Basic 中的 myPrint 方法
myPrint(); // 调用类 Derive 中的 myPrint 方法
}
}

protected 的访问权限

对于类外而言,protected 的访问权限与 private 相似。对于派生类而言, protected 的访问权限与 public 相似。

多态公有继承

在进行本节之前需要知道:父类的指针/引用可以指向/引用派生类,但只能调用继承于父类的方法或重写在子类中的虚方法。

多态有静态多态和动态多态两个部分。

  • 静态多态相当于在派生类中重写了(不是重载)父类中的函数,函数的声明中,形参类型和数量可以与父类不一致。派生类只能调用重写后的函数,而不能够再使用被重写的父类中的函数。
  • 动态多态发生在使用父类指针或引用,调用或重写修函数时。下面将进行详细的演示。

关键字 virtual

  • 只有类的非静态成员函数可以成为虚函数,其他非类成员函数或类的静态成员函数不行。
  • 类中的虚函数,即使不被调用也必须被定义。而非虚函数在不被调用的情况下可以只声明不定义。
  • 派生类与父类中的虚函数声明必须完全一致,包括函数名与形参类型。
  • virtual 还可用于类的虚继承。

使用 virtual 修饰类的成员函数,其成员函数的调用行为只在下列情况下不同:

  • 对于一般的成员函数,如果使用类的指针或者引用来调用该函数,则函数的行为将取决于声明该指针/引用的类型。即如果是父类类型的的指针/引用,则将调用父类中的函数;如果是派生类类型的指针/引用,则将调用派生类中的函数。
  • 对于虚成员函数,如果使用类的指针或引用来调用该函数,则函数的行为取决于指针所指向的对象的类型。

示例

// 基类头文件 basic.h
#ifndef __BASIC__
#define __BASIC__ class Basic
{
private:
int basic_pri_val;
void basicPriFuc() const;
public:
Basic(int _pri_val, int _pub_val):basic_pri_val(_pri_val), basic_pub_val(_pub_val){}
// virtual ~Basic() {}; // 在包含虚函数时,应当包含一个虚析构函数
int basic_pub_val;
void basicPubFuc() const;
void showBasicPriVal() const; void myPrint() const; // 非虚函数,下面将在派生类中重写
};
#endif
// 基类源文件 basic.cpp
#include <iostream>
#include "basic.h"
using std::cout;
void Basic::basicPriFuc() const
{
cout<< "Basic private function.\n";
} void Basic::basicPubFuc() const
{
cout<< "Baisc public function.\n";
} void Basic::showBasicPriVal() const
{
cout<< "Basic private value: "<< basic_pri_val<< '\n';
} void Basic::myPrint() const
{
cout<< "Basic class print.\n";
}
// 派生类头文件 derive.h
#ifndef __DERIVE__
#define __DERIVE__ #include "basic.h" class Derive:public Basic // 从 Basic 类派生
{
public:
Derive(int _basic_pri_val, int _basic_pub_val):Basic(_basic_pri_val, _basic_pub_val){} // 委托基类构造函数初始化基类成员变量 void myPrint(); // 重写父类中的非虚函数 }; #endif
// 派生类源文件 derive.cpp
#include "derive.h"
using std::cout;
void Derive::myPrint() const
{
cout<< "Derive class print.\n";
}
// 主程序源文件 main.cpp
#include "derive.h" int main()
{
Derive derive = {1, 2};
Basic & basic = derive;
basic.myPrint(); return 0;
}

Basic class print.

当重写父类中的普通函数时,即使父类引用引用了派生类,但是该引用调用的却是父类中的函数。myPrint 函数的行为却决于引用类型。

如果我们在声明中为 myPrint() 函数加上 virtual 修饰,则程序结果为:

Derive class print

可以看到,myPrint() 函数的行为取决于引用所指向的类。

如果需要在派生类中重定义基类中的方法,则应尽量将该方法声明为虚。且需要在基类中声明一个虚析构函数。

当类被用作基类时,它的虚构函数应当被设置为虚函数,否则将会对派生类对象的内存释放造成麻烦。

抽象基类(ABC)

类中包含纯虚函数的类被称为抽象基类。纯虚函数的声明如下:

class ABC
{
public:
virtual void myPrint() const = 0;
}

纯虚函数可以在抽象基类中被定义。抽象基类的派生类必须使用重写虚函数的方法来重写纯虚函数。抽象基类强制派生类实现其纯虚函数。

私有继承和保护继承

  • 在私有继承中,基类的公有成员和保护成员都将成为派生类的私有成员。
  • 在保护继承中,基类的公有成员和保护成员都将成为派生类的保护成员。

多重继承

class Deriver : public Basic1, private Basic2 {···};

C++基础杂记(3)的更多相关文章

  1. java复习(2)---java基础杂记

    java命名规范: 参考:http://www.cnblogs.com/maowang1991/archive/2013/06/29/3162366.html 1.项目名小写 2.包名小写 3.类名每 ...

  2. Webpack系列-第一篇基础杂记

    前言 公司的前端项目基本都是用Webpack来做工程化的,而Webpack虽然只是一个工具,但内部涉及到非常多的知识,之前一直靠CV来解决问题,之知其然不知其所以然,希望这次能整理一下相关的知识点. ...

  3. python基础杂记

    一.编码 1.ACSII                        0000 0001           8位       一个字节 2. uncoide                     ...

  4. .Net基础杂记

    1.面向对象程序思想 面向对象是程序开发的一种机制,特征为封装.继承.多态.以面向对象方式编写程序时,将复杂的项目抽象为多个对象互相协作的模型,然后编写模型结构,声明或实现类型的成员,即描述对象的特征 ...

  5. Webpack系列-第三篇流程杂记

    系列文章 Webpack系列-第一篇基础杂记 Webpack系列-第二篇插件机制杂记 Webpack系列-第三篇流程杂记 前言 本文章个人理解, 只是为了理清webpack流程, 没有关注内部过多细节 ...

  6. webpack-插件机制杂记

    系列文章 Webpack系列-第一篇基础杂记 webpack系列-插件机制杂记 前言 webpack本身并不难,他所完成的各种复杂炫酷的功能都依赖于他的插件机制.或许我们在日常的开发需求中并不需要自己 ...

  7. 5天揭秘js高级技术-第一天

    一.基础杂记 1. document.write() <script type="text/javascript"> document.write('<h2> ...

  8. 面试基础知识集合(python、计算机网络、操作系统、数据结构、数据库等杂记)

    python python _.__.__xx__之间的差别 python中range.xrange和randrange的区别 python中 =.copy.deepcopy的差别 python 继承 ...

  9. java基础(杂记)

    java基础夯实(杂记):1:创建实例对象可以通过无参的构造函数然后调用成员变量去初始化属性,也可以自己定义有参构造方法直接初始化属性,当属性为private时我们可以通过getset方法间接访问:2 ...

  10. elasticsearch基础知识杂记

    日常工作中用到的ES相关基础知识和总结.不足之处请指正,会持续更新. 1.集群的健康状况为 yellow 则表示全部主分片都正常运行(集群可以正常服务所有请求),但是 副本 分片没有全部处在正常状态. ...

随机推荐

  1. C#中DataTable的一些使用(后续继续补充)

    C#中DataTable的一些使用 新建一个DataTable DataTable table = new DataTable(); table.Columns.Add("姓名", ...

  2. Unity UGUI的PhysicsRaycaster (物理射线检测)组件的介绍及使用

    Unity UGUI的PhysicsRaycaster (物理射线检测)组件的介绍及使用 1. 什么是PhysicsRaycaster组件? PhysicsRaycaster是Unity UGUI中的 ...

  3. 利用python分析pdf数据,分析上市公司财报

    import re import os.path import matplotlib import matplotlib.pyplot as plt from pdfminer.pdfparser i ...

  4. AcWing 4798. 打怪兽题解

    可以从 \(1\) 枚举到 \(n\) 表示要打多少个怪兽. 因为你要打 \(t\) 个怪兽,并不管顺序,所以我们可以对 \([1, t]\) 这一段进行排序,然后计算 \(a[t], a[t - 2 ...

  5. SpringBoot3.x原生镜像-Native Image实践

    前提 之前曾经写过一篇<SpringBoot3.x 原生镜像-Native Image 尝鲜>,当时SpringBoot处于3.0.0-M5版本,功能尚未稳定.这次会基于SpringBoo ...

  6. U268603 I Hate This Tree 题解

    传送门 一道纯粹的码力 + 卡常题. 前置 矩阵乘法,线段树. 分析 线段树存矩阵. 构造迭代矩阵: \[\begin{pmatrix}f_i&f_{i-1}\end{pmatrix}\tim ...

  7. PIP升级+安装Django命令[WARNING: Retrying (Retry(total=4, connect=None...]

    升级: >pip install -U Django 安装: >pip install Django 如果发现超时错误内容:(WARNING: Retrying (Retry(total= ...

  8. 日志开源组件(六)Adaptive Sampling 自适应采样

    业务背景 有时候日志的信息比较多,怎么样才可以让系统做到自适应采样呢? 拓展阅读 日志开源组件(一)java 注解结合 spring aop 实现自动输出日志 日志开源组件(二)java 注解结合 s ...

  9. Flutter系列文章-Flutter在实际业务中的应用

    不同场景下的解决方案 1. 跨平台开发: 在移动应用开发中,面对不同的平台(iOS和Android),我们通常需要编写两套不同的代码.而Flutter通过一套代码可以构建适用于多个平台的应用,大大提高 ...

  10. WPF学习 - 动画基础(1)

    1. WPF中的动画(Animation),是一种属性动画.技术上来说,它是让属性从一个值,变化到另一个值的过程.因此,有两条重要的特性: 1.1 只能为依赖属性应用动画(因为第二条特性). 1.2 ...