C++ 常用惯用法大全

C++ 作为一门多范式编程语言,拥有丰富的编程惯用法(idioms)。这些惯用法是经验丰富的 C++ 程序员在实践中总结出的高效解决方案。以下是 C++ 中最重要和常用的惯用法:

1. RAII (Resource Acquisition Is Initialization)

资源获取即初始化是 C++ 最核心的惯用法。

class FileHandle {
public:
    FileHandle(const char* filename, const char* mode) {
        file = fopen(filename, mode);
        if (!file) throw std::runtime_error("File open failed");
    }
    
    ~FileHandle() {
        if (file) fclose(file);
    }
    
    // 禁用拷贝
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;
    
    // 可以添加移动语义
    FileHandle(FileHandle&& other) noexcept : file(other.file) {
        other.file = nullptr;
    }
    
    // 其他成员函数...
    
private:
    FILE* file;
};

void useFile() {
    FileHandle f("test.txt", "r"); // 资源在构造函数中获取
    // 使用文件...
    // 资源在析构函数中自动释放
}

2. PIMPL (Pointer to Implementation)

指向实现的指针,用于接口与实现分离。

// widget.h
class Widget {
public:
    Widget();
    ~Widget();
    void doSomething();
private:
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};

// widget.cpp
struct Widget::Impl {
    int data;
    std::string name;
    void helperFunction() { /*...*/ }
};

Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
void Widget::doSomething() { pImpl->helperFunction(); }

3. CRTP (Curiously Recurring Template Pattern)

奇异递归模板模式,用于静态多态。

template <typename Derived>
class Base {
public:
    void interface() {
        static_cast<Derived*>(this)->implementation();
    }
};

class Derived : public Base<Derived> {
public:
    void implementation() {
        std::cout << "Derived implementation\n";
    }
};

template <typename T>
void useBase(Base<T>& base) {
    base.interface();
}

4. Type Erasure

类型擦除,用于运行时多态而不需要继承。

class AnyDrawable {
    struct Concept {
        virtual ~Concept() = default;
        virtual void draw() const = 0;
    };
    
    template <typename T>
    struct Model : Concept {
        Model(T value) : value(std::move(value)) {}
        void draw() const override { value.draw(); }
        T value;
    };
    
    std::unique_ptr<Concept> pimpl;
    
public:
    template <typename T>
    AnyDrawable(T value) : pimpl(std::make_unique<Model<T>>(std::move(value))) {}
    
    void draw() const { pimpl->draw(); }
};

// 使用
struct Circle { void draw() const { std::cout << "Drawing circle\n"; } };
struct Square { void draw() const { std::cout << "Drawing square\n"; } };

void drawAll(const std::vector<AnyDrawable>& items) {
    for (const auto& item : items) {
        item.draw();
    }
}

5. SFINAE (Substitution Failure Is Not An Error)

替换失败不是错误,用于模板元编程。

template <typename T>
auto print(const T& value) -> decltype(std::cout << value, void()) {
    std::cout << value << std::endl;
}

void print(...) {
    std::cout << "[object cannot be printed]" << std::endl;
}

// C++17 后可以用 constexpr if 简化
template <typename T>
void print(const T& value) {
    if constexpr (std::is_ostreamable_v<T>) {
        std::cout << value << std::endl;
    } else {
        std::cout << "[object cannot be printed]" << std::endl;
    }
}

6. Expression Templates

表达式模板,用于优化数值计算。

template <typename E>
class VecExpression {
public:
    double operator[](size_t i) const { return static_cast<const E&>(*this)[i]; }
    size_t size() const { return static_cast<const E&>(*this).size(); }
};

class Vec : public VecExpression<Vec> {
    std::vector<double> data;
public:
    double operator[](size_t i) const { return data[i]; }
    size_t size() const { return data.size(); }
    // ... 其他成员函数
};

template <typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2>> {
    const E1& u;
    const E2& v;
public:
    VecSum(const E1& u, const E2& v) : u(u), v(v) {}
    double operator[](size_t i) const { return u[i] + v[i]; }
    size_t size() const { return u.size(); }
};

template <typename E1, typename E2>
VecSum<E1, E2> operator+(const VecExpression<E1>& u, const VecExpression<E2>& v) {
    return VecSum<E1, E2>(static_cast<const E1&>(u), static_cast<const E2&>(v));
}

7. NVI (Non-Virtual Interface)

非虚接口,用于模板方法模式。

class Base {
public:
    void execute() {
        preExecute();
        doExecute();
        postExecute();
    }
    virtual ~Base() = default;
private:
    virtual void doExecute() = 0;
    void preExecute() { /* 通用前置处理 */ }
    void postExecute() { /* 通用后置处理 */ }
};

class Derived : public Base {
private:
    void doExecute() override {
        // 具体实现
    }
};

8. Copy-and-Swap

拷贝并交换,用于实现异常安全的赋值操作。

class Resource {
    int* data;
    size_t size;
    
    void swap(Resource& other) noexcept {
        using std::swap;
        swap(data, other.data);
        swap(size, other.size);
    }
    
public:
    Resource(size_t size) : size(size), data(new int[size]) {}
    
    ~Resource() { delete[] data; }
    
    Resource(const Resource& other) : size(other.size), data(new int[other.size]) {
        std::copy(other.data, other.data + size, data);
    }
    
    Resource& operator=(Resource other) {
        swap(other);
        return *this;
    }
    
    Resource(Resource&& other) noexcept : Resource() {
        swap(other);
    }
};

9. Rule of Zero/Five

零/五法则,现代C++资源管理准则。

// Rule of Zero - 使用智能成员变量,不需要自定义特殊成员函数
class RuleOfZero {
    std::unique_ptr<int> ptr;
    std::vector<int> data;
    std::string name;
    // 编译器生成的默认特殊成员函数足够好
};

// Rule of Five - 需要管理资源时,定义全部五个特殊成员函数
class RuleOfFive {
    int* data;
    size_t size;
    
public:
    explicit RuleOfFive(size_t size = 0) : size(size), data(size ? new int[size] : nullptr) {}
    
    ~RuleOfFive() { delete[] data; }
    
    RuleOfFive(const RuleOfFive& other) : size(other.size), data(size ? new int[size] : nullptr) {
        std::copy(other.data, other.data + size, data);
    }
    
    RuleOfFive(RuleOfFive&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
    }
    
    RuleOfFive& operator=(RuleOfFive other) {
        swap(*this, other);
        return *this;
    }
    
    friend void swap(RuleOfFive& first, RuleOfFive& second) noexcept {
        using std::swap;
        swap(first.data, second.data);
        swap(first.size, second.size);
    }
};

10. Tag Dispatching

标签分发,用于基于类型特性的函数重载。

namespace tags {
    struct contiguous {};
    struct non_contiguous {};
}

template <typename Iterator>
void algorithm_impl(Iterator first, Iterator last, tags::contiguous) {
    std::cout << "Optimized version for contiguous memory\n";
}

template <typename Iterator>
void algorithm_impl(Iterator first, Iterator last, tags::non_contiguous) {
    std::cout << "Generic version for non-contiguous memory\n";
}

template <typename Iterator>
void algorithm(Iterator first, Iterator last) {
    using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
    
    if constexpr (std::is_same_v<iterator_category, std::random_access_iterator_tag>) {
        algorithm_impl(first, last, tags::contiguous{});
    } else {
        algorithm_impl(first, last, tags::non_contiguous{});
    }
}

11. Policy-Based Design

基于策略的设计,用于高度可配置的类。

template <typename OutputPolicy, typename LanguagePolicy>
class HelloWorld : private OutputPolicy, private LanguagePolicy {
public:
    void run() {
        OutputPolicy::output(LanguagePolicy::getMessage());
    }
};

class ConsoleOutput {
protected:
    void output(const std::string& msg) {
        std::cout << msg << std::endl;
    }
};

class English {
protected:
    std::string getMessage() {
        return "Hello, World!";
    }
};

class French {
protected:
    std::string getMessage() {
        return "Bonjour, le monde!";
    }
};

// 使用
HelloWorld<ConsoleOutput, English> hello;
hello.run(); // 输出: Hello, World!

12. Barton-Nackman Trick

Barton-Nackman技巧,用于在模板类中定义友元运算符。

template <typename T>
class Wrapper {
    T value;
public:
    Wrapper(const T& v) : value(v) {}
    
    friend bool operator==(const Wrapper& lhs, const Wrapper& rhs) {
        return lhs.value == rhs.value;
    }
    
    friend std::ostream& operator<<(std::ostream& os, const Wrapper& w) {
        return os << w.value;
    }
};

13. Virtual Constructor (Clone)

虚拟构造函数,用于多态复制对象。

class Shape {
public:
    virtual ~Shape() = default;
    virtual std::unique_ptr<Shape> clone() const = 0;
    virtual void draw() const = 0;
};

class Circle : public Shape {
    double radius;
public:
    explicit Circle(double r) : radius(r) {}
    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Circle>(*this);
    }
    void draw() const override {
        std::cout << "Circle with radius " << radius << std::endl;
    }
};

void cloneAndDraw(const Shape& shape) {
    auto cloned = shape.clone();
    cloned->draw();
}

14. Shrink-to-fit

收缩至合适大小,用于优化容器内存使用。

std::vector<int> v = {1, 2, 3, 4, 5};
v.erase(v.begin() + 2); // 删除第三个元素
v.shrink_to_fit(); // 释放未使用的内存

// 或者使用交换技巧(C++11前)
std::vector<int>(v).swap(v);

15. Empty Base Optimization (EBO)

空基类优化,用于无状态策略类的空间优化。

struct Empty {}; // 空类

// 没有EBO
struct NoEBO {
    Empty e;
    int x;
}; // 通常 sizeof(NoEBO) > sizeof(int)

// 使用EBO
template <typename Base>
struct WithEBO : Base {
    int x;
}; // sizeof(WithEBO<Empty>) == sizeof(int)

这些惯用法展示了 C++ 的强大功能和灵活性。掌握这些惯用法可以帮助你写出更高效、更可维护的 C++ 代码。根据具体场景选择合适的惯用法是成为高级 C++ 程序员的关键。

作者:(美)默里 著,王昕 译 出版日期:2004-2-1 出版社:其它 页数:208 ISBN:9787508319124 文件格式:PDF 书籍简介 本书第1版荣获美国“软件开发”杂志评选的1996年图书震撼大奖(Jolt Award),中文版自2000年推出以来,经久不衰,获得了读者的充分肯定和高度评价。 第2版与第1版相比,在章节安排上有以下改变。增加了两章:“对象的创建与使用”和“C++中的C”。前者与“对象导言”实际上是第1版“对象的演化”一 章的彻底重写,增加了近几年面向对象方和编程方的最新研究与实践的丰硕成果;后者的添加使不熟悉C的读者可以直接使用本书。删去了四章:“输入输出流 介绍”、“多重继承”、“异常处理”和“运行时类型识别”,删去的内容均为C++中较复杂的主题,作者将它们连同C++标准完成后增加的一些内容放到本书 的第2卷中,使本书的第1卷内容显得更加集中,可以供不同程度的读者选择阅读。需要强调的是,第2版的改变不仅体现在这些章节的调整上,更多的改变体现在 每一章的字里行间,包括例子的调整和练习的补充。与众不同的精心选材和认真推敲的叙述使得第2版更趋成熟。 本书是C++领域内一本权威的著作,书中的内容、讲授方、例子和练习既适合课堂教学,又适合读者自学。无论是高等院校计算机及相关专业的学生,还是业界的从业人员,以及广大的计算机爱好者,都可从阅读本书中获益。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值