C++14 新特性_第二章 C++14 标准库特性_std::exchange,std::quoted

本文介绍C++14新特性,std::exchange 和 std::quoted。

文章目录

  • [第二章 C++14 标准库特性](#第二章 C++14 标准库特性)
    • [2.5 std::exchange](#2.5 std::exchange)
      • [2.5.1 功能介绍](#2.5.1 功能介绍)
      • [2.5.2 使用举例](#2.5.2 使用举例)
    • [2.6 std::quoted](#2.6 std::quoted)
      • [2.6.1 使用说明](#2.6.1 使用说明)
      • [2.6.2 举例说明](#2.6.2 举例说明)
      • [2.6.3 总结](#2.6.3 总结)

第二章 C++14 标准库特性

2.5 std::exchange

C++14中引入了std::exchange,包含在头文件中。

C++11引入了移动语义后,在编写类时需要添加移动构造和移动赋值运算符,完成资源的转移,比如下面例子中将其他对象的ptr 转移到当前对象中,C++11实现如下:

cpp 复制代码
class Widget {
    int* ptr_;
public:
    // 移动构造函数
    Widget(Widget&& other) {
        ptr_ = other.ptr_;      // 1. 获取资源
        other.ptr_ = nullptr;   // 2. 将源置空,防止析构时双重释放
    }
};

这种实现比较经典,但是在C++14中引入了 std::exchange,将上边移动构造函数的共功能封装成了一个方法,可以让代码更简洁,下面开始介绍。

2.5.1 功能介绍

std::exchange 将一个新值赋给对象,并返回对象的旧值。大概的实现逻辑如下:

cpp 复制代码
template<class T, class U = T>
T exchange(T& obj, U&& new_value) {
    T old_value = std::move(obj);    // 移动或拷贝旧值
    obj = std::forward<U>(new_value); // 赋予新值,转发
    return old_value;                // 返回旧值
}

整体流程:分别传递进来一个新值和一个旧值。将新址资源转移给旧址,然后返回旧值。

2.5.2 使用举例

示例1:使用std::exchange完成移动构造函数。

cpp 复制代码
    class Buffer
    {
    public:
        Buffer()
        {}
        // 移动构造
        Buffer(Buffer && obj) noexcept
            : m_pData(std::exchange(obj.m_pData,nullptr))
            , m_size(std::exchange(obj.m_size,0))
        {
            //  总体实现逻辑:将
        }

        //
        // 添加拷贝构造函数
        Buffer(const Buffer& obj)
            : m_size(obj.m_size)
        {
            // 注意:如果 m_pData 指向动态内存,这里需要进行深拷贝(Deep Copy)
            // 否则两个对象指向同一块内存,析构时会崩溃
            if (obj.m_pData)
            {
                delete m_pData;
                m_pData = new char[m_size];
            }
        }

    public:
        char *m_pData = nullptr;
        size_t m_size;

    };

    void test()
    {
        Buffer buf;
        buf.m_size = 22;
        cout << "buf.m_size = " <<  buf.m_size << endl;
        Buffer buf2(std::move(buf));
        cout << "buf.m_size = " <<  buf.m_size << endl;
        cout << "buf2.m_size = " <<  buf2.m_size << endl;
        /*
         *  buf.m_size = 22
            buf.m_size = 0
            buf2.m_size = 22
         */
    }

示例2:状态重置与标志检查

当需要检查一个标志位时,并在检查后立即将其重置时,使用std::exchange非常方便。

cpp 复制代码
void EventLoop::process() {
    // 检查是否有新事件,如果有,处理它,并立即将 has_new_event_ 重置为 false
    // 这一行代码完成了:读取 -> 判断 -> 重置
    if (std::exchange(has_new_event_, false)) {
        handle_events();
    }
}

2.6 std::quoted

C++14之前,处理包含空格的字符串流(Stream)操作主要有两个痛点:

痛点1:读取待空格的字符串

使用cin>>s时,默认会以空格为分隔符,比如输入"hello world",流操作会把它切分成"Hello"和"world",导致数据错位。

痛点2:转义字符的丢失

当你把一个包含引号 " 或反斜杠 \ 的字符串写入文件,再读回来时,原始的结构往往会被破坏,因为这些特殊字符没有被正确转义(Escape)。

以前的笨办法: 你需要手动编写复杂的逻辑来检测引号、处理转义字符,或者使用 getline 配合自定义的分隔符解析逻辑。

std::quoted 的出现就是为了用一行代码解决"带引号和转义字符的字符串往返(Round-trip)"问题。

2.6.1 使用说明

std::quoted 是一个流操纵符(Stream Manipulator),它既可以用于输出流,也可以用于输入流。

函数原型

cpp 复制代码
#include <iomanip>

// 用于输出
std::quoted(const String& s, char delim = '"', char escape = '\\');

// 用于输入
std::quoted(String& s, char delim = '"', char escape = '\\');

参数说明:

参数1:s 要处理的字符串。

参数2:delim 分隔符,默认为双引号"

参数3:escape,转义符,默认为反斜杠 \

函数作用:

输出时:自动在字符串两端加上双引号;如果字符串内部包含双引号或反斜杠,会自动在它们前面加上转义符,例如 " 或 \

输入时:期望输入流中的下一个token是被双引号包围的,自动去掉两端的双引号,自动处理转义字符。

2.6.2 举例说明

示例1:最经典的"空格截断"问题

cpp 复制代码
	// 不使用std::quote 处理带空格的字符串
    void test2()
    {
		std::stringstream ss;
        std::string out = "zhongguo beijing"s;
        std::string in;

		ss << out;
		ss >> in;

        std::cout << "原始: " << out << std::endl;
        // 原始: zhongguo beijing
        std::cout << "读取: " << in << std::endl;
		// 读取: zhongguo
		// beijings 在>> 操作符中被空格分隔符截断 
    }

	// 使用 std::quote 处理带引号的字符串
    void test()
    {
        std::stringstream ss;
        std::string out = "zhongguo beijing"s;
        std::string in;

        // 写入是  "zhongguo beijing" 完整名字
		ss << std::quoted(out);

		// 读取时解析引号,正确读取完整名字
		ss >> std::quoted(in);

        std::cout << "原始: " << out << std::endl;
        // 原始: zhongguo beijing
        std::cout << "读取: " << in << std::endl;
		// 读取: zhongguo beijing
    }

示例2:处理特殊字符(JSON/CSV场景)

处理一个句子,包含了双引号" ",使用std::quote。

假设我们要保存一个包含引号的 句子:She said, "Hello, World!"

cpp 复制代码
    void test()
    {
        std::stringstream ss;

		std::string original = R"(She said, "Hello, World!")"; 

		// 1 写入,在写入时,quoted 会自动转义内部引号
        ss << std::quoted(original);
		cout << "流中的原始内容:" << ss.str() << std::endl;
        // She said, \"Hello, World!\""

		// 2 读取,quoted 会自动处理转义字符
		std::string loaded;
		ss >> std::quoted(loaded);
		cout << "读取的内容:" << loaded << std::endl;
        // 读取的内容:She said, "Hello, World!"
    }

示例3:自定义分隔符(非标准CSV)

下面使用单引号' 作为定界符。

cpp 复制代码
    void test3()
    {
        std::stringstream ss;
        std::string data = "O'Neil"; // 名字里带单引号

        // 告诉 quoted 使用单引号作为边界,反斜杠作为转义
        ss << std::quoted(data, '\'', '\\');

        // 流中内容变为: 'O\'Neil'
        std::cout << ss.str() << std::endl;

        std::string loaded;
        ss >> std::quoted(loaded, '\'', '\\');
        cout << "读取的内容:" << loaded << std::endl;
		// 读取的内容:O'Neil
    }

2.6.3 总结

简化带空格,引号字符串的序列化和反序列化。

主要使用场景:读写配置文件、简单的 CSV 解析、JSON 字段生成、日志记录。

相关推荐
无限进步_39 分钟前
基于顺序表的通讯录系统设计与实现
c语言·开发语言·数据结构·c++·后端·算法·visual studio
宠..1 小时前
使用纯代码设计界面
开发语言·c++·qt
小此方1 小时前
Re:从零开始的链式二叉树:建树、遍历、计数、查找、判全、销毁全链路实现与底层剖析
c语言·数据结构·c++·算法
筱砚.1 小时前
【C++——文件操作案例】
开发语言·c++
FMRbpm1 小时前
STL中栈的实现
数据结构·c++·算法
sulikey1 小时前
C/C++内存管理深度解析:从内存分布到new/delete底层原理
c语言·c++·内存管理·placement-new
bin91531 小时前
当AI化身Git管家:初级C++开发者的版本控制焦虑与创意逆袭——老码农的幽默生存指南
c++·人工智能·git·工具·ai工具
自由生长20241 小时前
C++折叠表达式完全指南:从打印函数到空包处理的深入解析
c++·后端
zore_c2 小时前
【C语言】文件操作详解1(文件的打开与关闭)
c语言·开发语言·数据结构·c++·经验分享·笔记·算法