侯捷系列:c++面向对象高级编程(上)

2023-11-19

文章目录

基于对象的程序设计

不带有指针成员变量的类(以复数类Complex为例)

头文件的结构

头文件complex.h的结构如下,主要有4部分:
在这里插入图片描述

  • 防卫式声明:防止头文件被重复包含
  • 前置声明: 声明头文件中用到的类和函数
  • 类声明: 声明类的函数和变量,部分简单的函数可以在这一部分加以实现
  • 类定义: 实现前面声明的函数

访问级别

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
类的声明内可以交叉定义多个不同级别的访问控制块:

class complex 
{
public:
	// public访问控制块1...
    
private:
    // private访问控制块1...

public:
    // public访问控制块2...

}

函数设计

内联函数

  • 在类声明内定义的函数,自动成为inline函数;
  • 在类声明外定义的函数,需要加上inline关键字才能成为inline函数.

inline只是编程者给编译器的一个建议,在编译时未必会真正被编译为inline函数.因此如果函数足够简单,我们就把它声明为inline就好了

构造函数

与其他语言类似,C++的构造函数也可以有默认实参.C++构造函数的特殊之处在于列表初始化(initialization list).

在这里插入图片描述
上面两种构造函数的效果是一样的,但是使用列表初始化的效率更高,应尽量使用列表初始化.

常量成员函数

若成员函数中不改变成员变量,应加以 const 修饰
在这里插入图片描述

参数的值传递和引用传递

在这里插入图片描述
为了提高效率,使用引用传递参数,避免了参数的复制.若不希望在函数体内对输入参数进行修改,应使用const修饰输入参数
函数的参数应尽量使用引用传递.

返回值的值传递和引用传递

在这里插入图片描述
为提高效率,若函数的返回值是原本就存在的对象,则应以引用形式返回.

若函数的返回值是临时变量,则只能通过值传递返回.

友元

友元函数不受访问级别的控制,可以自由访问对象的所有成员.
在这里插入图片描述
同一类的各个对象互为友元,因此在类定义内可以访问其他对象的私有变量
在这里插入图片描述

complex c1, c2;
c2.func(c1);		// 因为c1和c2互为友元,因此c2可以在func()函数内调用c1的私有变量

操作符重载

在C++中的操作符重载有两种形式:

  • 一种是在类内声明public函数实现操作函重载(这种情况下,操作符是作用在左操作数上的);
  • 另一种是在类外声明全局函数实现操作符重载.

为便于调用该类的用户使用,不同的操作符使用不同的方式进行重载。

在类内声明 public 函数重载 +=

输出参数类型为 complex& ,这是为了支持将多个 += 操作符串联起来.若返回参数类型设为 void ,也支持形如 c2+=c1 的运算,但不支持形如 c3+=c2+=c1 的运算.
在这里插入图片描述
函数体内调用友元函数 __doapl(complex *, const complex &) 实现功能,其第一个参数接收成员函数内隐含的 this 指针,其内容在函数中会被改变;第二个参数接收重载函数的参数,该参数在函数中不会被改变,以 const 修饰.
在这里插入图片描述

在类外声明或函数重载+

考虑到+操作符有三种可能的用法如下:

complex c1(2,1);
complex c2;

c2 = c1 + c2;	// 用法1: complex + complex
c2 = c1 + 5;	// 用法2: complex + double
c2 = 7 + c1;	// 用法3: double + complex

因为重载操作符的成员函数是作用在左操作数上的,若使用类内声明public函数重载操作符的方法,就不能支持第3种用法了.因此使用在类外声明函数重载+运算符.

在这里插入图片描述
这3个函数返回的是局部对象(local object),在退出函数时对象就会被销毁,因此不能使用引用传递返回值.

在类外声明函数重载<<

与重载 + 的考虑方法类似, << 操作符通常的使用方式是 cout<<c1 而非 c1<<cout ,因此不能使用成员函数重载 << 运算符.

虑到形如 cout<<c1<<c2<<c3 的级联用法,重载函数的返回值为 ostream& 而非 void.

在这里插入图片描述

总结:在编写类的时候应该注意的5件事

在编写类的时候应该注意的5件事,通过这5件事可以看出你写的代码是否大气:

  1. 构造函数中使用列表初始化(initialization list)为成员变量赋值.

  2. 常量成员函数使用const修饰.

  3. 参数的传递尽量考虑使用引用传递,若函数体内不改变传入的参数,应加以const修饰.

  4. 返回值若非局部变量,其传递尽量考虑使用引用传递,

  5. 数据放入private中,大部分函数放入public中.

带有指针成员变量的类——以字符串类String为例

在这里插入图片描述
类声明如下,使用指针成员变量m_data管理String类中的字符串数据.
在这里插入图片描述

3个特殊函数:拷贝构造函数、拷贝赋值函数和析构函数

对于不带有指针的类,这3个函数可以使用编译器默认为我们生成的版本;但是编写带有指针的类时就有必要定义这3个特殊函数.

构造函数和析构函数

构造函数和析构函数中执行数据的深拷贝和释放.
在这里插入图片描述

值得注意的是使用delete[]操作符释放数组内存,若直接使用delete操作符释放数组内存虽然能够通过编译,但有可能产生内存泄漏.

拷贝构造函数和拷贝赋值函数

注意拷贝3调用的是拷贝构造函数。

String s1 = "hello";
String s2(s1);      // 拷贝1: 调用拷贝构造函数
String s3;
s3 = s1;            // 拷贝2: 调用拷贝赋值函数
String s4 = s1;     // 拷贝3: 调用拷贝构造函数

拷贝构造函数的实现较为简单,直接调用友元对象的数据指针进行拷贝即可.

在这里插入图片描述
拷贝赋值函数中要检测自我赋值,这不仅是为了效率考虑,也是为了防止出现bug.

在这里插入图片描述
若在拷贝赋值函数中不检测自我赋值,在第2步中会出现bug.

堆栈与内存管理

https://blog.csdn.net/jialong_chen/article/details/115421050

new和delete过程中的内存分配

  • new操作先分配内存,再调用构造函数.
    在这里插入图片描述
  • delete操作先调用析构函数,再释放内存.

在这里插入图片描述
VC中对象在debug模式和release模式下的内存分布如下图所示,变量在内存中所占字节数必须被补齐为16的倍数,红色代表cookie保存内存块的大小,其最低位的1和0分别表示内存是否被回收.

Complex对象 String对象
在这里插入图片描述 在这里插入图片描述

数组中的元素是连续的,数组头部4个字节记录了数组长度:

Complex对象 String对象
在这里插入图片描述 在这里插入图片描述

根据数组在内存中的状态,自然可以理解为什么 new[] 和 delete[] 应该配对使用了: delete 操作符仅会调用一次析构函数,而 delete[] 操作符依次对每个元素调用析构函数.对于 String 这样带有指针的类,若将 delete[] 误用为 delete 会引起内存泄漏.
在这里插入图片描述

static成员

https://zhuanlan.zhihu.com/p/37439983

静态成员变量:类的所有对象共有。
静态成员函数:只能访问静态成员变量和静态成员函数。

面向对象(Object Oriented)的程序设计——类之间的关系

类之间的关系有复合(composition)、委托(aggregation)和继承(extension)3种.

类之间的关系

复合(composition)

复合表示一种has-a的关系,STL中queue的实现就使用了复合关系.这种结构也被称为adapter模式.
在这里插入图片描述
复合关系下构造由内而外,析构由外而内:
在这里插入图片描述

委托(aggregation;composition by reference)

在这里插入图片描述
使用类指针。

继承(extension)

继承表示一种is-a的关系,STL中_List_node的实现就使用了继承关系.
在这里插入图片描述
继承关系下构造由内而外,析构由外而内:
在这里插入图片描述

虚函数

成员函数有3种: 非虚函数、虚函数和纯虚函数

  • 非虚函數(non-virtual function): 不希望子类重新定义(override)的函数.

  • 虚函數(virtual function): 子类可以重新定义(override)的函数,且有默认定义.

  • 纯虚函數(pure virtual function): 子类必须重新定义(override)的函数,没有默认定义.
    在这里插入图片描述
    使用虚函数实现框架: 框架的作者想要实现一般的文件处理类,由框架的使用者定义具体的文件处理过程,则可以用虚函数来实现.

面向对象设计范例

使用委托+继承实现Observer模式

在这里插入图片描述
在这里插入图片描述

使用委托+继承实现Composite模式

使用Composite模式实现多态,类结构图如下
在这里插入图片描述

使用委托+继承实现Prototype模式

在这里插入图片描述

参考

https://blog.csdn.net/ncepu_Chen/article/details/113843775

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

侯捷系列:c++面向对象高级编程(上) 的相关文章

随机推荐

  • 异步加载vue组件

    什么时候使用 组件较大 或者不是必用的 通常组件在script标签对中导入 而异步组件在component中使用 例如 components ForData gt import view Fordata
  • J2EE集合框架

    1 UML 二 集合的基本特点 list集合的特点 增删改查 有序 可重复 三 List集合的三种遍历方式 for foreath iterator 四 ArrayList LinkedList 的比较与分析 比较 1 ArrayList
  • 基于Matlab的灰狼算法优化LSTM风电功率预测

    基于Matlab的灰狼算法优化LSTM风电功率预测 随着可再生能源的快速发展 风能作为一种重要的清洁能源形式变得越来越受关注 风电功率预测在风电场的运营和调度中起着关键作用 然而 由于风速的不稳定性和不确定性 精确地预测风电功率仍然具有一定
  • SpringCloud Gateway:status: 503 error: Service Unavailable

    使用SpringCloud Gateway路由请求时 出现如下错误 yml配置如下 可能的一种原因是 yml配置了gateway discovery locator enabled true 此时gateway会使用负载均衡模式路由请求 但
  • Lim接口测试平台-接口测试功能详解

    一 接口测试 项目地址 Gitee Github 接口测试模块是整个Lim平台的核心 左侧是接口的模块树 右侧顶部是用例操作功能区 列表展示接口用例信息 文章目录 一 接口测试 二 维护接口用例 各步骤类型详解 1 执行步骤 1 接口步骤
  • Unity --- 触摸方法,以及灯光与烘培的使用

    触摸方法 1 首先触摸分为两大类 多点触摸和单点触摸 这两种方式的触摸通过下面这个触摸数来进行判断 当其等于1的时候 为单点触摸 当其大于1的时候为多点触摸 2 当我们在调用触摸方法的时候我们首先需要打开对应的多点 单点触摸 上面这个是开启
  • QObject的d_ptr成员——箭头符号的重载

    QObject中的d ptr是这样定义的 QScopedPointer
  • vscode cmake 编译32位程序

    vscode cmake 编译32位程序 为什么要用cmake vscode中的C C 插件直接支持的只是最简单的单文件编译 运行和调试 要管理大的项目 或者生成库 C C 插件不能直接支持 需要开发者利用vscode的task功能 结合脚
  • 【由浅入深】爬虫技术,值得收藏,来了解一下~

    爬虫技术 来了解一下 一 为什么需要爬虫技术 现在的互联网来说 包含着各种海量的信息 无孔不入 包罗万象 出于数据分析或产品需求 我们需要从某些网站 提取出我们感兴趣 有价值的内容 我们需要一种能自动获取网页内容并可以按照指定规则提取相应内
  • 关于两数交换的两种方法

    目录 前言 一 引入变量 这个方法也是最常用的方法 二 通过使用数学的方法相加或者相减从而得到两数运算 这种方法不常见 总结 前言 从键盘输入两个整数 并交换两位数字 这里小编用两种方法告诉大家 注意小编这里用的是VS2019 所以在代码的
  • STM32 --通用定时器输入捕获功能

    问题 开始的时候没有搞清楚 定时器时基 于 定时器溢出中断的概念 导致在计算频率的时候一直有问题 开始并没有怀疑是配置有问题 因为之前接触过定时器输入捕获功能 靠着自己的记忆配置了一下 认为 捕获功能 的定时是通过定时器设置的定时溢出频率来
  • 栈实现队列(继续细起来啊)

    生命不是要等待风暴过去 而是要学会在风暴中跳舞 卡莉尔 吉布朗目录 一 栈实现队列 二 使用两个栈实现队列的功能 1 在队列的结构体中创建两个栈 2 创建一个队列的结构体指针 3 myQueuePush入队列操作 4 myQueuePeek
  • SpringBoot项目实战(一)

    SpringBoot实战之系统架构 1 系统介绍 该实战项目 是一个B2C模式的职业技能在线教育系统 分为前台用户系统和后台运营平台 前台用户系统包括课程 问答 文章三大部分 后台运营平台包括会员管理 讲师管理 课程管理 文章资讯 统计分析
  • Gradle入门(二)尝试理解gralde编译项目

    前言 前面我们了解了如何通过groovy DSL转换为KTS 我也在尝试的证明可以看到源码和有代码提示对于入门的重要性 2022年11月12日 我发现最新的idea 有gradle的代码提示 点击也可以看到源码 学习Gradle还是建议整一
  • .NET框架和发展历史介绍

    NET框架知识 NET 框架是由微软开发的软件开发平台 其最主要的两个组成部分是公共语言运行时 CLR 和框架类库 FCL 基础类库 BCL 是框架类库的一个子集 NET框架的主要结构如下图所示 1 操作系统 最下层的无疑就是操作系统了 2
  • Linux内核之pid为0和pid为1【转】

    转自 https blog csdn net jingyilin2008 article details 7815508 ops request misc 257B 2522request 255Fid 2522 253A 25221592
  • 【Linux】Mint20.3系统安装Anaconda环境

    Anaconda是非常方便的python开发IDE环境 其中不仅包含了很多常用python库还有Spyder运行环境 Mint系统是近些年非常受欢迎的linux系统 易上手已操作特性使其普及非常快 本篇介绍在Mint20 3系统安装Anac
  • haclcon实现图像处理的傅里叶变换

    dev open file dialog read image default default Selection read image Image Selection mean image Image ImageMean 9 9 gaus
  • 多柱汉诺塔(Matrix上选做题)——递归与动态规划

    分析 对于三柱汉诺塔问题 我们已经熟知步骤数最优解为 2 i 1 2 i 1 2i 1 其中 i 为盘子个数 对于四柱以上的问题 我们将柱子分为三类 起点柱Start 辅助柱Buf
  • 侯捷系列:c++面向对象高级编程(上)

    文章目录 基于对象的程序设计 不带有指针成员变量的类 以复数类 Complex 为例 头文件的结构 访问级别 函数设计 内联函数 构造函数 常量成员函数 参数的值传递和引用传递 返回值的值传递和引用传递 友元 操作符重载 在类内声明 pub