C++Json项目笔记

Github源项目地址:TinyJson

本人仓库地址(跟原版差别不大,只是在有疑惑或者有收获的地方加上的注释作为笔记)

文章目录

    • 类的提前声明
    • 为什么定义函数的时候同时写左值和右值作为传参?
      • 解答
    • SFINAE机制(疑惑)
    • 指针类型可以被隐式转换成bool类型
      • 能够隐式转换成bool类型的数据
    • 为什么要同时有number_value和int_value?(疑惑)
    • 什么时候成员函数需要被声明为静态的?(疑惑)
    • size_t和std::string::size_type还是有区别
    • 为什么JsonValue的析构函数不是纯虚函数
    • snprinf()
    • Json数据中,”"“要进行处理
    • 构造函数非公有(public)(疑惑)
      • 解答:装饰类,装饰模式
    • 为什么右值在传值的时候还需要使用std::move(疑惑)
    • 数据一致性的实现
    • 匿名命名空间的作用

类的提前声明

我觉得这个比较简单,很好理解,因为在C语言中也有类似的语法:

int Temp(struct Exmp& value);

其中为什么要加上这个struct?就是为了告诉告诉编译器:我这个类是存在的,只是它不在这个文件中,你先别报错。

为什么定义函数的时候同时写左值和右值作为传参?

代码如下:

Json(const object& values);
Json(object&& values);

右值不是可以用于初始化const左值引用嘛?为什么还需要单独写一个右值引用版本?

解答

其实可以不写,因为右值可以转换成const左值引用,但是这会涉及到资源的所有权转移(这里我也不是很了解,后面再补上吧),当我们左值、右值情况都有相对应的函数进行处理的时候,一旦传入是个右值,就优先触发移动拷贝构造调用,使用移动语意,转移资源,减少拷贝

SFINAE机制(疑惑)

什么是SFINAE机制呢?这是一个缩写,展开来即:Substitution Failure Is Not An Error(替换失败不是错误),SFINAE是std::enable_if的模板别名。

[!ChatGPT]
在 C++ 模板编程中,SFINAE 是一种编译技术,它允许编译器在模板参数推导或重载解析时忽略某些不符合条件的实例化选项,而不会导致编译错误。

代码如下:

template<class T, class = decltype(&T::to_json)>
Json(const T& t) : Json(t.to_json()) {}

首先decltype应该是见过的,和auto一样用于自动类型推断,class = dacltype(&T::to_json)就是想在类型T中找到那么个函数to_json,如果没找到,就应该换用别的构造函数,而应该报错;也就是说,模板中的第二个参数其实是对类型T的一个限定。

接下来我们再说说Json(t.to_json),由于对模板元编程不了解,一开始以为就是C++的显式初始化列表,但是想起来没有名为Json的成员变量,所以它的作用应该是:
t.to_json的返回值用于Json构造函数,但是具体是使用哪个重载函数就不一定了,看to_json的结果是什么。

这只是其中一段比较好理解的,还有根本看不懂的,等学了模板元编程再回来补吧:

        template<class M, typename std::enable_if<std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,int>::type = 0>Json(const M& m) : Json(object(m.begin(), m.end())) {}

指针类型可以被隐式转换成bool类型

指针可以被隐式转换成bool类型,这其实也好理解:

若是指针指向的是nullptr,则对应的bool类型会是0;
否则就是1。

在Json中,有NULL这个类型,并且有对应的Json构造函数,因此我们要避免出现异常,就需要禁用这种可能导致异常的隐式转换:

Json(void*) = delete;

能够隐式转换成bool类型的数据

  1. 数值类型。这种情况是最常见的吧,所有的数字0为false,非零值都被认为是1,即为true
  2. 指针类型。此处就属于这个情况,就补过多赘述了
  3. 指针和数值类型的比较。应该也挺好理解吧

为什么要同时有number_value和int_value?(疑惑)

其中有两个获取NUMBER类型的值的函数:

/** 在该json项目中* 不会区分整数和非整数* number_value()和int_value()* 疑惑:为什么不能就使用一个number_value完成所有操作?*/
double number_value();
int int_value();

命名一个number_value()就能完成所有的任务,为什么还需要后者呢?
原作者也说了,Json不区分NUMBER是整数还是非整数

什么时候成员函数需要被声明为静态的?(疑惑)

size_t和std::string::size_type还是有区别

我们都知道size_t是unsigned int类型,而std::string::size_type也是unsigned int(这是我在初学C++的时候,在《C++primer》中看到的,所以我一直以为这两个就是同一个东西。

在大多数情况下,size_t和std::string::size_type是一个东西,因此它们通常可以互换,但是也可能因为平台的不同而出现些许的差异。

因此,为了程序的可移植性,还是使用std::string::size_type会更好,可以避免很多不必要的问题。
总结:最好还是使用C++本身就定义了的东西,特别是在编写库的时候,因为不知道程序会在什么系统中运行,因此,程序的可移植性尤为重要

为什么JsonValue的析构函数不是纯虚函数

class JsonValue{
protected:// 为什么使用的是友元?friend class Json;friend class JsonInt;friend class JsonDouble;virtual Json::Type type() const = 0;virtual bool equals(const JsonValue* other) const = 0;virtual bool less(const JsonValue* other) const = 0;virtual void dump(std::string& out) const = 0;virtual double number_value() const;virtual int int_value() const;virtual bool bool_value() const;virtual const std::string& string_value() const;virtual const Json::array& array_items() const;virtual const Json& operator[](size_t i) const;virtual const Json::object& object_items() const;virtual const Json& operator[](const std::string& key) const;virtual ~JsonValue() {}
};

不难看出,这个类是一个抽象类,这里我就有一个疑问:为什么析构函数没设置成纯虚的?而是提供了一个空实现?
因为如果析构函数也设置成了纯虚函数,继承它的所有类都强制需要重写析构函数,但是==这里提供了空实现的话,就可以使用编译器默认提供的析构函数==,会省很多事,同时可以避免一些可能的bug。

snprinf()

// 第三个参数用于指定格式化形式
int snprintf(char* str, size_t maxlen, const char* format, ...)

之前就看到了这个,说说这个函数的作用吧:

snprintf函数用于将格式化的数据输出到字符数组中,允许将数据格式化为指定格式的字符串。它以字符数组和格式化的方式工作,是一种基于C语言的函数。

上面提到了格式化的数据,什么是格式化的数据呢?就是将数据以一定格式进行处理,比如我们在使用printf输出小数的时候,我们能对输出的小数的位数进行规定,就是这个意思。

之前就想到了使用stringstream做类似的操作,但是现在看来这里选择使用snprintf()是有道理的。
stringstream由于是不定长的,它是动态管理内存,而snprintf()是直接对字符数组进行处理,在传参的时候就已经规定了缓冲区的大小,因此相比起stringstream,它的效率更高。
Json解析对性能有较高要求,因此更适合使用snprintf(),附上源码:

static void dump(double value, string &out) {if (std::isfinite(value)) {char buf[32];snprintf(buf, sizeof buf, "%.17g", value);out += buf;} else {out += "null";}
}

Json数据中,”"“要进行处理

在对字符串的处理中,有如下内容:

if (ch == '"') {out += "\\\"";
}

因为在 JSON 格式中,双引号是用来界定字符串值的起始和结束的标记,如果字符串中本身就含有双引号,为避免与 JSON 字符串的双引号冲突,需要进行转义处理。

这里可能需要去看看Json的内容。

构造函数非公有(public)(疑惑)

template <Json::Type tag, typename T>
class Value : public JsonValue {
protected:// Constructorsexplicit Value(const T &value) : m_value(value) {}explicit Value(T &&value)      : m_value(move(value)) {}// Get type tagJson::Type type() const override {return tag;}// Comparisonsbool equals(const JsonValue * other) const override {return m_value == static_cast<const Value<tag, T> *>(other)->m_value;}bool less(const JsonValue * other) const override {return m_value < static_cast<const Value<tag, T> *>(other)->m_value;}const T m_value;void dump(string &out) const override { json11::dump(m_value, out); }
};

主要的疑惑就是在这个构造函数上,它被声明为protected的,和private一样类外无法访问
岂不是说,我根本没法创建Value类型的对象?那么,这么做有何用?

解答:装饰类,装饰模式

装饰类是一种设计模式,属于结构性设计设计模式之一
在装饰模式中,我们可以动态地给一个对象添加一些额外的职能,而不需继承自子类。这种模式通过创建一个包装类来包裹一个原始类的对象。然后按需扩展其功能。从而实现对对象的功能增强而不改变原有的类结构。
在项目的后面,有很多其它的类继承自该类,如:JsonObject、JsonInt等,这很符合装饰模式的特点。
装饰模式通常具有以下要素:

  1. 抽象构件(Component):定义了对象的接口,可以是一个抽象类或者接口,声明了对象的基本功能
  2. 具体构建(ConcreteComponent):实现了抽象构建接口,是被装饰的类
  3. 装饰者(Decorator):持有一个抽象构件的引用并实现其接口,负责给对象动态添加新的功能
  4. 具体装饰者(ConcreteDecorator):实现了装饰者的接口,并对具体构件进行装饰。即:扩展或改变核心功能

所以在该项目中,有个最原始的类JsonValue,它用来定义最原始的接口,然后再使用Value进行装饰,最后使用JsonObject等子类对功能进行拓展。

为什么右值在传值的时候还需要使用std::move(疑惑)

源码如下:

explicit Value(T&& value)       : m_value(move(value)) {}

明明接收的就是一个右值,为什么还使用move呢?

数据一致性的实现

struct Statics {const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);const string empty_string;const vector<Json> empty_vector;const map<string, Json> empty_map;Statics() {}
};static const Statics & statics() {static const Statics s {};return s;
}static const Json & static_null() {// This has to be separate, not in Statics, because Json() accesses statics().null.static const Json json_null;return json_null;
}

在Json数据中,很多地方这些空值需要重复使用,但是只有全局变量使用的是默认值初始化,这个类定义了各种类型的Json数据的初始值和空值,这样可以避免一些隐藏的bug,并且使用静态方法和常量,确保了程序的安全性。
当我们想要初始化一个Json数据的时候,相对应地使用这些已经创建好的数据就行了。

匿名命名空间的作用

匿名命名空间用于限制命名空间内的符号的作用域,使得这些符号只能在当前编译单元内访问,并且避免与其他编译单元或全局作用域内的相同名字的符号发生冲突
上文中提到的编译单元就是指的当前源代码文件

在编译过程中,每个源代码文件会被编译器单独编译成一个编译单元,然后这些编译单元最终会被链接,成为可执行文件或者库。

namespace{...
}

在此处,我们将Json解析的一个类放在这个匿名空间中,用于监控Json解析的状态、进度等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/721174.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python高光谱遥感数据处理与机器学习

通过高光谱矿物识别&#xff0c;木材含水量提取、土壤有机碳评估等案例&#xff0c;提供可借鉴的高光谱应用领域的技术服务方案&#xff0c;结合Python科学计算、可视化、数据处理与机器学习等开源开发库&#xff0c;深入介绍高光谱技术的应用功能开发。 高光谱遥感信息对于我…

C++11线程同步之条件变量

C11线程同步之条件变量 condition_variable成员函数生产者和消费者模型 condition_variable_any成员函数生产者和消费者模型 条件变量是C11提供的另外一种用于 等待的同步机制&#xff0c;它能阻塞一个或多个线程&#xff0c;直到收到另外一个线程发出的通知或者超时时&#x…

springboot+xjar加密打包部署教程

需求背景 为了跟上时代的步伐&#xff0c;为了更好的生存。开个玩笑&#xff0c;就是心血来潮&#xff0c;使用xjar加密部署jar包&#xff0c;于是就测试一下。 xjar教程 1-maven配置文件修改 首先找到自己ideal配置的maven文件夹&#xff0c;然后找到apache-maven-3.9.3\co…

vue修改打包后静态资源路径的修改

不得不说&#xff0c;ai是真的强大&#xff0c;直接自己生成。

消息队列-Kafka-基础架构

基础架构 官网地址 上面这张图类比RocketMQ 相当于对一个主题进行了分区&#xff08;类似于RockeMQ 消息队列&#xff09;&#xff0c;每个分区存储到不同的Broker。在发送消息的时候都是发送到主分区。如果一台Broker由于其它节点备份了挂掉节点的数据&#xff0c;所以可以…

世界的本质是旋转(6)-在复平面上借助软件无线电SDR解调BPSK波形

在上一篇文章中&#xff0c;已经完成了BPSK波形的发射。 相对于BPSK波形的生成总共就4行代码&#xff0c;接收要略微复杂一些&#xff0c;算上各种同步、锁相环&#xff0c;约80行。完整版参考Git仓库。 设备连接&#xff1a; #mermaid-svg-aMmvYQSgMzU6Gepf {font-family:&q…

探索Python编程世界:从入门到精通

一.Python 从入门到精通 随着计算机科学的发展&#xff0c;编程已经成为了一种必备的技能。而 Python 作为一种简单易学、功能强大的编程语言&#xff0c;越来越受到人们的喜爱。本文将为初学者介绍 Python 编程的基础知识&#xff0c;帮助他们踏入 Python 编程的大门&#xf…

Linux 驱动 中断(二)

中断下半部 在 Linux 内核中&#xff0c;中断下半部&#xff08;也称为中断下半场&#xff09;是指在中断服务程序&#xff08;Top Half&#xff09;执行完毕后&#xff0c;在上下文之外延迟执行的一些操作。中断下半部通常用于处理那些不适合在中断上下文中立即执行的任务&am…

O2OA(翱途)移动端如何查看系统日志?

O2OA(翱途)移动端如何查看系统日志&#xff1f; 一、进入APP移动端应用&#xff0c;点击右下角“设置” 二、点击“系统日志”&#xff0c;如下图所示 点开日志列表即可查看。

关于 CTF 中 php 考点与绕过那些事的总结

关于 CTF 中常见 php 绕过的总结可以参考我之前的博客&#xff1a; CTF之PHP特性与绕过 PHP特性之CTF中常见的PHP绕过-CSDN博客 其中主要介绍了 md5()、sha1()、strcmp、switch、intval、$_SERVER 函数、三元运算符、strpos() 、数组、非法参数名传参等相关的绕过。 在此基础上…

Linux系统使用宝塔面板安装MySQL服务并实现公网远程访问本地数据库【内网穿透】

文章目录 前言1.Mysql服务安装2.创建数据库3.安装cpolar3.2 创建HTTP隧道 4.远程连接5.固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 宝塔面板的简易操作性,使得运维难度降低,简化了Linux命令行进行繁琐的配置,下面简单几步,通过宝塔面板cp…

#LLM入门|prompt#【整合目录】面向开发者的LLM入门教程

面向开发者的LLM入门教程笔记合集&#xff08;更新中&#xff09; 点击链接可跳转 目录 前言环境配置第一部分 面向开发者的提示工程 概述 1. 简介 Introduction2. 提示原则 Guidelines3. 迭代优化 Iterative4. 文本概括 Summarizing5. 推断 Inferring6. 文本转换 Transformi…

在Java中处理JSON数据:Jackson与Gson库比较

引言 JSON&#xff0c;作为一种轻量级的数据交换格式&#xff0c;因其易于人阅读和编写&#xff0c;同时也易于机器解析和生成&#xff0c;而被广泛应用于网络通信和配置文件中。在Java中&#xff0c;有两个强大的工具帮助咱们处理JSON数据——Jackson和Gson。这两个库各有千秋…

UnityAPI的学习——Object类

Object类是Unity中所有对象的基类、例如GameObject、Component、Material、Shader、Texture、Mesh、Font等都是Object的子类 Object类实例方法 在Object类中&#xff0c;涉及到的实例方法主要有GetInstanceID方法 GetInstanceID方法&#xff1a;Object对象ID 基本语法 publi…

从零开始写 Docker(四)---使用 pivotRoot 切换 rootfs 实现文件系统隔离

本文为从零开始写 Docker 系列第四篇&#xff0c;在mydocker run 基础上使用 pivotRoot 系统调用切换 rootfs 实现容器和宿主机之间的文件系统隔离。 完整代码见&#xff1a;https://github.com/lixd/mydocker 欢迎 Star 推荐阅读以下文章对 docker 基本实现有一个大致认识&…

javascript作用域编译浅析

作用域思维导图 1&#xff1a;编译原理 分词/词法分析 如果词法单元生成器在判断a是一个独立的词法单元还是其他词法单元的一部分时&#xff0c;调用的是有状态的解析规则&#xff0c;那么这个过程就被称为词法分析。 解析/语法分析 由词法单元流转换成一个由元素逐级嵌套所组…

期货开户交易软件如何下单?

一、手机和电脑使用的交易软件 目前期货市场常用的软件有文华、博弈、快期、易盛、同花顺等&#xff0c;这5款电脑软件对应的手机端是文华随身行、博弈手机版、快期小Q、易盛易星、同花顺期货通&#xff0c;这些常用软件大部分期货公司都是支持的。 二、交易软件如何下单 软…

C打印内存16进制

下面是一段C代码打印16进制 void print_hex(const char *msg, void *addr, int len) {uint8_t *p (uint8_t *)addr;printf("%s ,stat:%0x8, len:%d\n", msg, addr, len);for (int i 0; i < len / 16; i) {printf("0x%08x: ", p i * 16);for (int j …

【音视频开发好书推荐】《RTC程序设计:实时音视频权威指南》

1、WebRTC概述 WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个由Google发起的实时音视频通讯C开源库&#xff0c;其提供了音视频采集、编码、网络传输&#xff0c;解码显示等一整套音视频解决方案&#xff0c;我们可以通过该开源库快速地构建出一个音视频通…

如何保证招投标评标(系统)的公正性和透明度

在招投标过程中&#xff0c;评标的公正性和透明度是至关重要的&#xff0c;因为它涉及到公共利益和社会公正。然而&#xff0c;实际上&#xff0c;招投标过程中经常存在非法和不道德的行为&#xff0c;如暗箱操作、贿赂行为等&#xff0c;这些行为都会对招投标的公正性和透明度…