本期我们接着深入项目的编写。
相关代码已经上传至作者个人gitee:仿muduo服务器: 本项目致力于实现一个仿造muduo库的简易并发服务器,为个人项目,参考即可喜欢请点个赞谢谢
目录
[默认构造与值构造 / 赋值](#默认构造与值构造 / 赋值)
正则表达式
正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
正则表达式的使用,可以使得HTTP请求的解析更加简单(这里指的是程序员的工作变得简单,这并不代表处理效率会变高,实际上效率上是低于直接的字符串处理的),使我们实现的HTTP组件库使用起来更加灵活。
在C++中,<regex>就是为了正则表达式实现的库
<regex>库的接口介绍
-
std::regex-
表达式 :
std::regex r(const std::string& pattern, flag_type flags = ECMAScript); -
参数 :
pattern为正则表达式字符串;flags可选语法选项(如ECMAScript、basic、extended、icase等)。 -
作用:编译正则表达式,存储为一个可用的正则对象。
-
-
std::regex_match-
表达式 :
bool regex_match(const std::string& s, std::smatch& m, const std::regex& r); -
参数 :
s为源字符串;m用于存储匹配结果(子匹配组);r为已编译的正则对象。 -
作用 :检查整个字符串是否完全匹配正则表达式(从开头到结尾必须完全符合)。
-
-
std::regex_search-
表达式 :
bool regex_search(const std::string& s, std::smatch& m, const std::regex& r); -
参数 :同上,
m存储首次匹配的子串及捕获组。 -
作用 :在字符串中搜索第一个匹配正则表达式的子序列(不需要全串匹配)。
-
-
std::regex_replace-
表达式 :
std::string regex_replace(const std::string& s, const std::regex& r, const std::string& fmt); -
参数 :
s源字符串;r正则对象;fmt替换格式字符串(可用$1、$2引用捕获组)。 -
作用 :查找所有匹配正则的子串,并用
fmt替换,返回新字符串。
-
-
std::sregex_iterator-
表达式 :
std::sregex_iterator it(s.begin(), s.end(), r); -
参数:迭代器范围(字符串首尾)和正则对象。
-
作用 :迭代遍历字符串中所有匹配的子串,每次解引用得到
std::smatch对象,用于批量处理匹配。
-
示例:
cpp
#include <iostream>
#include <regex>
#include <string>
int main()
{
std::string text = "Contact: alice@example.com, bob@test.org";
std::regex email_pattern(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
// 1. regex_match:要求整个字符串完全匹配
std::string single = "user@domain.com";
if (std::regex_match(single, email_pattern))
std::cout << "Full match: " << single << " is an email\n";
else
std::cout << "Not a full match\n";
// 2. regex_search:查找第一个匹配的子串
std::smatch match;
if (std::regex_search(text, match, email_pattern))
std::cout << "First email found: " << match[0] << "\n";
// 3. regex_replace:替换所有匹配项
std::string replaced = std::regex_replace(text, email_pattern, "[EMAIL]");
std::cout << "Replaced text: " << replaced << "\n";
// 4. sregex_iterator:遍历所有匹配项
std::cout << "All emails: ";
for (std::sregex_iterator it(text.begin(), text.end(), email_pattern), end; it != end; ++it)
std::cout << it->str() << " ";
std::cout << "\n";
return 0;
}
结果为:

正则表达式解析处理HTTP请求
cpp
#include <iostream>
#include <regex>
#include <string>
int main()
{
// 示例 HTTP 请求行
std::string request_line = "GET /api/users?id=123&name=john HTTP/1.1";
// 正则表达式:
// 分组1: 方法 (大写字母)
// 分组2: 路径 (非空白且不含 '?')
// 分组3: 查询字符串 (可选,'?' 后非空白字符)
// 分组4: HTTP版本 (HTTP/x.x)
std::regex pattern(R"(^([A-Z]+)\s+([^\s?]+)(?:\?([^\s]+))?\s+(HTTP/\d\.\d)$)");
std::smatch matches;
if (std::regex_match(request_line, matches, pattern)) {
std::cout << "完整请求行: " << request_line << "\n\n";
std::cout << "请求方法: " << matches[1] << std::endl;
std::cout << "请求路径: " << matches[2] << std::endl;
// 查询字符串可能不存在
if (matches[3].matched)
std::cout << "查询字符串: " << matches[3] << std::endl;
else
std::cout << "查询字符串: (无)" << std::endl;
std::cout << "HTTP版本: " << matches[4] << std::endl;
} else {
std::cerr << "请求行格式不匹配" << std::endl;
}
return 0;
}
结果为:

any类
每一个Connection对连接进行管理,最终都不可避免需要涉及到应用层协议的处理,因此在Connection中需要设置协议处理的上下文来控制处理节奏。但是应用层协议千千万,为了降低耦合度,这个协议接收解析上下文就不能有明显的协议倾向,它可以是任意协议的上下文信息,因此就需要一个通用的类型来保存各种不同的数据结构。
在C语言中,通用类型可以使用void*来管理,但是在C++中,boost库和C++17给我们提供了一个通用类型any来灵活使用,如果考虑增加代码的移植性,尽量减少第三方库的依赖,则可以使用C++17特性中的any,或者自己来实现。
<any>类介绍
默认构造与值构造 / 赋值
表达式: std::any a; 或 std::any a = 值;
参数: 无参构造得到空对象;若传入任意可拷贝类型 T 的值,则从该值构造或赋值。
作用: 创建一个 std::any 容器。默认构造时不含任何值(has_value() 返回 false)。传入具体值时,内部会存储一份 T 的拷贝(或通过移动语义接管资源),此后该 any 对象便拥有该类型的有效数据。
std::make_any<T>(args...)
表达式: auto a = std::make_any<T>(构造参数...);
参数: 目标类型 T 及其构造函数所需的实参列表。
作用: 直接在 any 内部原地构造 T 类型的对象,完全避免临时对象的产生与多余的拷贝/移动开销。与先构造临时对象再存入 any 相比,它更高效且异常安全。
has_value()
表达式: a.has_value()
参数: 无。
作用: 返回布尔值,指示当前 any 对象是否持有有效值(即非空)。空 any 通常由默认构造、调用 reset() 或被移走值产生。
type()
表达式: a.type()
参数: 无。
作用: 返回 const std::type_info&,代表当前所存储对象的准确类型。若 any 为空,则返回 typeid(void)。该接口是进行类型安全检查的基础,常与 any_cast 的指针形式配合使用。
std::any_cast<T>
表达式: 存在两种重载形式:
-
引用形式:
std::any_cast<T>(a) -
指针形式:
std::any_cast<T>(&a)
参数: 前者接受any对象的引用;后者接受any对象的指针。
作用: 提取any中存储的值。 -
引用形式 :若存储类型与
T不匹配,抛出std::bad_any_cast异常。 -
指针形式 :若存储类型与
T不匹配,返回nullptr;匹配则返回指向内部存储对象的指针。推荐优先使用指针形式进行安全访问。
emplace<T>(args...)
表达式: a.emplace<T>(构造参数...);
参数: 目标类型 T 及其构造参数。
作用: 销毁当前存储的任何旧值(如果有),然后在原地构造一个新的 T 类型对象。它避免了先构造临时值再赋值的中间步骤,且能复用已分配的内存(如果实现支持),提高运行期变更类型时的效率。
reset()
表达式: a.reset();
参数: 无。
作用: 销毁当前存储的对象(调用其析构函数),并将 any 对象重置为空状态。调用后 has_value() 将返回 false。该操作不会影响 any 对象本身的存储空间,仅是清空其内容。
swap()
表达式: a1.swap(a2); 或 std::swap(a1, a2);
参数: 另一个同类型的 std::any 对象。
作用: 交换两个 any 容器所持有的值以及它们的状态。该操作是高效的,通常仅交换内部指针或控制块,不涉及所存储类型的拷贝或移动构造。
示例代码
cpp
#include <any>
#include <iostream>
#include <string>
#include <vector>
int main()
{
// 1. 默认构造(空 any)
std::any a;
std::cout << "1. 默认构造后 has_value(): " << a.has_value() << '\n';
// 2. 值构造:存入 int
std::any b = 42; // 隐式转换自 int
std::any c(std::string("hello")); // 显式构造
std::cout << "2. 存入 int: " << std::any_cast<int>(b) << '\n';
// 3. std::make_any:原地构造 vector,避免临时对象
auto d = std::make_any<std::vector<int>>(3, 10); // 三个 10
std::cout << "3. make_any<vector>: size="
<< std::any_cast<std::vector<int>>(d).size() << '\n';
// 4. has_value() 和 type()
std::cout << "4. d 是否含有值? " << d.has_value()
<< ", 类型名: " << d.type().name() << '\n';
// 5. any_cast 指针形式(安全访问)
if (auto* ptr = std::any_cast<std::string>(&c))
{
std::cout << "5. 指针提取 string: " << *ptr << '\n';
}
if (auto* ptr = std::any_cast<int>(&c))
{
// 不会执行,因为 c 里是 string
}
else
{
std::cout << " 类型不匹配,返回 nullptr\n";
}
// 6. any_cast 引用形式(可能抛异常)
try
{
std::cout << "6. 引用提取 int: " << std::any_cast<int>(b) << '\n';
// 下面这行将抛出 std::bad_any_cast
// std::cout << std::any_cast<double>(b);
}
catch (const std::bad_any_cast& e)
{
std::cout << " 捕获异常: " << e.what() << '\n';
}
// 7. emplace:销毁旧值,原地构造新类型
b.emplace<double>(3.14159);
std::cout << "7. emplace<double> 后: " << std::any_cast<double>(b) << '\n';
// 8. reset:清空内容
b.reset();
std::cout << "8. reset 后 has_value(): " << b.has_value() << '\n';
// 9. swap:交换两个 any 的内容
std::any x = 100;
std::any y = std::string("world");
std::cout << "9. 交换前: x=" << std::any_cast<int>(x)
<< ", y=" << std::any_cast<std::string>(y) << '\n';
x.swap(y);
std::cout << " 交换后: x=" << std::any_cast<std::string>(x)
<< ", y=" << std::any_cast<int>(y) << '\n';
return 0;
}
结果为:

设计思想
嵌套一下,设计一个类,专门用于保存其他类型的数据,而Any类保存的是固定类的对象
cpp
class Any
{
private:
class holder
{
....
};
template<class T>
class placeholder: holder
{
T_val I
};
holder *content;
};
Any类中,保存的是holder类的指针,当Any容器需要保存一个数据的时候,只需要通过placeholder子类实例化一个特定类型的子类对象出来,让子类对象保存数据
源代码:
cpp
#pragma once
#include <utility>
#include <typeinfo>
class Any
{
private:
class holder
{
public:
virtual ~holder() {}
virtual const std::type_info& type() const = 0;
virtual holder* clone() const = 0;
};
template<class T>
class placeholder : public holder
{
public:
placeholder(const T& val) : _val(val) {}
virtual ~placeholder() = default;
virtual const std::type_info& type() const override {
return typeid(_val);
}
virtual holder* clone() const override {
return new placeholder<T>(_val);
}
T _val;
};
holder* _holder;
public:
Any() : _holder(nullptr) {}
template<class T>
Any(const T& val) : _holder(new placeholder<T>(val)) {}
Any(const Any& other)
: _holder(other._holder ? other._holder->clone() : nullptr) {}
~Any() {
delete _holder;
}
Any& Swap(Any& other) noexcept {
std::swap(_holder, other._holder);
return *this;
}
// 从任意类型赋值
template<class T>
Any& operator=(const T& val) {
Any(val).Swap(*this);
return *this;
}
// 拷贝赋值运算符(非模板)
Any& operator=(const Any& other) {
Any(other).Swap(*this);
return *this;
}
// 获取存储值的指针(非常量版本)
template<class T>
T* get() {
if (_holder && typeid(*_holder) == typeid(placeholder<T>)) {
return &(static_cast<placeholder<T>*>(_holder)->_val);
}
return nullptr;
}
// 获取存储值的指针(常量版本)
template<class T>
const T* get() const {
if (_holder && typeid(*_holder) == typeid(placeholder<T>)) {
return &(static_cast<const placeholder<T>*>(_holder)->_val);
}
return nullptr;
}
};
测试
cpp
#include "MyAny.h"
#include <iostream>
#include <cassert>
#include <string>
class TestCounter
{
public:
static int construct_count;
static int destruct_count;
TestCounter() { construct_count++; }
TestCounter(const TestCounter&) { construct_count++; }
~TestCounter() { destruct_count++; }
};
int TestCounter::construct_count = 0;
int TestCounter::destruct_count = 0;
void test_default_constructor()
{
std::cout << "Test 1: Default Constructor" << std::endl;
Any a;
assert(a.get<int>() == nullptr);
std::cout << " PASSED" << std::endl;
}
void test_template_constructor()
{
std::cout << "Test 2: Template Constructor" << std::endl;
Any a1(42);
Any a2(3.14);
Any a3(std::string("hello"));
std::cout << " PASSED" << std::endl;
}
void test_copy_constructor()
{
std::cout << "Test 3: Copy Constructor" << std::endl;
Any a1(42);
Any a2(a1);
Any a3(std::string("test"));
Any a4(a3);
std::cout << " PASSED" << std::endl;
}
void test_destructor()
{
std::cout << "Test 4: Destructor" << std::endl;
TestCounter::construct_count = 0;
TestCounter::destruct_count = 0;
{
Any a{TestCounter()}; // 修复 most vexing parse
}
assert(TestCounter::construct_count == TestCounter::destruct_count);
std::cout << " PASSED" << std::endl;
}
void test_swap()
{
std::cout << "Test 5: Swap Method" << std::endl;
Any a1(42);
Any a2(3.14);
a1.Swap(a2);
std::cout << " PASSED" << std::endl;
}
void test_get()
{
std::cout << "Test 6: Get Method" << std::endl;
Any a1(42);
Any a2(3.14);
Any a3(std::string("hello"));
int* p1 = a1.get<int>();
double* p2 = a2.get<double>();
std::string* p3 = a3.get<std::string>();
if (p1)
{
std::cout << " int value: " << *p1 << std::endl;
}
if (p2) {
std::cout << " double value: " << *p2 << std::endl;
}
if (p3) {
std::cout << " string value: " << *p3 << std::endl;
}
double* wrong_type = a1.get<double>(); // 修改这里
assert(wrong_type == nullptr);
std::cout << " PASSED" << std::endl;
}
void test_assignment_operator_value()
{
std::cout << "Test 7: Assignment Operator (Value)" << std::endl;
Any a1;
a1 = 42;
Any a2;
a2 = 3.14;
Any a3;
a3 = std::string("test");
std::cout << " PASSED" << std::endl;
}
void test_assignment_operator_any()
{
std::cout << "Test 8: Assignment Operator (Any)" << std::endl;
Any a1(42);
Any a2;
a2 = a1;
Any a3(std::string("hello"));
Any a4;
a4 = a3;
std::cout << " PASSED" << std::endl;
}
void test_self_assignment()
{
std::cout << "Test 9: Self Assignment" << std::endl;
Any a(42);
a = a;
std::cout << " PASSED" << std::endl;
}
void test_chained_assignment()
{
std::cout << "Test 10: Chained Assignment" << std::endl;
Any a1(42);
Any a2;
Any a3;
a3 = a2 = a1;
std::cout << " PASSED" << std::endl;
}
void test_multiple_types()
{
std::cout << "Test 11: Multiple Types" << std::endl;
Any a1(42);
Any a2(3.14);
Any a3('c');
Any a4(true);
Any a5(std::string("hello"));
std::cout << " PASSED" << std::endl;
}
void test_empty_any()
{
std::cout << "Test 12: Empty Any Operations" << std::endl;
Any a1;
Any a2;
a1.Swap(a2);
Any a3;
a3 = a1;
std::cout << " PASSED" << std::endl;
}
void test_overwrite_value()
{
std::cout << "Test 13: Overwrite Value" << std::endl;
Any a(42);
a = 3.14;
std::cout << " PASSED" << std::endl;
}
void test_complex_type()
{
std::cout << "Test 14: Complex Type" << std::endl;
struct Point {
int x, y;
Point(int x_, int y_) : x(x_), y(y_) {}
};
Any a(Point(10, 20));
Point* p = a.get<Point>();
if (p) {
std::cout << " Point: (" << p->x << ", " << p->y << ")" << std::endl;
}
std::cout << " PASSED" << std::endl;
}
void test_swap_with_empty()
{
std::cout << "Test 15: Swap with Empty Any" << std::endl;
Any a1(42);
Any a2;
a1.Swap(a2);
std::cout << " PASSED" << std::endl;
}
int main()
{
std::cout << "=== MyAny Unit Tests ===" << std::endl << std::endl;
test_default_constructor();
test_template_constructor();
test_copy_constructor();
test_destructor();
test_swap();
test_get();
test_assignment_operator_value();
test_assignment_operator_any();
test_self_assignment();
test_chained_assignment();
test_multiple_types();
test_empty_any();
test_overwrite_value();
test_complex_type();
test_swap_with_empty();
std::cout << std::endl << "=== All Tests Completed ===" << std::endl;
return 0;
}
结果为:

本期内容就到这里了,喜欢请点个赞谢谢
封面图自取:
