概述
本文介绍如何利用C++的模板和std::any
类型设计一个通用的对象管理器,以处理不同类型对象的增删改查操作。
实现方法
C++类设计
首先,我们定义一个简单的唯一标识符生成器类 CIdGenerator
,使用C++17的随机数生成器来生成唯一的标识符。
// Generate UUID using the <random> library
class CIdGenerator {
public:
CIdGenerator() : m_id(generateUUID()) {
}
const std::string& to_str() const {
return m_id;
}
private:
std::string m_id;
std::string generateUUID() const {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 15);
std::stringstream ss;
ss << std::hex;
for (int i = 0; i < 32; ++i) {
int random_value = dis(gen);
char c = (i == 8 || i == 12 || i == 16 || i == 20) ? '-' : '-';
ss << static_cast<char>(random_value < 10 ? '0' + random_value : 'a' + random_value - 10);
}
return ss.str();
}
};
接下来,我们定义一个通用的对象管理器类 CBaseObjectManager
,利用模板技术来处理任意类型对象的增删改查操作。这里使用了std::any
作为存储不同类型对象容器的方式,并通过模板函数实现操作。
// Forward declaration for ObjectNotFoundException
class ObjectNotFoundException : public std::runtime_error {
public:
explicit ObjectNotFoundException(const std::string& id) : std::runtime_error("Object with ID: " + id + " not found") {
}
};
// CBaseObjectManager class
class CBaseObjectManager {
public:
CBaseObjectManager() = default;
~CBaseObjectManager() = default;
// Get object with the specified ID
template <typename T>
T GetObject(const CIdGenerator& id, const std::any& anyVec) {
try {
const auto& vec = std::any_cast<const std::vector<T>&>(anyVec);
auto it = std::find_if(vec.begin(), vec.end(), [&id](const T& t) { return t.GetId() == id.to_str(); });
if (it != vec.end()) {
return *it;
} else {
throw ObjectNotFoundException(id.to_str());
}
} catch (const std::bad_any_cast&) {
throw std::runtime_error("Invalid cast: expected std::vector<T> in GetObject");
}
}
// Add object if it doesn't exist, return ID on success
template <typename T>
std::string AddObject(const T& src, std::any& anyVec) {
try {
auto& vec = std::any_cast<std::vector<T>&>(anyVec);
auto it = std::find_if(vec.begin(), vec.end(), [&src](const T& t) { return t.GetId() == src.GetId().to_str(); });
if (it == vec.end()) {
vec.push_back(src);
return src.GetId();
} else {
throw std::runtime_error("Object with ID: " + src.GetId().to_str() + " already exists");
}
} catch (const std::bad_any_cast&) {
throw std::runtime_error("Invalid cast: expected std::vector<T> in AddObject");
}
}
// Update object with the specified ID
template <typename T>
void UpdateObject(const T& src, const CIdGenerator& id, std::any& anyVec) {
try {
auto& vec = std::any_cast<std::vector<T>&>(anyVec);
auto it = std::find_if(vec.begin(), vec.end(), [&id](const T& t) { return t.GetId() == id.to_str(); });
if (it != vec.end()) {
*it = src;
} else {
throw ObjectNotFoundException(id.to_str());
}
} catch (const std::bad_any_cast&) {
throw std::runtime_error("Invalid cast: expected std::vector<T> in UpdateObject");
}
}
// Delete object with the specified ID
template <typename T>
void DeleteObject(const CIdGenerator& id, std::any& anyVec) {
try {
auto& vec = std::any_cast<std::vector<T>&>(anyVec);
auto it = std::remove_if(vec.begin(), vec.end(), [&id](const T& t) { return t.GetId() == id.to_str(); });
if (it != vec.end()) {
vec.erase(it, vec.end());
} else {
throw ObjectNotFoundException(id.to_str());
}
} catch (const std::bad_any_cast&) {
throw std::runtime_error("Invalid cast: expected std::vector<T> in DeleteObject");
}
}
};
性能考虑
在设计和实现上述类时,我们需要特别关注性能优化,尤其是在处理大量数据时的表现。以下是一些性能考虑的实践建议:
-
避免频繁的动态内存分配和释放: 在使用
std::vector
时,尽量预先分配适当的容量,以减少动态内存分配和频繁的重新分配。 -
利用现代C++特性: 使用C++标准库提供的现代化功能,如
std::any
和智能指针,可以帮助减少内存管理的复杂性和错误。 -
算法选择和优化: 在使用标准库算法时,选择最适合场景的算法(如使用
std::find_if
进行查找),并考虑数据结构的特性来优化性能。
示例代码
以下是一个简单的示例,演示了如何使用上述对象管理器类来管理用户对象的增删改查操作。
// Main function example
int main() {
std::vector<User> users;
std::any anyVec = std::ref(users); // Use std::ref to store std::vector<User>
CBaseObjectManager manager;
// Example: Add a user
User newUser("1");
try {
std::string id = manager.AddObject(newUser, anyVec);
std::cout << "Added user with ID: " << id << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
// Example: Update a user
User updatedUser("1");
manager.UpdateObject(updatedUser, updatedUser.GetId(), anyVec);
std::cout << "Updated user with ID: " << updatedUser.GetId() << std::endl;
// Example: Delete a user
try {
manager.DeleteObject<User>(updatedUser.GetId(), anyVec);
std::cout << "Deleted user with ID: " << updatedUser.GetId() << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
// Example: Query a user
try {
User foundUser = manager.GetObject<User>(updatedUser.GetId(), anyVec);
std::cout << "Found user with ID: " << foundUser.GetId() << std::endl;
} catch (const ObjectNotFoundException& e) {
std::cerr << "Error: " << e.what() << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
结论
通过本文的实现和示例,我们展示了如何使用C++的模板和std::any
实现通用的增删改查业务逻辑。在设计时,我们特别关注了代码性能的优化,确保在处理大量数据时仍能保持高效。开发团队可以根据具体项目需求和性能要求,进一步调整和优化上述实现,以实现更好的软件质量和用户体验。
参考
- C++17标准文档
- cppreference.com,C++参考文档
完整代码
#include <algorithm>
#include <any>
#include <iomanip>
#include <iostream>
#include <random>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
// Generate UUID using the <random> library
class CIdGenerator {
public:
CIdGenerator() : m_id(generateUUID()) {
}
const std::string& to_str() const {
return m_id;
}
private:
std::string m_id;
std::string generateUUID() const {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 15);
std::stringstream ss;
ss << std::hex;
for (int i = 0; i < 32; ++i) {
int random_value = dis(gen);
char c = (i == 8 || i == 12 || i == 16 || i == 20) ? '-' : '-';
ss << static_cast<char>(random_value < 10 ? '0' + random_value : 'a' + random_value - 10);
}
return ss.str();
}
};
// Forward declaration for ObjectNotFoundException
class ObjectNotFoundException : public std::runtime_error {
public:
explicit ObjectNotFoundException(const std::string& id) : std::runtime_error("Object with ID: " + id + " not found") {
}
};
// CBaseObjectManager class
class CBaseObjectManager {
public:
CBaseObjectManager() = default;
~CBaseObjectManager() = default;
// Get object with the specified ID
template <typename T>
T GetObject(const CIdGenerator& id, const std::any& anyVec) {
try {
const auto& vec = std::any_cast<const std::vector<T>&>(anyVec);
auto it = std::find_if(vec.begin(), vec.end(), [&id](const T& t) { return t.GetId() == id.to_str(); });
if (it != vec.end()) {
return *it;
} else {
throw ObjectNotFoundException(id.to_str());
}
} catch (const std::bad_any_cast&) {
throw std::runtime_error("Invalid cast: expected std::vector<T> in GetObject");
}
}
// Add object if it doesn't exist, return ID on success
template <typename T>
std::string AddObject(const T& src, std::any& anyVec) {
try {
auto& vec = std::any_cast<std::vector<T>&>(anyVec);
auto it = std::find_if(vec.begin(), vec.end(), [&src](const T& t) { return t.GetId() == src.GetId().to_str(); });
if (it == vec.end()) {
vec.push_back(src);
return src.GetId();
} else {
throw std::runtime_error("Object with ID: " + src.GetId().to_str() + " already exists");
}
} catch (const std::bad_any_cast&) {
throw std::runtime_error("Invalid cast: expected std::vector<T> in AddObject");
}
}
// Update object with the specified ID
template <typename T>
void UpdateObject(const T& src, const CIdGenerator& id, std::any& anyVec) {
try {
auto& vec = std::any_cast<std::vector<T>&>(anyVec);
auto it = std::find_if(vec.begin(), vec.end(), [&id](const T& t) { return t.GetId() == id.to_str(); });
if (it != vec.end()) {
*it = src;
} else {
throw ObjectNotFoundException(id.to_str());
}
} catch (const std::bad_any_cast&) {
throw std::runtime_error("Invalid cast: expected std::vector<T> in UpdateObject");
}
}
// Delete object with the specified ID
template <typename T>
void DeleteObject(const CIdGenerator& id, std::any& anyVec) {
try {
auto& vec = std::any_cast<std::vector<T>&>(anyVec);
auto it = std::remove_if(vec.begin(), vec.end(), [&id](const T& t) { return t.GetId() == id.to_str(); });
if (it != vec.end()) {
vec.erase(it, vec.end());
} else {
throw ObjectNotFoundException(id.to_str());
}
} catch (const std::bad_any_cast&) {
throw std::runtime_error("Invalid cast: expected std::vector<T> in DeleteObject");
}
}
};
// User class
class User {
public:
User() : m_id("") {
}
explicit User(const std::string& id) : m_id(id) {
}
const std::string& GetId() const {
return m_id;
}
private:
std::string m_id;
};
// Main function example
int main() {
std::vector<User> users;
std::any anyVec = std::ref(users); // Use std::ref to store std::vector<User>
CBaseObjectManager manager;
// Example: Add a user
User newUser("1");
try {
std::string id = manager.AddObject(newUser, anyVec);
std::cout << "Added user with ID: " << id << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
// Example: Update a user
User updatedUser("1");
manager.UpdateObject(updatedUser, updatedUser.GetId(), anyVec);
std::cout << "Updated user with ID: " << updatedUser.GetId() << std::endl;
// Example: Delete a user
try {
manager.DeleteObject<User>(updatedUser.GetId(), anyVec);
std::cout << "Deleted user with ID: " << updatedUser.GetId() << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
// Example: Query a user
try {
User foundUser = manager.GetObject<User>(updatedUser.GetId(), anyVec);
std::cout << "Found user with ID: " << foundUser.GetId() << std::endl;
} catch (const ObjectNotFoundException& e) {
std::cerr << "Error: " << e.what() << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}