C++ 面向对象编程完整教程

1. 面向对象编程基础

什么是面向对象编程(OOP)?

  • 将数据和操作数据的函数捆绑在一起(封装)
  • 通过抽象、继承和多态实现代码复用和灵活性
  • 模拟现实世界的对象关系

OOP四大特性

  • 封装(Encapsulation)
  • 继承(Inheritance)
  • 多态(Polymorphism)
  • 抽象(Abstraction)

2. 类与对象

类(Class):用户自定义的数据类型,包含数据成员和成员函数。

对象(Object):类的实例。

示例

#include <iostream>
#include <string>

// 定义类
class Person {
private:
    // 私有数据成员
    std::string name;
    int age;

public:
    // 构造函数
    Person(std::string n, int a) {
        name = n;
        age = a;
    }

    // 成员函数
    void introduce() {
        std::cout << "我叫" << name << ",今年" << age << "岁。" << std::endl;
    }

    // Getter和Setter
    int getAge() const {
        return age;
    }

    void setAge(int a) {
        if (a > 0) age = a;  // 数据验证
    }
};

int main() {
    // 创建对象
    Person p1("张三", 25);
    Person p2("李四", 30);

    // 调用成员函数
    p1.introduce();  // 输出: 我叫张三,今年25岁。
    p2.introduce();  // 输出: 我叫李四,今年30岁。

    // 修改年龄
    p1.setAge(26);
    std::cout << "修改后张三的年龄: " << p1.getAge() << std::endl;

    return 0;
}

3. 访问控制

访问修饰符

  • private:只能被类内部访问
  • public:可以被任何外部代码访问
  • protected:可以被类内部和子类访问

示例

class BankAccount {
private:
    double balance;  // 私有成员,外部无法直接访问

public:
    // 公有接口
    void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }

    double getBalance() const {
        return balance;
    }
};

4. 构造函数与析构函数

构造函数(Constructor)

  • 与类同名,无返回类型
  • 创建对象时自动调用
  • 可以重载

析构函数(Destructor)

  • 与类同名,前面加~
  • 对象销毁时自动调用
  • 不接受参数,不能重载

示例

class Resource {
private:
    int* data;

public:
    // 默认构造函数
    Resource() {
        data = new int[100];  // 分配资源
        std::cout << "资源已分配" << std::endl;
    }

    // 带参数的构造函数
    Resource(int size) {
        data = new int[size];
        std::cout << "分配了大小为" << size << "的资源" << std::endl;
    }

    // 析构函数
    ~Resource() {
        delete[] data;  // 释放资源
        std::cout << "资源已释放" << std::endl;
    }
};

int main() {
    {
        Resource r1;         // 调用默认构造函数
        Resource r2(200);    // 调用带参数的构造函数
    } // r1和r2超出作用域,自动调用析构函数

    return 0;
}

5. 继承

继承:允许一个类(子类)继承另一个类(父类)的属性和方法。

语法

class 子类 : 访问修饰符 父类 {
    // 子类成员
};

示例

// 基类
class Animal {
protected:
    std::string name;

public:
    Animal(std::string n) : name(n) {}

    void eat() {
        std::cout << name << "正在吃东西" << std::endl;
    }

    virtual void makeSound() {  // 虚函数
        std::cout << name << "发出声音" << std::endl;
    }
};

// 派生类
class Dog : public Animal {
public:
    Dog(std::string n) : Animal(n) {}

    void makeSound() override {  // 重写虚函数
        std::cout << name << "汪汪叫" << std::endl;
    }

    void fetch() {
        std::cout << name << "正在捡球" << std::endl;
    }
};

// 派生类
class Cat : public Animal {
public:
    Cat(std::string n) : Animal(n) {}

    void makeSound() override {  // 重写虚函数
        std::cout << name << "喵喵叫" << std::endl;
    }
};

int main() {
    Dog dog("小白");
    Cat cat("小花");

    dog.eat();       // 输出: 小白正在吃东西
    dog.makeSound(); // 输出: 小白汪汪叫
    dog.fetch();     // 输出: 小白正在捡球

    cat.eat();       // 输出: 小花正在吃东西
    cat.makeSound(); // 输出: 小花喵喵叫

    return 0;
}

6. 多态

多态:通过基类指针或引用调用派生类的重写函数。

实现条件

  • 基类中使用virtual关键字声明虚函数
  • 派生类中使用override关键字重写虚函数

示例

int main() {
    Animal* animal1 = new Dog("小白");
    Animal* animal2 = new Cat("小花");

    animal1->makeSound();  // 输出: 小白汪汪叫
    animal2->makeSound();  // 输出: 小花喵喵叫

    delete animal1;
    delete animal2;

    return 0;
}

7. 抽象类与纯虚函数

纯虚函数:在基类中声明但没有实现的虚函数。

抽象类:包含至少一个纯虚函数的类,不能实例化。

示例

// 抽象类
class Shape {
public:
    // 纯虚函数
    virtual double area() const = 0;

    // 普通虚函数
    virtual void draw() const {
        std::cout << "绘制形状" << std::endl;
    }
};

// 派生类
class Circle : public Shape {
private:
    double radius;

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

    double area() const override {
        return 3.14159 * radius * radius;
    }

    void draw() const override {
        std::cout << "绘制圆形" << std::endl;
    }
};

int main() {
    // Shape s;  // 错误!不能实例化抽象类

    Shape* circle = new Circle(5.0);
    std::cout << "圆的面积: " << circle->area() << std::endl;  // 输出: 78.5397
    circle->draw();  // 输出: 绘制圆形

    delete circle;
    return 0;
}

8. 运算符重载

运算符重载:允许自定义运算符对类对象的行为。

示例:重载+运算符实现复数加法

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);
    }

    // 重载<<运算符(友元函数)
    friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
        os << c.real;
        if (c.imag >= 0) os << "+";
        os << c.imag << "i";
        return os;
    }
};

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

    Complex c3 = c1 + c2;  // 调用重载的+运算符

    std::cout << "c1 = " << c1 << std::endl;  // 输出: c1 = 3+4i
    std::cout << "c2 = " << c2 << std::endl;  // 输出: c2 = 1-2i
    std::cout << "c1 + c2 = " << c3 << std::endl;  // 输出: c1 + c2 = 4+2i

    return 0;
}

9. 拷贝构造函数与赋值运算符

拷贝构造函数:创建新对象时使用另一个对象初始化。

赋值运算符:将一个对象的值赋给另一个已存在的对象。

示例

class MyClass {
private:
    int* data;
    int size;

public:
    // 构造函数
    MyClass(int s) : size(s) {
        data = new int[size];
        for (int i = 0; i < size; i++) {
            data[i] = i;
        }
    }

    // 拷贝构造函数
    MyClass(const MyClass& other) : size(other.size) {
        data = new int[size];
        for (int i = 0; i < size; i++) {
            data[i] = other.data[i];
        }
    }

    // 赋值运算符
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {  // 防止自我赋值
            delete[] data;     // 释放原有资源

            size = other.size;
            data = new int[size];
            for (int i = 0; i < size; i++) {
                data[i] = other.data[i];
            }
        }
        return *this;
    }

    // 析构函数
    ~MyClass() {
        delete[] data;
    }
};

10. 智能指针与RAII

RAII(资源获取即初始化):利用对象生命周期管理资源。

智能指针:自动管理动态内存的对象。

示例

#include <memory>

class Resource {
public:
    Resource() { std::cout << "资源分配" << std::endl; }
    ~Resource() { std::cout << "资源释放" << std::endl; }
    void use() { std::cout << "使用资源" << std::endl; }
};

int main() {
    // 使用unique_ptr管理资源
    {
        std::unique_ptr<Resource> res = std::make_unique<Resource>();
        res->use();
    } // 离开作用域时自动释放资源

    // 使用shared_ptr共享资源
    {
        std::shared_ptr<Resource> res1 = std::make_shared<Resource>();
        {
            std::shared_ptr<Resource> res2 = res1;  // 引用计数+1
            res2->use();
        } // res2销毁,引用计数-1
        res1->use();
    } // res1销毁,引用计数为0,释放资源

    return 0;
}

11. 面向对象设计原则

SOLID原则

  1. 单一职责原则(SRP):一个类只负责一项职责
  2. 开闭原则(OCP):对扩展开放,对修改关闭
  3. 里氏替换原则(LSP):子类可以替换父类而不影响程序
  4. 接口隔离原则(ISP):客户端不应该依赖它不需要的接口
  5. 依赖倒置原则(DIP):高层模块不依赖低层模块,二者都依赖抽象

设计模式

  • 单例模式(Singleton)
  • 工厂模式(Factory)
  • 观察者模式(Observer)
  • 装饰器模式(Decorator)等

12. 综合示例:银行账户系统

#include <iostream>
#include <string>
#include <vector>
#include <memory>

// 抽象基类:账户
class Account {
protected:
    std::string accountNumber;
    double balance;

public:
    Account(std::string number, double initialBalance)
        : accountNumber(number), balance(initialBalance) {}

    // 纯虚函数:计算利息
    virtual double calculateInterest() = 0;

    // 存款
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            std::cout << "存款成功,当前余额: " << balance << std::endl;
        }
    }

    // 取款
    virtual bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            std::cout << "取款成功,当前余额: " << balance << std::endl;
            return true;
        }
        std::cout << "取款失败,余额不足" << std::endl;
        return false;
    }

    // 获取账户信息
    virtual void displayInfo() const {
        std::cout << "账户号: " << accountNumber << ", 余额: " << balance << std::endl;
    }

    virtual ~Account() {}
};

// 储蓄账户
class SavingsAccount : public Account {
private:
    double interestRate;

public:
    SavingsAccount(std::string number, double initialBalance, double rate)
        : Account(number, initialBalance), interestRate(rate) {}

    double calculateInterest() override {
        return balance * interestRate;
    }

    void displayInfo() const override {
        Account::displayInfo();
        std::cout << "储蓄账户,利率: " << interestRate << std::endl;
    }
};

// 支票账户
class CheckingAccount : public Account {
private:
    double overdraftLimit;

public:
    CheckingAccount(std::string number, double initialBalance, double limit)
        : Account(number, initialBalance), overdraftLimit(limit) {}

    double calculateInterest() override {
        return 0;  // 支票账户通常没有利息
    }

    bool withdraw(double amount) override {
        if (amount > 0 && amount <= (balance + overdraftLimit)) {
            balance -= amount;
            std::cout << "取款成功,当前余额: " << balance << std::endl;
            return true;
        }
        std::cout << "取款失败,超出透支限额" << std::endl;
        return false;
    }

    void displayInfo() const override {
        Account::displayInfo();
        std::cout << "支票账户,透支限额: " << overdraftLimit << std::endl;
    }
};

// 银行类
class Bank {
private:
    std::string name;
    std::vector<std::shared_ptr<Account>> accounts;

public:
    Bank(std::string bankName) : name(bankName) {}

    // 添加账户
    void addAccount(std::shared_ptr<Account> account) {
        accounts.push_back(account);
        std::cout << "账户添加成功" << std::endl;
    }

    // 显示所有账户信息
    void displayAllAccounts() const {
        std::cout << "银行名称: " << name << std::endl;
        std::cout << "所有账户信息:" << std::endl;
        for (const auto& account : accounts) {
            account->displayInfo();
            std::cout << "利息: " << account->calculateInterest() << std::endl;
            std::cout << "------------------------" << std::endl;
        }
    }
};

int main() {
    // 创建银行
    Bank bank("ABC银行");

    // 创建账户
    auto savings = std::make_shared<SavingsAccount>("S1001", 1000.0, 0.03);
    auto checking = std::make_shared<CheckingAccount>("C2001", 2000.0, 500.0);

    // 添加账户到银行
    bank.addAccount(savings);
    bank.addAccount(checking);

    // 进行交易
    savings->deposit(500.0);
    checking->withdraw(2300.0);

    // 显示所有账户信息
    bank.displayAllAccounts();

    return 0;
}

总结

面向对象编程核心概念

  • 类与对象:封装数据和行为
  • 继承:实现代码复用和层次结构
  • 多态:通过虚函数实现运行时绑定
  • 抽象:定义接口而不实现具体细节
  • 内存管理:智能指针和RAII

最佳实践

  • 优先使用组合而非继承
  • 依赖接口而非实现
  • 遵循SOLID设计原则
  • 使用智能指针避免内存泄漏

通过掌握面向对象编程,你可以设计出更模块化、可维护和可扩展的C++程序。

1 条评论

  • @ 2025-5-29 15:58:40

    🧱 C++ 面向对象编程(OOP)完整教程


    📘 什么是面向对象编程?

    面向对象编程(Object-Oriented Programming, OOP) 是一种以“对象”为中心的编程范式,强调将数据和操作封装在一起。C++ 支持四大核心特性:

    1. 封装(Encapsulation)
    2. 继承(Inheritance)
    3. 多态(Polymorphism)
    4. 抽象(Abstraction)

    🧩 1. 类与对象(Class & Object)

    📦 类(Class)

    类是对象的模板或蓝图,定义了对象的属性和行为。

    // 定义一个简单的类
    class Car {
    private:
        std::string color;     // 私有成员变量
        int speed;
    
    public:
        // 构造函数(初始化)
        Car(std::string c, int s) : color(c), speed(s) {}
    
        // 成员函数(方法)
        void accelerate(int increment) {
            speed += increment;
            std::cout << "加速后速度: " << speed << " km/h\n";
        }
    
        void showInfo() {
            std::cout << "颜色: " << color << ", 当前速度: " << speed << " km/h\n";
        }
    };
    

    🧍 对象(Object)

    对象是类的一个实例。

    int main() {
        Car myCar("红色", 60);   // 创建对象
        myCar.showInfo();        // 输出信息
        myCar.accelerate(20);    // 加速
        return 0;
    }
    

    📌 输出示例:

    颜色: 红色, 当前速度: 60 km/h
    加速后速度: 80 km/h
    

    🔒 2. 封装(Encapsulation)

    封装就是把数据设为私有(private),并通过公有方法访问它们。

    示例:使用 getter 和 setter

    class Person {
    private:
        std::string name;
        int age;
    
    public:
        // 设置名字
        void setName(std::string n) { name = n; }
    
        // 获取名字
        std::string getName() { return name; }
    
        // 设置年龄(带验证)
        void setAge(int a) {
            if (a > 0) age = a;
        }
    
        int getAge() { return age; }
    };
    
    int main() {
        Person p;
        p.setName("张三");
        p.setAge(25);
    
        std::cout << p.getName() << " 的年龄是 " << p.getAge() << " 岁。\n";
        return 0;
    }
    

    📌 输出示例:

    张三 的年龄是 25 岁。
    

    🧬 3. 继承(Inheritance)

    继承允许我们基于一个已有的类创建新类,称为派生类(子类)。

    示例:基类 Animal 和派生类 Dog

    #include <iostream>
    using namespace std;
    
    class Animal {
    public:
        void eat() { cout << "动物在吃东西。\n"; }
    };
    
    // 派生类 Dog 继承自 Animal
    class Dog : public Animal {
    public:
        void bark() { cout << "狗在汪汪叫!\n"; }
    };
    
    int main() {
        Dog myDog;
        myDog.eat();      // 来自父类
        myDog.bark();     // 子类自己的方法
        return 0;
    }
    

    📌 输出示例:

    动物在吃东西。
    狗在汪汪叫!
    

    🎭 4. 多态(Polymorphism)

    多态是指同一个接口可以有不同的实现方式。通常通过虚函数(virtual)实现。

    示例:基类 Shape 与派生类 Circle、Rectangle

    #include <iostream>
    using namespace std;
    
    class Shape {
    public:
        virtual void draw() = 0;   // 纯虚函数,使 Shape 成为抽象类
    };
    
    class Circle : public Shape {
    public:
        void draw() override {
            cout << "画一个圆形 ✅\n";
        }
    };
    
    class Rectangle : public Shape {
    public:
        void draw() override {
            cout << "画一个矩形 🔲\n";
        }
    };
    
    int main() {
        Shape* shapes[2];
        shapes[0] = new Circle();
        shapes[1] = new Rectangle();
    
        for (int i = 0; i < 2; ++i)
            shapes[i]->draw();
    
        delete shapes[0];
        delete shapes[1];
    
        return 0;
    }
    

    📌 输出示例:

    画一个圆形 ✅
    画一个矩形 🔲
    

    🧠 5. 抽象(Abstraction)

    抽象隐藏复杂的实现细节,只展示必要的接口。

    class BankAccount {
    private:
        double balance;
    
    public:
        BankAccount(double initial) : balance(initial) {}
    
        void deposit(double amount) {
            if (amount > 0) balance += amount;
        }
    
        void withdraw(double amount) {
            if (amount > 0 && amount <= balance)
                balance -= amount;
        }
    
        double getBalance() const {
            return balance;
        }
    };
    
    int main() {
        BankAccount acc(1000);
        acc.deposit(500);
        acc.withdraw(200);
    
        cout << "账户余额: $" << acc.getBalance() << endl;
        return 0;
    }
    

    📌 输出示例:

    账户余额: $1300
    

    📚 6. 构造函数与析构函数

    构造函数:对象创建时自动调用

    析构函数:对象销毁时自动调用

    class MyClass {
    public:
        MyClass() { cout << "构造函数被调用!🔥\n"; }
        ~MyClass() { cout << "析构函数被调用!💥\n"; }
    };
    
    int main() {
        MyClass obj;   // 构造函数调用
        return 0;      // 析构函数调用
    }
    

    📌 输出示例:

    构造函数被调用!🔥
    析构函数被调用!💥
    

    🔄 7. 运算符重载(Operator Overloading)

    允许你为运算符赋予新的含义。

    class Complex {
    private:
        double real, imag;
    
    public:
        Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
        // 重载 + 运算符
        Complex operator+(const Complex& other) {
            return Complex(real + other.real, imag + other.imag);
        }
    
        void print() {
            cout << real << " + " << imag << "i" << endl;
        }
    };
    
    int main() {
        Complex c1(3, 4), c2(1, 2);
        Complex c3 = c1 + c2;
        c3.print();  // 输出:4 + 6i
        return 0;
    }
    

    📌 总结图解

    特性 描述
    封装 数据私有化,提供公共方法访问
    继承 子类继承父类的属性和方法
    多态 同一接口,不同实现
    抽象 隐藏复杂细节,只暴露必要接口

    🧠 学习建议

    ✅ 先掌握类与对象的基本语法
    ✅ 熟悉封装和访问控制(private, protected, public
    ✅ 掌握继承机制和虚函数实现多态
    ✅ 使用抽象类设计良好的程序结构


    📚 参考资料推荐

    • 《C++ Primer》——Stanley B. Lippman
    • 《Effective C++》——Scott Meyers
    • 《C++面向对象程序设计》——谭浩强
    • CppReference.com

    🧰 工具推荐

    • 编译器:GCC / Clang / MSVC
    • IDE:Visual Studio / CLion / Code::Blocks
    • 调试工具:GDB / Visual Studio Debugger
    • 1