类的继承

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

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

// 基类头文件 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. 利用Spire.Pdf实现PDF添加印章的操作

    在一些文档处理中,我们需要对PDF盖上公司的印章操作,本篇随笔介绍利用Spire.Pdf实现PDF添加印章的操作,如全章和骑缝章的处理. 1.实现效果和处理代码 有时候,需要在特定的位置盖章,以及各个 ...

  2. Python日志模块:实战应用与最佳实践

    本文详细解析了Python的logging模块,从基本介绍到实际应用和最佳实践.我们通过具体的代码示例解释了如何高效地使用这个模块进行日志记录,以及如何避免常见的陷阱,旨在帮助读者更好地掌握这个强大的 ...

  3. 初始elasticSearch

    elasticSearch 大致印象 为什么用? mysql更擅长于crud等操作,当一张表达到百万级别时,检索速度过慢 es检索速度快 基本概念 Index索引(两层意思) 动词:类似mysql的i ...

  4. 数据分析师如何用SQL解决业务问题?

    本文来自问答. 提问:数据分析人员需要掌握sql到什么程度? 请问做一名数据分析人员,在sql方面需要掌握到什么程度呢?会增删改查就可以了吗?还是说关于开发的内容也要会?不同阶段会有不同的要求吗? 正 ...

  5. 超详细的mysql总结(DQL)

    上一篇文章总结了 DDL.DML的使用,这一篇文章把剩下的 DQL 加上~   DQL(Data Query Language)即数据库查询语言,用来查询所需要的信息,在查询的过程中,需要判断所查询的 ...

  6. [超详细] GraalVM打包含有JNI的本地镜像

    GraalVM 是一种高性能.多语言通用虚拟机和编译器技术.它由 Oracle 开发并开源,旨在为不同的编程语言和应用场景提供统一的运行时环境和编译器平台.以下是 GraalVM 的一些主要特点和功能 ...

  7. MySql Workbench 迁移工具 migration 提示缺少pyodbc 2.1.8 的解决方法

    想把公司的数据库转到MySQL,所以想装个MySQL测试,发现新版的MySQL(8.0.34)默认安装还是有不少问题, 一.譬如表.字段大小写的问题: lower_case_table_names=0 ...

  8. Pandas 使用教程 CSV

    CSV(Comma-Separated Values,逗号分隔值,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本). CSV 是一种通用的.相对简单的文 ...

  9. 《CTFshow-Web入门》06. Web 51~60

    @ 目录 web51 题解 web52 题解 原理 web53 题解 原理 web54 题解 原理 web55 题解 原理 web56 题解 原理 web57 题解 原理 web58 题解 原理 we ...

  10. Graphviz入门

    Graphviz可以用于状态机图的绘制 要绘制一张状态图,我们需要两个图形元素 结点,边 结点和边都有自己的属性 结点可以是圆.矩形.填充 边有粗细