当前位置: 首页 > java >正文

More Effective C++ 条款30:代理类

More Effective C++ 条款30:代理类


核心思想代理类是一种通过中间层对象来控制或修改对实际对象访问的设计模式,它能够在保持接口一致性的同时实现延迟计算、访问控制、日志记录等额外功能。

🚀 1. 问题本质分析

1.1 直接访问的局限性

  • 无法重载操作符行为:如无法区分operator[]的读/写操作
  • 无法添加额外逻辑:如访问控制、日志记录、延迟计算等
  • 接口固化:难以在不修改原有接口的情况下增加新功能
  • 隐式转换问题:需要控制某些类型的隐式转换

1.2 代理类的核心需求

  • 透明拦截:在不改变客户端代码的情况下拦截操作
  • 行为定制:根据需要修改或增强原有行为
  • 类型安全:保持编译时类型检查
  • 性能可控:避免不必要的开销
// 基础示例:数组下标代理
template<typename T>
class Array {
public:// 代理类,用于区分operator[]的读/写操作class Proxy {public:Proxy(Array& array, size_t index) : owner(array), idx(index) {}// 转换操作符:用于读操作operator T() const {std::cout << "Reading index " << idx << std::endl;return owner.data[idx];}// 赋值操作符:用于写操作Proxy& operator=(const T& value) {std::cout << "Writing index " << idx << " with value " << value << std::endl;owner.data[idx] = value;return *this;}// 禁止隐式取地址T* operator&() = delete;private:Array& owner;size_t idx;};Array(size_t size) : size(size), data(new T[size]) {}~Array() { delete[] data; }// 返回代理对象而非直接引用Proxy operator[](size_t index) {return Proxy(*this, index);}const T& operator[](size_t index) const {return data[index];}private:size_t size;T* data;
};// 使用示例
void basicExample() {Array<int> arr(10);arr[0] = 42;        // 写操作:Writing index 0 with value 42int value = arr[0]; // 读操作:Reading index 0
}

📦 2. 问题深度解析

2.1 区分operator[]的读/写操作

// 完善的读写区分代理
template<typename T>
class DifferentiatedArray {
public:// 可区分的代理类class Reference {public:Reference(DifferentiatedArray& arr, size_t idx) : array(arr), index(idx) {}// 读操作:转换为Toperator T() const {std::cout << "Const access to index " << index << std::endl;return array.get(index);}// 写操作:赋值运算符Reference& operator=(const T& value) {std::cout << "Non-const assignment to index " << index << std::endl;array.set(index, value);return *this;}// 支持链式赋值Reference& operator=(const Reference& other) {return operator=(static_cast<T>(other));}// 支持复合赋值操作符Reference& operator+=(const T& value) {T current = array.get(index);array.set(index, current + value);return *this;}private:DifferentiatedArray& array;size_t index;};DifferentiatedArray(size_t size) : size(size), data(new T[size]()) {}~DifferentiatedArray() { delete[] data; }// 非const版本返回代理Reference operator[](size_t index) {checkBounds(index);return Reference(*this, index);}// const版本返回直接值T operator[](size_t index) const {checkBounds(index);std::cout << "Direct const access to index " << index << std::endl;return data[index];}size_t getSize() const { return size; }private:void checkBounds(size_t index) const {if (index >= size) {throw std::out_of_range("Index out of bounds");}}T get(size_t index) const {return data[index];}void set(size_t index, const T& value) {data[index] = value;}size_t size;T* data;
};// 使用示例
void differentiationExample() {DifferentiatedArray<double> da(5);da[0] = 3.14;           // 写操作double val1 = da[0];    // 读操作(通过代理)const DifferentiatedArray<double>& constDa = da;double val2 = constDa[0]; // 直接const访问da[1] = da[0];          // 读+写操作da[2] += 1.0;           // 复合赋值操作
}

2.2 延迟计算与表达式模板

// 延迟计算代理
class LazyVector {
public:// 表达式模板基类struct VectorExpression {virtual double operator[](size_t i) const = 0;virtual size_t size() const = 0;virtual ~VectorExpression() = default;};// 具体向量表达式template<typename E>class VectorProxy : public VectorExpression {public:VectorProxy(const E& expr) : expression(expr) {}double operator[](size_t i) const override {return expression[i];}size_t size() const override {return expression.size();}private:const E& expression;};LazyVector(size_t size) : size(size), data(new double[size]()) {}// 从表达式构造template<typename E>LazyVector(const VectorExpression& expr) : size(expr.size()), data(new double[size]) {for (size_t i = 0; i < size; ++i) {data[i] = expr[i];}}// 表达式赋值template<typename E>LazyVector& operator=(const VectorExpression& expr) {assert(size == expr.size());for (size_t i = 0; i < size; ++i) {data[i] = expr[i];}return *this;}~LazyVector() { delete[] data; }double operator[](size_t index) const { return data[index]; }double& operator[](size_t index) { return data[index]; }size_t getSize() const { return size; }// 向量加法代理template<typename E1, typename E2>class AddProxy : public VectorExpression {public:AddProxy(const E1& e1, const E2& e2) : expr1(e1), expr2(e2) {}double operator[](size_t i) const override {return expr1[i] + expr2[i];}size_t size() const override {return expr1.size();}private:const E1& expr1;const E2& expr2;};// 加法操作符返回表达式代理template<typename E>AddProxy<LazyVector, E> operator+(const E& other) const {return AddProxy<LazyVector, E>(*this, other);}private:size_t size;double* data;
};// 使用示例
void lazyEvaluationExample() {LazyVector v1(3), v2(3), v3(3);v1[0] = 1.0; v1[1] = 2.0; v1[2] = 3.0;v2[0] = 4.0; v2[1] = 5.0; v2[2] = 6.0;// 这里并不立即计算,而是创建表达式模板auto expr = v1 + v2;// 只有在赋值时才实际计算v3 = expr;// 复杂的表达式也能高效处理v3 = v1 + v2 + v3;
}

⚖️ 3. 解决方案与最佳实践

3.1 智能代理与访问控制

// 带访问控制的代理
template<typename T>
class ControlledAccess {
public:class AccessProxy {public:AccessProxy(ControlledAccess& owner, const std::string& operation): owner(owner), op(operation) {owner.logAccess("Begin: " + op);}~AccessProxy() {owner.logAccess("End: " + op);}// 转发到实际对象T* operator->() { return &owner.object; }const T* operator->() const { return &owner.object; }T& operator*() { return owner.object; }const T& operator*() const { return owner.object; }private:ControlledAccess& owner;std::string op;};ControlledAccess(const T& obj = T()) : object(obj) {}AccessProxy operator->() { return AccessProxy(*this, "operator->"); }const AccessProxy operator->() const { return AccessProxy(*this, "const operator->"); }void logAccess(const std::string& message) const {std::cout << "Access log: " << message << std::endl;// 这里可以扩展到文件日志、审计等}private:T object;
};// 使用示例
class ExpensiveResource {
public:void performOperation() {std::cout << "Performing expensive operation..." << std::endl;}int calculate(int x, int y) {std::cout << "Calculating..." << std::endl;return x + y;}
};void accessControlExample() {ControlledAccess<ExpensiveResource> resource;// 所有访问都会被记录resource->performOperation();int result = resource->calculate(10, 20);// 输出:// Access log: Begin: operator->// Performing expensive operation...// Access log: End: operator->// Access log: Begin: operator->// Calculating...// Access log: End: operator->
}

3.2 空代理与惰性初始化

// 惰性初始化代理
template<typename T>
class LazyInitializer {
public:class InitProxy {public:InitProxy(LazyInitializer& owner) : owner(owner) {}T* operator->() {owner.ensureInitialized();return &owner.getObject();}const T* operator->() const {owner.ensureInitialized();return &owner.getObject();}T& operator*() {owner.ensureInitialized();return owner.getObject();}const T& operator*() const {owner.ensureInitialized();return owner.getObject();}private:LazyInitializer& owner;};template<typename... Args>LazyInitializer(Args&&... args) : initFunction([=]() { return T(std::forward<Args>(args)...); }),initialized(false) {}LazyInitializer(std::function<T()> initFunc) : initFunction(initFunc), initialized(false) {}InitProxy operator->() { return InitProxy(*this); }const InitProxy operator->() const { return InitProxy(*this); }InitProxy operator*() { return InitProxy(*this); }const InitProxy operator*() const { return InitProxy(*this); }bool isInitialized() const { return initialized; }void reset() {if (initialized) {object.~T();initialized = false;}}private:void ensureInitialized() const {if (!initialized) {// const_cast是安全的,因为我们知道实际上需要修改const_cast<LazyInitializer*>(this)->initialize();}}void initialize() {if (!initialized) {new (&storage) T(initFunction());initialized = true;}}T& getObject() {return *reinterpret_cast<T*>(&storage);}const T& getObject() const {return *reinterpret_cast<const T*>(&storage);}mutable std::aligned_storage_t<sizeof(T), alignof(T)> storage;std::function<T()> initFunction;bool initialized;
};// 使用示例
void lazyInitExample() {std::cout << "Creating lazy initializer..." << std::endl;LazyInitializer<std::vector<int>> lazyVec([]() {std::cout << "Actually initializing vector..." << std::endl;return std::vector<int>{1, 2, 3, 4, 5};});std::cout << "Lazy initializer created, but no initialization yet" << std::endl;if (!lazyVec.isInitialized()) {std::cout << "Vector not initialized yet" << std::endl;}// 第一次访问触发初始化std::cout << "First access: " << (*lazyVec)[0] << std::endl;if (lazyVec.isInitialized()) {std::cout << "Vector is now initialized" << std::endl;}
}

3.3 类型代理与维度安全

// 维度安全的物理量代理
template<typename Unit>
class Quantity {
public:explicit Quantity(double value) : value(value) {}double getValue() const { return value; }// 单位安全的运算Quantity operator+(const Quantity& other) const {return Quantity(value + other.value);}Quantity operator-(const Quantity& other) const {return Quantity(value - other.value);}template<typename OtherUnit>auto operator*(const Quantity<OtherUnit>& other) const {using ResultUnit = typename Unit::template Multiply<OtherUnit>;return Quantity<ResultUnit>(value * other.getValue());}template<typename OtherUnit>auto operator/(const Quantity<OtherUnit>& other) const {using ResultUnit = typename Unit::template Divide<OtherUnit>;return Quantity<ResultUnit>(value / other.getValue());}bool operator==(const Quantity& other) const {return value == other.value;}private:double value;
};// 单位类型定义
struct Meter { template<typename U> using Multiply = /* 单位乘法结果 */;template<typename U> using Divide = /* 单位除法结果 */;
};struct Second { template<typename U> using Multiply = /* 单位乘法结果 */;template<typename U> using Divide = /* 单位除法结果 */;
};struct MeterPerSecond { template<typename U> using Multiply = /* 单位乘法结果 */;template<typename U> using Divide = /* 单位除法结果 */;
};// 使用示例
void dimensionalSafetyExample() {Quantity<Meter> distance(100.0);     // 100米Quantity<Second> time(20.0);         // 20秒// 自动推导出速度类型(米/秒)auto speed = distance / time;        // 5米/秒// 编译时类型检查,防止错误的运算// auto error = distance + time;      // 编译错误:不能将米和秒相加Quantity<Meter> anotherDistance(50.0);auto totalDistance = distance + anotherDistance;  // 正确:相同单位可以相加std::cout << "Speed: " << speed.getValue() << " m/s" << std::endl;
}

💡 关键实践原则

  1. 透明性优先
    代理类应该尽可能透明,客户端代码不应感知代理的存在
  2. 性能考量
    代理类可能引入额外开销,需要在功能和性能间权衡
  3. 生命周期管理
    注意代理对象和实际对象的生命周期关系
  4. 接口完整性
    代理类需要提供与实际对象一致的接口
  5. 错误处理
    在代理中实现适当的错误处理和边界检查

代理模式的应用场景

// 1. 智能引用:添加额外行为(日志、审计、缓存)
// 2. 虚拟代理:延迟昂贵对象的创建
// 3. 保护代理:添加访问控制
// 4. 缓存代理:为昂贵操作添加缓存
// 5. 同步代理:添加线程安全

C++中的代理技术

// 标准库中的代理模式:
// - std::vector<bool>::reference:位代理
// - std::shared_ptr:引用计数代理
// - std::optional:值存在性代理
// - 迭代器:容器访问代理void stlProxyExamples() {std::vector<bool> bitset(8);bitset[0] = true;  // 使用代理对象std::optional<int> maybeValue;if (maybeValue) {  // 布尔转换代理int value = *maybeValue;  // 解引用代理}
}

总结
代理类是C++中强大的抽象工具,通过在客户端和实际对象之间插入中间层,实现了对对象访问的精细控制。

关键应用包括区分操作符重载版本、实现延迟计算、添加访问控制、确保维度安全等。代理模式使得可以在不修改现有接口的情况下增强功能,保持代码的清晰性和可维护性。

正确使用代理类需要平衡透明性和功能性的关系,注意性能影响和生命周期管理。现代C++开发中,代理模式是实现高级抽象和资源管理的重要技术。

http://www.xdnf.cn/news/20325.html

相关文章:

  • 2025高中文凭能考的证书大全
  • 2021/07 JLPT听力原文 问题一 4番
  • 第八章 惊喜05 笑笑点评团队
  • Claude Code成本浪费严重?80%开支可省!Token黑洞解密与三层省钱攻略
  • 使用YOLO11训练鸟类分类模型
  • AI应用开发-技术架构 PAFR介绍
  • JS魔法中介:Proxy和Reflect为何形影不离?
  • 【1】MOS管的结构及其工作原理
  • Linux系统: docker安装RagFlow教程
  • 【工具变量】上市公司企业海外业务收入数据集(2003-2024年)
  • C++ map和set
  • 2025年经济学专业女生必考证书指南:打造差异化竞争力
  • Netty从0到1系列之JDK零拷贝技术
  • Spring DI详解--依赖注入的三种方式及优缺点分析
  • Windows 权限提升(一)
  • ES模块(ESM)、CommonJS(CJS)和UMD三种格式
  • Java全栈学习笔记30
  • RX 9 Audio Editor 音频编辑器安装教程(v9.3.0 Windows版)
  • if __name__=‘__main__‘的用处
  • 推荐收藏!5款低代码工具,告别复杂开发!
  • 8051单片机-蜂鸣器
  • 数据库索引结构 B 树、B + 树与哈希索引在不同数据查询场景下的适用性分析
  • vue-amap组件呈现的效果图如何截图
  • 米尔RK3576部署端侧多模态多轮对话,6TOPS算力驱动30亿参数LLM
  • MySQL数据库基础(DCL,DDL,DML)详解
  • Axure笔记
  • 【VoNR】VoNR是5G语音,VoLTE是4G语音,他们是同一个IMS,只是使用了新的访问方式?
  • 传统神经网络实现-----手写数字识别(MNIST)项目
  • 状压 dp --- 棋盘覆盖问题
  • 使用smb协议同步远程文件失败