跳转至

Effective C++进阶

📖 章节简介

本章将介绍Effective C++的进阶概念,包括运算符重载、继承与多态和模板基础。

Effective C++进阶三大支柱

上图概括了本章的三条主线:运算符重载、继承多态与模板泛型,便于建立整体学习框架。

➕ 运算符重载

1. 基本运算符重载

C++
// 运算符重载
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;

class Complex {
private:
    double real;
    double imag;

public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}

    // 重载加法运算符
    Complex operator+(const Complex& other) const {
        return Complex(real + other.real, imag + other.imag);
    }

    // 重载减法运算符
    Complex operator-(const Complex& other) const {
        return Complex(real - other.real, imag - other.imag);
    }

    // 重载乘法运算符
    // 复数乘法公式: (a+bi)(c+di) = (ac-bd) + (ad+bc)i
    Complex operator*(const Complex& other) const {
        return Complex(
            real * other.real - imag * other.imag,
            real * other.imag + imag * other.real
        );
    }

    // 重载除法运算符
    // 复数除法: (a+bi)/(c+di) = ((ac+bd) + (bc-ad)i) / (c²+d²)
    Complex operator/(const Complex& other) const {
        double denominator = other.real * other.real + other.imag * other.imag; // 分母 = c²+d²
        if (denominator == 0) {
            throw std::invalid_argument("除数为零,无法进行复数除法");
        }
        return Complex(
            (real * other.real + imag * other.imag) / denominator,
            (imag * other.real - real * other.imag) / denominator
        );
    }

    // 重载输出运算符
    friend ostream& operator<<(ostream& os, const Complex& c) {
        os << c.real;
        if (c.imag >= 0) {
            os << "+" << c.imag << "i";
        } else {
            os << c.imag << "i";
        }
        return os;
    }

    void display() const {
        cout << real;
        if (imag >= 0) {
            cout << "+" << imag << "i";
        } else {
            cout << imag << "i";
        }
        cout << endl;
    }
};

int main() {
    Complex c1(3, 4);
    Complex c2(1, 2);

    cout << "c1 = ";
    c1.display();
    cout << "c2 = ";
    c2.display();

    Complex c3 = c1 + c2;
    cout << "c1 + c2 = ";
    c3.display();

    Complex c4 = c1 - c2;
    cout << "c1 - c2 = ";
    c4.display();

    Complex c5 = c1 * c2;
    cout << "c1 * c2 = ";
    c5.display();

    Complex c6 = c1 / c2;
    cout << "c1 / c2 = ";
    c6.display();

    cout << "c3: " << c3 << endl;

    return 0;
}

2. 关系运算符重载

C++
// 关系运算符重载
#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    string name;
    int age;

public:
    Person(string n, int a) : name(n), age(a) {}

    // 重载==运算符
    bool operator==(const Person& other) const {
        return name == other.name && age == other.age;
    }

    // 重载!=运算符
    bool operator!=(const Person& other) const {
        return !(*this == other);
    }

    // 重载<运算符
    bool operator<(const Person& other) const {
        return age < other.age;
    }

    // 重载>运算符
    bool operator>(const Person& other) const {
        return age > other.age;
    }

    void display() const {
        cout << "姓名: " << name << ", 年龄: " << age << endl;
    }
};

int main() {
    Person p1("张三", 25);
    Person p2("李四", 30);
    Person p3("王五", 20);

    cout << "p1: ";
    p1.display();
    cout << "p2: ";
    p2.display();
    cout << "p1 == p2: " << (p1 == p2 ? "是" : "否") << endl;
    cout << "p1 < p3: " << (p1 < p3 ? "是" : "否") << endl;

    return 0;
}

🧬 继承与多态

1. 虚函数

C++
// 虚函数与多态
#include <iostream>
#include <memory>
using namespace std;

class Shape {
public:
    virtual ~Shape() = default;
    virtual void draw() const {
        cout << "绘制形状" << endl;
    }
    virtual double getArea() const = 0;
};

class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    void draw() const override {
        cout << "绘制圆形,半径: " << radius << endl;
    }

    double getArea() const override {
        return 3.141592653589793 * radius * radius;
    }
};

class Rectangle : public Shape {
private:
    double width;
    double height;

public:
    Rectangle(double w, double h) : width(w), height(h) {}

    void draw() const override {
        cout << "绘制矩形,宽: " << width << ", 高: " << height << endl;
    }

    double getArea() const override {
        return width * height;
    }
};

// 通过基类引用调用虚函数,运行时根据实际类型执行对应的draw和getArea
void processShape(const Shape& shape) {
    shape.draw();
    cout << "面积: " << shape.getArea() << endl;
}

int main() {
    // 使用智能指针管理多态对象,存入基类指针容器
    vector<unique_ptr<Shape>> shapes;

    shapes.push_back(make_unique<Circle>(5.0));
    shapes.push_back(make_unique<Rectangle>(4.0, 3.0));
    shapes.push_back(make_unique<Circle>(3.0));

    cout << "多态遍历形状:" << endl;
    for (const auto& shape : shapes) {
        processShape(*shape);
        cout << endl;
    }

    return 0;
}

2. 继承层次设计

C++
// 继承层次设计
#include <iostream>
#include <memory>
using namespace std;

class Animal {
public:
    virtual ~Animal() = default;
    virtual void makeSound() const {
        cout << "动物发出声音" << endl;
    }

    void eat() const {
        cout << "动物在吃东西" << endl;
    }
};

class Dog : public Animal {
private:
    string breed;

public:
    Dog(string b) : breed(b) {}

    void makeSound() const override {
        cout << "汪汪叫" << endl;
    }

    void fetch() const {
        cout << "狗去捡球" << endl;
    }

    string getBreed() const {
        return breed;
    }
};

class Cat : public Animal {
private:
    string color;

public:
    Cat(string c) : color(c) {}

    void makeSound() const override {
        cout << "喵喵叫" << endl;
    }

    void climb() const {
        cout << "猫爬树" << endl;
    }

    string getColor() const {
        return color;
    }
};

void interact(const Animal& animal) {
    animal.eat();
    animal.makeSound();
}

int main() {
    Dog dog("金毛");
    Cat cat("橘猫");

    cout << "与狗交互:" << endl;
    interact(dog);
    cout << endl;

    cout << "与猫交互:" << endl;
    interact(cat);
    cout << endl;

    return 0;
}

🎯 模板基础

1. 函数模板

C++
// 函数模板
#include <iostream>
using namespace std;

// 比较函数模板(命名为 my_max 避免与 std::max 冲突)
template <typename T>
T my_max(T a, T b) {
    return (a > b) ? a : b;
}

// 交换函数模板(命名为 my_swap 避免与 std::swap 冲突)
template <typename T>
void my_swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

// 冒泡排序模板:每轮将未排序部分的最大值"冒泡"到末尾
template <typename T>
void sortArray(T* arr, int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) { // size-i-1: 已排好的尾部无需再比较
            if (arr[j] > arr[j + 1]) {
                my_swap(arr[j], arr[j + 1]); // 相邻元素逆序则交换
            }
        }
    }
}

int main() {
    int intArr[] = {64, 34, 25, 12, 22, 11, 90};
    double doubleArr[] = {3.14, 1.59, 2.71, 0.5, 0.25, 0.3};

    cout << "整数数组排序前:" << endl;
    for (int i = 0; i < 7; i++) {
        cout << intArr[i] << " ";
    }
    cout << endl;

    sortArray(intArr, 7);

    cout << "整数数组排序后:" << endl;
    for (int i = 0; i < 7; i++) {
        cout << intArr[i] << " ";
    }
    cout << endl;

    cout << "\n浮点数排序前:" << endl;
    for (int i = 0; i < 6; i++) {
        cout << doubleArr[i] << " ";
    }
    cout << endl;

    sortArray(doubleArr, 6);

    cout << "浮点数排序后:" << endl;
    for (int i = 0; i < 6; i++) {
        cout << doubleArr[i] << " ";
    }
    cout << endl;

    cout << "\n最大整数: " << my_max(10, 20) << endl;
    cout << "最大浮点数: " << my_max(3.14, 1.59) << endl;

    return 0;
}

2. 类模板

C++
// 类模板
#include <iostream>
#include <vector>
using namespace std;

template <typename T>
class Stack {
private:
    vector<T> data;

public:
    Stack() = default;

    void push(const T& value) {
        data.push_back(value);
    }

    T pop() {
        if (data.empty()) {
            throw runtime_error("栈为空");
        }
        T value = data.back();
        data.pop_back();
        return value;
    }

    T peek() const {
        if (data.empty()) {
            throw runtime_error("栈为空");
        }
        return data.back();
    }

    bool isEmpty() const {
        return data.empty();
    }

    int size() const {
        return data.size();
    }
};

int main() {
    Stack<int> intStack;

    // 入栈
    intStack.push(10);
    intStack.push(20);
    intStack.push(30);

    cout << "栈大小: " << intStack.size() << endl;

    // 出栈
    while (!intStack.isEmpty()) {
        cout << "出栈: " << intStack.pop() << endl;
    }

    cout << "\n栈已空" << endl;

    return 0;
}

💡 最佳实践

1. 运算符重载

C++
// 运算符重载
int main() {
    // ✅ 好的做法:保持运算符的语义
    class GoodClass {
    public:
        GoodClass(int value) : data(value) {}

        GoodClass operator+(const GoodClass& other) const {
            return GoodClass(data + other.data);
        }

        int getData() const { return data; }

    private:
        int data;
    };

    // ❌ 不好的做法:改变运算符的语义
    class BadClass {
    public:
        BadClass(int value) : data(value) {}

        BadClass operator+(const BadClass& other) const {
            return BadClass(data - other.data);  // 加法变减法
        }

        int getData() const { return data; }

    private:
        int data;
    };

    return 0;
}

2. 模板设计

C++
// 模板设计

// ✅ 好的做法:使用类型萃取
template <typename T>
void process(T value) {
    cout << "处理: " << value << endl;
}

// ❌ 不好的做法:不检查类型
template <typename T>
void processBad(T value) {
    if (sizeof(T) > 4) {
        // 特殊处理
        cout << "大对象" << endl;
    } else {
        // 普通处理
        cout << "小对象" << endl;
    }
}

int main() {
    process(42);
    process(3.14);
    return 0;
}

📝 练习题

基础题

  1. 如何重载运算符?
  2. 虚函数有什么作用?
  3. 模板有什么优势?

进阶题

  1. 实现一个完整的复数类。
  2. 设计一个合理的继承层次。
  3. 实现一个模板容器类。

实践题

  1. 创建一个矩阵运算库。
  2. 实现一个智能指针包装器。
  3. 构建一个简单的序列化系统。

📚 推荐阅读

🔗 下一步

13-STL深入剖析 - 深入学习STL容器、算法与迭代器。