C++如何写一个C++类

上一篇:C++类与结构体对比

1. 概述

到目前为止,我们学了类,并尝试着从头开始写一个类。本篇也不会讲的太深,我不会写一个复杂的类,我们要写一个基本的log类,演示一下我们已经学过的东西。在后续的章节中,我们会继续关注类,并引入新的概念,所以你会看到从一个基本版本的类到一个做同样事情的更高级版本的类的过程和区别。

让我们来讨论下,我们要写的这个log类,这个log类到底是什么?

这个log类是我们管理日志信息的一种方式。换句话说,我们想要我们的程序打印信息到控制台,这通常对于调试非常有用。在游戏或应用中,如果我们想知道发生了什么,我们只需要将事物的状态打印到控制台,因为应用程序中的控制台就像一个放信息的地方。我们可以用它来打印出发生了什么。这也是几乎可以保证代码在正确工作的东西。如果我们的游戏中有一个图形要显示在控制台,或者是一些不同的东西,它可能不会一直起作用,因为如果我们的图形渲染系统出现了问题,或者其他类似的问题,我们可能得不到那些信息被正确的打印。然而,控制台基本上是内置在操作系统中的东西,所以我们可以几乎保证它总是有效的。我也很喜欢用log类作为例子。因为日志系统可以根据你的需要,简单或复杂都行,有些日志系统非常复杂,我说的是,几千行代码,只是为了把东西打印到控制台。但它对调试和开发非常重要,所以花时间在上面是绝对值得的。log日志系统不仅可以做打印到控制台这样的事情,也可以用不同颜色打印,或是通过网络输出日志信息到一个文件,我们可以做很多事情。我们可以写一个log类,从十行到十万行代码都行。这就是为什么log是一个很好的例子的原因。

log类开始的时候非常简单,它提供向控制台写入文本的能力,并保持某种日志级别。这种日志级别是指我们真正想要发送给控制台的日志信息的级别,开始我们有三个层次,错误,警告,信息或跟踪。如果我们把我们的日志系统级别设置为警告。这意味着只会打印警告和错误,而不会打印跟踪信息。这很有用,如果你不想看到一堆信息,你只想知道哪里出了问题,或者你的警告是什么。同样的,控制台会很清爽,通过过滤实际发送和打印的内容。

2. 案例

1. 项目准备

准备一个简单的项目,项目中有一个main.cpp文件,main.cpp文件中的内容如下

2. 开始案例

先创建一个log类

现在我们思考下,log类是如何工作的,创建一个类或者设计一个API时,一个很好的方法就是通过研究它的使用情况。

下面,我们将进入main函数中,开始输入我们可能会如何使用log类的情况。

首先,我们需要实例化log类

我可能想指定某种日志级别作为一个实际参数,但是这里我先跳过它。我肯定会设置一个log级别,假设log类有一个SetLevel方法,参数LogLevelWarning是指Warning级别,这意味着只有警告或更重要的信息,比如警告或错误,才会被打印出来,但不跟踪信息。

然后我可能想要打印一个警告信息,log.warn(),如下

现在我知道了,我们的log类看起来像什么了,所以我们现在可以回到log类中去填空。

在log类中,我们将创建所有public函数开始。首先,创建一个SetLevel函数,将log级别,也就是level作为函数的整形参数,这样比较简单

然后还需要一个warn函数,它有一个const char*参数,const char*现在就是字符串的意思,后续会有一篇专门和字符串相关的文章,这里先使用cont char*已经够用了,对于本篇的内容没什么坏处。

现在,可以看到,VS还给了一处错误提示,SetLevel,Warn这些函数我们都已经在log类中声明了。然而,LogLevelWarning不存在,声明log级别。

我们在log类中创建一些私有的成员变量,来保存我们的log级别的设定。定义一个整型,名叫m_LogLevel的变量来报讯log级别。按照惯例,我们使用了m_前缀,这告诉我们这是一个私有的类成员变量。

通过这种命名方式,当我在log类的函数内部,如SetLevel函数内部写代码时,我如下引用私有类成员变量。

我就能知道,在类代码中,哪些是成员变量,哪些只是局部变量。当然这样的写法约定也不是必须的,但是他确实对整理你的代码并保持代码干净很有帮助。特别是当我们处理大型代码库和复杂类这样的东西时,这真的很有帮助。所以我建议大家遵循这样的惯例。

在SetLevel函数中,可以看到,它所做的,就是为我们的成员变量赋值一个参数。

回到main函数中,我们调用log对象的SetLevel函数设置日志级别,传入的LogLevelWarning。

让我们开始设置这些日志级别整数参数。当然我们可以设2意思是信息或跟踪,1的意思是警告,0的意思是错误。

如果我们像log.SetLevel(1);这样直接传数字的方式来设置日志级别。这会使得代码读起来困难,1是什么,我不知道它是什么意思,只能靠记忆来记住它代表什么意思,1是警告。我们不想处理这个问题,如果有人写出这样的代码,我们会发现很难理解什么是1, 1是什么?我们需要去看代码来推理1是什么,这会使代码变得很难维护,这也就是魔鬼数字。因此我们要创建一个变量,它的值是1,来表示我们想要的东西。在这种情况下,这个参数名字是LogLevelWarning。

我们在log类中将日志级别变量LogLevelErrorLogLevelWarningLogLevelInfo设为public变量

可以看到,我们在log类中用了两次public,那是因为我喜欢把类中不同的部分分开来写,换句话说,public方法可能在一个部分,然后public变量可以放到另一部分,public静态变量可能会放到另一个块中。这只是一种代码风格,可供参考。这些日志级别都是常数,所以要写const int。然后,我们为警告级别创建一个变量const int LogLevelWarning = 1;,为错误写一个级别const int LogLevelError = 0;,为日志的信息跟踪写一个级别const int LogLevelInfo = 2;。因此,我们有三种类型的日志消息,错误Error,警告Warning,信息Infomation。

在log类中,我们可以设置默认情况下,把我们的日志级别设置为LogLevelInfo,意思是将所有的东西都应该打印出来。

最后,我们可以填充我们的Warn函数。当然我们希望我们的Warn函数能打印东西到控制台。所以我们现在要使用std::cout,然后cout这个message。

我们想做的就是打印出它是什么类型的消息,换句话说,如果这是警告,我们可能想这样打印,在消息前面放一个waring。如果它是info,则在前面放上info,然后打印消息,以此类推。

所以我们加上一些东西,如[WARNING]:

好了,让我们来补充其他日志级别的打印函数

现在,所有这些信息只要函数被调用那么信息就都会一直被打印出来。现在我们没办法做到,如果日志级别设置成了warning,就不要打印所有的info信息了。我们可以通过添加if语句来搞定这个。

所以我们要在日志输出函数中添加判断,判断当前的日志级别是否大于或等于这个特定的消息级别。如果满足就打印。

所以在main函数中,如果我们将日志级别设置为LogLevelWarning,这意味着我们把log级别设置为1。

我们F5运行程序

可以看到警告信息"Hello!"打印出来了。如果我们在这个级别下,试着打印其他级别的信息。

F5运行程序

可以看到只有waring和error的信息被打印出来,info的没有。因为我们现在的日志级别是warning,也就是1,我们看看info打印的函数

m_level是1,而LogLevelInfo是2,所以这个if条件不满足。所以info的信息不会被打印。

如果我们不设定一个日志级别

当然,log会采用它默认的log级别info

这也就意味着我们运行程序,应该所有日志都应该被打印出来

F5运行程序

可以看到,所有的日志都打印出来了。

最后,我们把日志级别设置为error

F5运行程序

可以看到只有error相关的信息被打印出来。

到现在,我们实现了所有目标,我们创建了一个非常基本的log类。

现在我只想说,这绝对不是我写log类的方式,这是相当糟糕的代码,有几个原因,这里先不讲,在后续的篇章中会继续。但是它非常,非常简单,而且很有逻辑。当一个人考虑如何编写这个类时,一个经验丰富的程序员是不会这样写。当然了,这不是好的代码,但它是简单的代码,如果你刚刚开始,希望你能够理解这些。它也给了我一个很好的机会来展示如何使用一些不同的概念来改进这个类,以及为什么这是不好的。所以在后续的篇章中,我们会花点时间来更多的了解C++类,学习更多如何改善代码,使其达到专业的生产级水平。

相关推荐
圆头猫爹1 小时前
第34次CCF-CSP认证第4题,货物调度
c++·算法·动态规划
十五年专注C++开发1 小时前
hiredis: 一个轻量级、高性能的 C 语言 Redis 客户端库
开发语言·数据库·c++·redis·缓存
hi0_61 小时前
03 数组 VS 链表
java·数据结构·c++·笔记·算法·链表
碧海蓝天20222 小时前
C++法则21:避免将#include放在命名空间内部。
开发语言·c++
CodeWithMe2 小时前
【读书笔记】《C++ Software Design》第一章《The Art of Software Design》
开发语言·c++
Tanecious.4 小时前
C++--红黑树
开发语言·c++
tanyongxi666 小时前
C++ Map 和 Set 详解:从原理到实战应用
开发语言·c++
飒飒真编程6 小时前
C++类模板继承部分知识及测试代码
开发语言·c++·算法
救赎小恶魔8 小时前
C++11的整理笔记
c++·笔记
岁忧8 小时前
(LeetCode 面试经典 150 题 ) 209. 长度最小的子数组(双指针)
java·c++·算法·leetcode·面试·go