c++ 中的多态,封装,继承学习

面向对象编程(OOP)的三大核心特性——封装、继承和多态

基础概念:类与对象

  • 类:是一个大类的抽象集合,如(动物,汽车),它定义了这个类的共有属性(变量)和行为(函数)。如汽车可以包含能源,颜色等变量以及启动,刹车等函数。

  • 对象:是类中集合的一个具体实现,如一辆黑色特斯拉电车

封装

        封装的核心思想将数据和对数据的操作封装成一个类,只能通过接口进行访问

  1. 实现机制:在C++中,封装主要通过以及三种访问修饰符来实现:

    • private:私有成员,仅在类内部可以访问。

    • protected:保护成员,可在类内部派生类中访问。

    • public:公有成员,任何地方都可以访问。

  2. 核心目的与好处

    • 数据保护与安全:将关键数据成员设为private,防止外部代码直接随意修改,保证了对象内部状态的一致性和合法性。外部只能通过类提供的公共接口(公有成员函数)来访问或修改数据,接口中可以加入必要的验证逻辑。

    • 隐藏实现细节:使用者只需知道类提供了哪些接口以及接口的用途,而无需关心内部复杂的实现。这使得代码更易于使用和维护。

    • 降低耦合度:模块之间通过清晰的接口交互,减少了相互依赖,使得修改类的内部实现不会影响到使用它的其他代码。

class BankAccount {
private: // 数据被隐藏和保护
    std::string accountNumber;
    double balance;

public: // 公开的接口
    BankAccount(std::string accNum, double initialBalance) 
      : accountNumber(accNum), balance(initialBalance) {}

    // 通过公共方法操作数据,确保安全性
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    double getBalance() const { // 提供读取接口
        return balance;
    }
    // 无法直接从外部设置账户号和余额,保证了数据安全
};

继承

        继承即依据一个已有的类建立一个新的类,新的类具有原来的类的属性和行为,并且可以增加自己特有的属性和行为。。其中,继承方式决定了基类成员在派生类中的访问权限,主要分为publicprotectedprivate三种。

基类中的原始访问权限

在 public继承后

在 protected继承后

在 private继承后

public

public

protected

private

protected

protected

protected

private

private

不可见

不可见

不可见

        在内存中,派生类对象包含其基类的成员副本(基类部分通常位于对象内存布局的前部),然后才是派生类自己的新成员。这使得基类指针或引用可以指向派生类对象。

// 基类
class Animal {
public:
    void eat() { std::cout << "Eating...\n"; }
};

// 派生类 (public继承)
class Dog : public Animal { 
public:
    void bark() { std::cout << "Woof!\n"; }
};

int main() {
    Dog myDog;
    myDog.eat();  // 继承自Animal的方法
    myDog.bark(); // Dog自己的方法
    return 0;
}

        

  • public继承:基类的接口(public)和受保护的部分(protected完全传递给派生类及其后代。

  • protected继承:基类的接口被"收回"到派生类的家族内部(变为protected),只能在家族内传递,不对外公开。

  • private继承:基类的所有都被"吸收"为派生类的私有实现细节(变为private),传递就此终止,后代也无法使用。

选择建议

  • 绝大多数情况下,应使用 public继承,因为它准确地表达了"是一个"的逻辑关系,符合面向对象的设计原则。

  • 当需要复用代码但又想隐藏基类接口时,优先考虑使用组合(将一个类的对象作为成员),这通常比privateprotected继承更灵活、更清晰。

  • 仅在非常特殊的情况下,例如需要重写基类的虚函数或访问其保护成员,且不希望暴露基类接口时,才考虑使用private继承。protected继承在实践中极为罕见。

多态        

        多态意为"多种形态"。它允许使用统一的接口来处理不同类型的对象,从而根据对象的实际类型执行不同的操作。多态分为静态多态和动态多态。

  1. 静态多态(编译时多态)

    在编译期就确定了具体要调用的函数。

    • 函数重载:在同一作用域内,函数名相同但参数列表不同(类型、个数、顺序)。

    • 运算符重载:赋予已有运算符新的功能,用于自定义类型。

    • 模板:编写与类型无关的通用代码。

  2. 动态多态(运行时多态)

    在程序运行时才能确定调用哪个函数。这是通过虚函数实现的。

    • 虚函数:在基类中使用virtual关键字声明的成员函数。派生类中可以对其进行重写(使用override关键字是良好实践)。

    • 实现机制:虚函数表(vtable)和虚表指针(vptr)

      • 编译器会为每个包含虚函数的类创建一个虚函数表,表中存放了该类所有虚函数的地址。

      • 每个该类对象内部都有一个隐含的虚表指针,指向其所属类的虚函数表。

      • 当通过基类指针或引用调用虚函数时,程序会通过对象的vptr找到vtable,再根据函数在表中的偏移量找到正确的函数地址进行调用。这个过程称为动态绑定

  3. 纯虚函数与抽象类

    纯虚函数是在基类中声明的没有具体实现的虚函数,语法为 virtual ReturnType Function() = 0;。包含纯虚函数的类称为抽象类,它不能实例化对象,只能作为接口使用

        

class Shape { // 抽象基类
public:
    virtual void draw() const = 0; // 纯虚函数,定义接口
    virtual ~Shape() {} // 虚析构函数至关重要
};

class Circle : public Shape {
public:
    void draw() const override { // 重写虚函数
        std::cout << "Drawing a circle.\n";
    }
};

class Square : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing a square.\n";
    }
};

void renderShape(const Shape& shape) { // 统一用基类引用
    shape.draw(); // 动态绑定!根据传入的实际对象类型调用对应的draw
}

int main() {
    Circle c;
    Square s;
    
    renderShape(c); // 输出 "Drawing a circle."
    renderShape(s); // 输出 "Drawing a square."
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值