完整代码
#include <string>
#include <map>
#include <memory>
#include <stdexcept>
#include<iostream>
// 1. 配置基类(统一接口)
class ConfigVarBase {
public:
using ptr = std::shared_ptr<ConfigVarBase>; // 智能指针别名
ConfigVarBase(const std::string& name, const std::string& desc = "")
: m_name(name), m_desc(desc) {}
const std::string& getName() const { return m_name; }
const std::string& getDesc() const { return m_desc; }
// 纯虚函数:值转字符串 / 字符串转值(子类实现)
virtual std::string toString() = 0;
virtual bool fromString(const std::string& val) = 0;
protected:
std::string m_name; // 配置名称(如"server.port")
std::string m_desc; // 配置描述
};
// 2. 具体类型配置类(模板类,继承基类)
template<class T>
class ConfigVar : public ConfigVarBase {
public:
using ptr = std::shared_ptr<ConfigVar<T>>; // 智能指针别名
ConfigVar(const std::string& name, const T& default_val, const std::string& desc = "")
: ConfigVarBase(name, desc), m_val(default_val) {}
// 实现基类接口:值转字符串(简化版)
std::string toString() override {
return std::to_string(m_val); // 仅支持int等基础类型
}
// 实现基类接口:字符串转值(简化版)
bool fromString(const std::string& val) override {
try {
m_val = std::stoi(val); // 仅支持int类型转换(示例)
return true;
}
catch (...) {
return false;
}
}
// 获取/设置值(核心功能)
T getValue() const { return m_val; }
void setValue(const T& v) { m_val = v; }
private:
T m_val; // 存储具体类型的值(由模板参数T决定)
};
// 3. 配置管理器(全局管理所有配置)
class Config {
public:
// 存储所有配置的map(键:名称,值:基类指针)
using ConfigMap = std::map<std::string, ConfigVarBase::ptr>;
// 模板函数:查找或创建配置
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name,
const T& default_val,
const std::string& desc = "") {
// 先查是否已存在
auto it = s_datas.find(name);
if (it != s_datas.end()) {
// 转换为具体类型并返回
return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
}
// 检查名称合法性(简化版)
if (name.find("!") != std::string::npos) { // 禁止特殊字符
throw std::invalid_argument("无效配置名称: " + name);
}
// 创建新配置并加入全局map
auto new_var = std::make_shared<ConfigVar<T>>(name, default_val, desc);
s_datas[name] = new_var;
return new_var;
}
// 模板函数:仅查找配置
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name) {
auto it = s_datas.find(name);
if (it != s_datas.end()) {
return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
}
return nullptr; // 没找到返回空
}
private:
static ConfigMap s_datas; // 全局配置存储(静态成员)
};
// 初始化全局配置map
Config::ConfigMap Config::s_datas;
int main() {
// 1. 创建/获取int类型配置(端口,默认8080)
auto port = Config::Lookup<int>("server.port", 8080, "服务器端口");
// 2. 使用配置
std::cout << "当前端口: " << port->getValue() << std::endl; // 输出8080
// 3. 修改配置
port->setValue(443);
std::cout << "修改后的端口: " << port->getValue() << std::endl; // 输出443
// 4. 再次获取配置(全局生效)
auto same_port = Config::Lookup<int>("server.port");
std::cout << "再次获取端口: " << same_port->getValue() << std::endl; // 输出443
}
一、整体架构
代码通过三个核心组件实现通用配置管理:
ConfigVarBase
:所有配置的基类(统一接口)ConfigVar<T>
:模板类,存储具体类型的配置值Config
:配置管理器,全局管理所有配置
二、逐类解析
1. 基类 ConfigVarBase
作用:提供统一接口,让不同类型的配置能被全局存储和管理。
class ConfigVarBase {
public:
using ptr = std::shared_ptr<ConfigVarBase>; // 智能指针别名(简化写法)
// 构造函数:初始化名称和描述
ConfigVarBase(const std::string& name, const std::string& desc = "")
: m_name(name), m_desc(desc) {}
// 获取配置名称和描述(所有配置通用)
const std::string& getName() const { return m_name; }
const std::string& getDesc() const { return m_desc; }
// 纯虚函数:强制子类实现值与字符串的互转
virtual std::string toString() = 0; // 值 → 字符串
virtual bool fromString(const std::string& val) = 0; // 字符串 → 值
protected:
std::string m_name; // 配置名称(如"server.port")
std::string m_desc; // 配置描述(如"服务器端口")
};
关键点:
纯虚函数
toString()
和fromString()
是核心接口,确保所有配置都能转为字符串(方便存储)和从字符串恢复。智能指针别名
ptr
统一管理所有配置对象的生命周期。
2. 模板类 ConfigVar<T>
作用:存储具体类型的配置值(如 int
/string
),实现类型安全的读写。
template<class T>
class ConfigVar : public ConfigVarBase {
public:
using ptr = std::shared_ptr<ConfigVar<T>>; // 具体类型的智能指针
// 构造函数:初始化名称、默认值、描述(调用基类构造)
ConfigVar(const std::string& name, const T& default_val, const std::string& desc = "")
: ConfigVarBase(name, desc), m_val(default_val) {}
// 实现基类接口:T类型值 → 字符串
std::string toString() override {
return std::to_string(m_val); // 示例:int转字符串
}
// 实现基类接口:字符串 → T类型值
bool fromString(const std::string& val) override {
try {
m_val = std::stoi(val); // 示例:字符串转int
return true;
} catch (...) {
return false; // 转换失败返回false
}
}
// 类型安全的读写接口(核心功能)
T getValue() const { return m_val; } // 获取值(返回T类型)
void setValue(const T& v) { m_val = v; } // 设置值(只能传T类型)
private:
T m_val; // 存储具体类型的值(由模板参数T决定)
};
关键点:
- 模板参数
T
:决定配置值的类型(如int
),让一份代码支持多种类型。 - 类型安全:
getValue()
和setValue()
只能操作T
类型,编译器会检查类型错误(如给int
类型传字符串会报错)。 - 接口实现:
toString()
和fromString()
完成值与字符串的转换(示例仅支持int
,可扩展到string
等类型)。
3. 配置管理器 Config
作用:全局管理所有配置,负责创建、查找和维护配置的唯一性。
class Config {
public:
// 配置存储容器:键(名称)→ 值(基类指针)
using ConfigMap = std::map<std::string, ConfigVarBase::ptr>;
// 模板函数1:查找或创建配置(带默认值)
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name,
const T& default_val, const std::string& desc = "") {
// 步骤1:检查配置是否已存在
auto it = s_datas.find(name);
if (it != s_datas.end()) {
// 存在则转换为具体类型并返回(确保类型匹配)
return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
}
// 步骤2:检查名称合法性(简化版:禁止"!")
if (name.find("!") != std::string::npos) {
throw std::invalid_argument("无效配置名称: " + name);
}
// 步骤3:创建新配置并加入全局存储
auto new_var = std::make_shared<ConfigVar<T>>(name, default_val, desc);
s_datas[name] = new_var; // 存入map
return new_var;
}
// 模板函数2:仅查找配置(不带默认值)
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name) {
auto it = s_datas.find(name);
if (it != s_datas.end()) {
return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
}
return nullptr; // 没找到返回空
}
private:
static ConfigMap s_datas; // 全局配置存储(静态成员,全程序唯一)
};
// 初始化全局配置容器
Config::ConfigMap Config::s_datas;
关键点:
- 全局存储
s_datas
:静态map
容器,存储所有配置(键为名称,确保唯一)。 Lookup
函数:- 带默认值版本:首次调用创建配置并存入
s_datas
,后续调用直接返回已存在的配置。 - 仅查找版本:只查询不创建,找不到返回
nullptr
。
类型转换:std::dynamic_pointer_cast
将基类指针转为具体类型指针(如 ConfigVar<int>
),确保操作的是正确类型的配置(下行转换)。
下行转换关键点整理:
继承关系:
ConfigVar<T>
(派生类)继承自 ConfigVarBase
(基类)。
存储时的上行转换:
// 存入时:派生类指针自动转为基类指针(上行转换,隐式)
s_datas["port"] = std::make_shared<ConfigVar<int>>("port", 8080);
s_datas
存储的是 基类指针(ConfigVarBase::ptr
),丢失了具体类型信息。
使用时的下行转换:
// 取出时:必须从基类指针转回派生类指针(下行转换,显式)
return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
基类
ConfigVarBase
没有getValue()
等方法,必须转换为ConfigVar<T>
才能使用具体类型的功能。
auto new_var = std::make_shared<ConfigVar<T>>(name, default_val, desc);
的作用:
- 创建对象:在堆上构造
ConfigVar<T>
对象。 - 包装指针:返回
std::shared_ptr
自动管理对象生命周期。 - 优化内存:单次分配内存,比手动
new
更高效安全。
4. 主函数示例(使用流程)
int main() {
// 1. 创建/获取int类型配置(端口,默认8080)
auto port = Config::Lookup<int>("server.port", 8080, "服务器端口");
// 2. 使用配置
std::cout << "当前端口: " << port->getValue() << std::endl; // 输出8080
// 3. 修改配置
port->setValue(443);
std::cout << "修改后端口: " << port->getValue() << std::endl; // 输出443
// 4. 再次获取同一配置(全局生效)
auto same_port = Config::Lookup<int>("server.port");
std::cout << "再次获取端口: " << same_port->getValue() << std::endl; // 输出443
return 0;
}
流程解析:
首次调用
Lookup<int>("server.port", 8080)
:创建ConfigVar<int>
对象,存入s_datas
。修改值后,所有获取该配置的地方都会拿到新值(全局唯一)。