【C++】const 与非 const 重载方法的代码复用

在 C++ 中,实现 const 与非 const 重载方法的代码复用有多种技术。现介绍几种常见的方法:

1. 基本的 const 和非 const 重载

问题示例 - 代码重复

class MyVector {
private:
    int* data;
    size_t size;
    
public:
    // 非 const 版本
    int& at(size_t index) {
        // 边界检查、日志等公共逻辑
        if (index >= size) throw std::out_of_range("Index out of range");
        return data[index];
    }
    
    // const 版本 - 大量重复代码!
    const int& at(size_t index) const {
        // 重复的边界检查、日志等公共逻辑
        if (index >= size) throw std::out_of_range("Index out of range");
        return data[index];
    }
};

2. 方法1:使用 const_cast(不推荐)

class MyVector {
private:
    int* data;
    size_t size;
    
public:
    // const 版本作为主要实现
    const int& at(size_t index) const {
        if (index >= size) throw std::out_of_range("Index out of range");
        return data[index];
    }
    
    // 非 const 版本调用 const 版本
    int& at(size_t index) {
        // 使用 const_cast 移除 const
        return const_cast<int&>(static_cast<const MyVector*>(this)->at(index));
    }
};

缺点:const_cast 可能不安全,违反了 const 正确性。

3. 方法2:使用辅助私有方法(推荐)

class MyVector {
private:
    int* data;
    size_t size;
    
    // 私有辅助方法,包含公共逻辑
    void checkIndex(size_t index) const {
        if (index >= size) 
            throw std::out_of_range("Index out of range");
    }
    
public:
    // const 版本
    const int& at(size_t index) const {
        checkIndex(index);  // 复用公共逻辑
        return data[index];
    }
    
    // 非 const 版本
    int& at(size_t index) {
        checkIndex(index);  // 复用公共逻辑
        return data[index];
    }
};

4. 方法3:提取公共逻辑到单独方法

class MyVector {
private:
    int* data;
    size_t size;
    
    // 公共逻辑提取
    size_t validateIndex(size_t index) const {
        if (index >= size) 
            throw std::out_of_range("Index out of range");
        return index;
    }
    
public:
    const int& at(size_t index) const {
        return data[validateIndex(index)];
    }
    
    int& at(size_t index) {
        return data[validateIndex(index)];
    }
};

5. 方法4:使用模板和 SFINAE(高级技巧)

#include <type_traits>

class MyVector {
private:
    int* data;
    size_t size;
    
    // 公共验证逻辑
    void checkIndex(size_t index) const {
        if (index >= size) 
            throw std::out_of_range("Index out of range");
    }
    
public:
    // 统一的接口
    template<typename Self>
    auto&& at(this Self&& self, size_t index) {
        self.checkIndex(index);
        return self.data[index];
    }
};

注意:这使用了 C++23 的显式对象参数,需要现代编译器支持。

6. 方法5:对于复杂场景使用 CRTP

// CRTP 基类模板
template<typename Derived>
class VectorBase {
protected:
    void checkIndex(size_t index, size_t size) const {
        if (index >= size) 
            throw std::out_of_range("Index out of range");
    }
    
public:
    // const 版本
    auto at(size_t index) const -> const typename Derived::value_type& {
        const auto& self = static_cast<const Derived&>(*this);
        checkIndex(index, self.size());
        return self.data()[index];
    }
    
    // 非 const 版本  
    auto at(size_t index) -> typename Derived::value_type& {
        auto& self = static_cast<Derived&>(*this);
        checkIndex(index, self.size());
        return self.data()[index];
    }
};

// 具体实现
class MyVector : public VectorBase<MyVector> {
private:
    int* data_;
    size_t size_;
    
public:
    using value_type = int;
    
    MyVector(size_t size) : size_(size), data_(new int[size]) {}
    ~MyVector() { delete[] data_; }
    
    size_t size() const { return size_; }
    int* data() { return data_; }
    const int* data() const { return data_; }
    
    // 自动获得 at() 方法
};

7. 方法6:使用宏(不推荐但实用)

#define DEFINE_AT_METHODS() \
    const int& at(size_t index) const { \
        if (index >= size) throw std::out_of_range("Index out of range"); \
        return data[index]; \
    } \
    int& at(size_t index) { \
        return const_cast<int&>(static_cast<const MyVector*>(this)->at(index)); \
    }

class MyVector {
private:
    int* data;
    size_t size;
    
public:
    DEFINE_AT_METHODS()
    // 其他方法...
};

8. 实际完整示例

#include <iostream>
#include <stdexcept>
#include <vector>

class StringArray {
private:
    std::vector<std::string> data;
    
    // 公共验证逻辑
    void validateIndex(size_t index) const {
        if (index >= data.size()) {
            throw std::out_of_range("Index " + std::to_string(index) + 
                                  " out of range for size " + 
                                  std::to_string(data.size()));
        }
    }
    
    // 公共业务逻辑
    void logAccess(size_t index, const std::string& operation) const {
        std::cout << operation << " at index " << index << std::endl;
    }
    
public:
    StringArray(std::initializer_list<std::string> init) : data(init) {}
    
    // const 版本
    const std::string& at(size_t index) const {
        validateIndex(index);
        logAccess(index, "Read access");
        return data[index];
    }
    
    // 非 const 版本
    std::string& at(size_t index) {
        validateIndex(index);
        logAccess(index, "Write access");
        return data[index];
    }
    
    // 同样处理 operator[]
    const std::string& operator[](size_t index) const {
        return at(index);  // 复用 at 的逻辑
    }
    
    std::string& operator[](size_t index) {
        return at(index);  // 复用 at 的逻辑
    }
    
    size_t size() const { return data.size(); }
};

int main() {
    StringArray arr{"Hello", "World", "C++"};
    
    // 使用 const 版本
    const StringArray& constArr = arr;
    std::cout << constArr.at(0) << std::endl;  // 调用 const 版本
    
    // 使用非 const 版本
    arr.at(1) = "Programming";  // 调用非 const 版本
    std::cout << arr[1] << std::endl;  // 调用非 const 版本
    
    return 0;
}

9. 最佳实践总结

推荐做法:

  1. 提取公共验证逻辑到私有方法
  2. 提取公共业务逻辑到私有方法
  3. 保持两个版本方法简洁,只包含必要的差异
  4. 优先选择方法2和方法3,它们最安全且易维护

避免的做法:

  1. 大量代码重复在两个版本中
  2. 过度使用 const_cast,可能破坏 const 正确性
  3. 复杂的模板技巧,除非确实需要

现代 C++ 趋势:

  • C++23 的显式对象参数可以简化这种模式
  • 概念(Concepts)可以提供更好的类型约束
  • 始终优先考虑代码的可读性和维护性

选择哪种方法取决于具体的代码复杂度、团队约定和性能要求。对于大多数情况,方法2(提取公共逻辑)是最实用和安全的選擇。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值