C++ 遗留代码重构:策略与示例

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

重构C++遗留代码:逐步指南



处理C++遗留代码是开发人员职业生涯中最困难但不可避免的任务之一。遗留代码通常缺乏单元测试,包含魔法数字、内存泄漏和混乱的依赖关系。然而,如果遵循经过验证的策略,重构此类代码而不冒破坏系统的风险是可能的。在本文中,我们将通过实际示例分析重构遗留C++代码的关键方法。



1. 准备:创建安全网



在修改代码之前,必须创建一个由测试组成的“安全网”。对于C++来说,这一点尤为重要,因为错误可能导致未定义行为或程序崩溃。



步骤 1.1:特征化测试

编写能够记录代码当前行为的测试,即使该行为不正确。使用类似Google Test或Catch2的框架。



// 遗留函数的特征化测试示例#include <gtest/gtest.h>

// 我们将要重构的遗留函数int calculatePrice(int quantity, float basePrice) { if (quantity > 100) { return static_cast<int>(basePrice * quantity * 0.9); } else { return static_cast<int>(basePrice * quantity); }}

TEST(PriceCalculatorTest, LegacyBehavior) { EXPECT_EQ(calculatePrice(50, 10.0f), 500); EXPECT_EQ(calculatePrice(150, 10.0f), 1350); // 150*10*0.9}


步骤 1.2:集成到CI/CD

将测试添加到持续集成系统中。这可以确保更改不会破坏现有逻辑。



2. 策略:无痛“智能”重构



不要试图一次性重写所有内容。使用增量方法。



2.1. 提取接口 (Extract Interface)

如果代码与全局状态或特定类紧密耦合,请从提取抽象开始。



问题: 类直接与文件系统交互。

// 遗留代码class DataProcessor {public:    void process(const std::string& filename) {        std::ifstream file(filename);        // 复杂的处理逻辑    }};


解决方案: 引入接口和依赖注入。

// 新接口class IFileReader {public:    virtual std::string read(const std::string& path) = 0;    virtual ~IFileReader() = default;};

// 重构后class DataProcessor {public: explicit DataProcessor(std::unique_ptr<IFileReader> reader) : reader_(std::move(reader)) {} void process(const std::string& filename) { auto data = reader_->read(filename); // 处理逻辑 }

private: std::unique_ptr<IFileReader> reader_;};


2.2. 用常量替换魔法数字

遗留代码通常包含含义不明的数字。



// 重构前if (buffer.size() > 4096) {    resizeBuffer(8192);}

// 重构后constexpr size_t kMaxBufferSize = 4096;constexpr size_t kResizeTarget = 8192;

if (buffer.size() > kMaxBufferSize) { resizeBuffer(kResizeTarget);}


3. 应对内存泄漏和不安全构造



C++遗留代码经常使用原始指针和手动内存管理。将它们替换为智能指针和RAII包装器。



3.1. 将new/delete替换为std::unique_ptr

// 旧代码(异常时有泄漏风险)void processData() {    Data* data = new Data();    // ... 使用data ...    delete data;}

// 新代码(安全)void processData() { auto data = std::make_unique<Data>(); // ... 使用data ... // 不需要delete}


3.2. 使用std::vector代替原始数组

                

博客

书籍推荐