C++——7.类与对象,掌握封装、继承、多态.详解

1. 类与对象:从结构体到抽象数据类型

在 C++ 中,类 是自定义类型的蓝图,它描述了一组数据以及可以对这些数据进行的操作。对象 则是类的实例化实体,拥有独立的内存空间。示例:

Person.h

cpp 复制代码
#pragma once
#include <string>
class Person {
public:
    // 成员函数(行为)
    std::string GetName() const;
    void SetName(const std::string& name);
    void Work();
private:
    // 成员变量(属性)
    std::string m_name;
    int m_age;
};

Person.cpp

cpp 复制代码
#include "Person.h"
#include <string>
#include <iostream>


std::string Person::GetName() const 
{
	return m_name;
}

void Person::SetName(const std::string& name)
{
	m_name = name;
}

void Person::Work()
{
	std::cout << m_name << " is working." << std::endl;
}

调用脚本:

cpp 复制代码
#include <iostream>
#include "Person.h"
int main() {
    Person p;
	std::string name = "Alice";
	p.SetName(name);
    std::cout << p.GetName(); // 输出 Alice
	p.Work(); 
    return 0;
}

类与 C 语言的结构体最大的区别是:类可以定义成员函数,并拥有访问控制能力。C++ 的结构体 struct 实际上也是一种类,只是默认访问权限为 public,而 class 默认为 private。

2. 封装:隐藏实现细节,保护数据安全

封装是将数据和操作数据的方法绑定在一起,并对外隐藏内部实现细节的机制。在 C++ 中,封装通过访问限定符实现:

1.private:类内部可访问,派生类和外部代码不可直接访问。

2.protected:类内部和派生类可访问,外部代码不可访问。

3.public:完全公开。

封装的优点

1.控制数据读写权限,避免非法赋值(如年龄不能为负)。

2.降低模块间耦合,修改内部实现不影响调用者。

3.提供清晰的接口,增强可维护性。

典型设计:将数据成员设为 private,提供公共的 getter/setter 成员函数来控制访问逻辑。

cpp 复制代码
class BankAccount {
private:
    double balance;  // 余额不直接暴露

public:
    void deposit(double amount) {
        if (amount > 0) balance += amount;
    }
    bool withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            return true;
        }
        return false;
    }
    double getBalance() const { return balance; }
};

这里外部只能通过 deposit 和 withdraw 安全地操作余额,无法直接修改 balance,保证了数据的完整性。

3. 继承:复用代码,建立类型层级

继承允许我们从已有的类(基类/父类)派生出新的类(派生类/子类),派生类自动获得基类的成员,并可添加新的成员或重写已有功能。

3.1 基本概念与语法

继承允许我们创建一个新类(派生类)来复用、扩展或修改现有类(基类)的行为。它体现了 is-a 关系。

cpp 复制代码
#pragma once
class Base {
public:
    int pub;
protected:
    int prot;
private:
    int priv;  // 派生类不可直接访问
};
cpp 复制代码
#pragma once
#include "Base.h"

class Derived : public Base {   // public 继承
    // pub 在 Derived 中仍是 public
    // prot 在 Derived 中仍是 protected
    // priv 不可访问
};

3.2 继承方式与访问权限变化

派生类继承基类时,可指定继承方式:public、protected、private。它影响基类成员在派生类中的访问级别:

基类成员访问 public继承 protected继承 private继承
public成员 仍为public 变为protected 变为private
protected成员 仍为protected 变为protected 变为private
private成员 不可直接访问 不可直接访问 不可直接访问

通常使用 public 继承,保持 is-a 关系,这样派生类对象可当作基类对象使用。

cpp 复制代码
class A { public: int x; };
class B : private A { };   // x 在 B 中为 private
class C : public B {
    // 无法访问 x,因为 B 中 x 为 private
};

3.3 构造函数与析构函数调用顺序

创建派生类对象时,先调用基类构造函数(初始化基类部分),再调用派生类构造函数。析构时相反:先析构派生类,再析构基类。

如果基类没有默认构造函数,派生类必须在初始化列表中显式调用基类构造函数:

Base.h:

cpp 复制代码
#pragma once
#include <iostream>
class Base {
public:
    Base(int val) 
    {
        std::cout << "Base constructor called\n";
        value = val; 
    }
    ~Base()
    {
        std::cout << "Base destructor called\n";
    }
	int GetBValue() { return value; }
private:
    int value;
};

Derived.h:

cpp 复制代码
#pragma once
#include "Base.h"

class Derived : public Base {
public:
    Derived(int a, double b) : Base(a), d_val(b) 
    {
        std::cout << "Derived  constructor called\n";
        b = d_val; 
    }
    ~Derived()
    {
        std::cout << "Derived destructor called\n";
    }
    double GetDValue(){return d_val;}
private:
    double d_val;
};

调用:

cpp 复制代码
#include <iostream>
#include "Derived.h"
int main()
{
    Derived d(123, 45.67);// 构造函数
	printf("Derived object created with Base value: %d and Derived value: %.2f\n", d.GetBValue(), d.GetDValue());
    return 0;
}

3.4 同名成员与隐藏

若派生类定义了与基类同名的成员(函数或变量),基类的同名成员会被隐藏。即使函数参数不同,也不会形成重载,而是隐藏。若要调用基类版本,需加 Base::作用域限定。

csharp 复制代码
#include <iostream>
#include <string>

class Vehicle {
public:
    // 基类成员变量
    std::string id = "GenericVehicle";

    // 基类重载的两个 show 函数
    void show(int count) {
        std::cout << "Vehicle::show(int): count = " << count << std::endl;
    }

    void show(double weight) {
        std::cout << "Vehicle::show(double): weight = " << weight << std::endl;
    }
};

class Car : public Vehicle {
public:
    // 派生类定义了同名的 id(类型不同)
    int id = 1001;   // 隐藏了基类的 std::string id

    // 派生类定义了同名的 show 函数(参数类型不同)
    void show(const std::string& brand) {
        std::cout << "Car::show(string): brand = " << brand << std::endl;
    }
    // 这一写,基类的所有 show 重载版本都被隐藏
};

int main() {
    Car c;

    // 1. 成员变量隐藏
    std::cout << c.id << std::endl;           // 输出 1001(派生类的 int)
    std::cout << c.Vehicle::id << std::endl;  // 输出 "GenericVehicle"(被隐藏的基类成员)

    // 2. 成员函数隐藏
    c.show("Toyota");          // 正确,调用 Car::show(string)
    // c.show(4);             // 错误!Vehicle::show(int) 被隐藏
    // c.show(1.5);           // 错误!Vehicle::show(double) 被隐藏

    // 显式调用被隐藏的基类函数
    c.Vehicle::show(4);
    c.Vehicle::show(1.5);

    return 0;
}

3.5 多重继承与虚基类(进阶)

C++ 支持多重继承,可能带来二义性和菱形继承问题。使用虚基类可让公共基类只保留一份实例。

Intern 同时继承 Student 和 Employee,而两者都继承自 Person。若无虚继承,Intern 对象中会包含两份 Person 的成员,造成冗余和二义性。

1. 非虚继承 ------ 出现二义性和多份基类实例

cpp 复制代码
#include <iostream>
#include <string>

class Person {
public:
    std::string name;
    Person(const std::string& n) : name(n) {}
};

class Student : public Person {
public:
    int studentId;
    Student(const std::string& n, int id) : Person(n), studentId(id) {}
};

class Employee : public Person {
public:
    int employeeId;
    Employee(const std::string& n, int id) : Person(n), employeeId(id) {}
};

class Intern : public Student, public Employee {
public:
    Intern(const std::string& n, int sId, int eId)
        : Student(n, sId), Employee(n, eId) {
    }
};

int main() {
    Intern in("Alice", 1001, 2001);

    // 错误:name 不明确,有两份 Person,编译器不知访问哪一个
    // std::cout << in.name << std::endl;

    // 必须显式指定路径
    std::cout << "Student 路径的名字: " << in.Student::name << std::endl;
    std::cout << "Employee 路径的名字: " << in.Employee::name << std::endl;

    // 两份 name 是独立的,可以不同(逻辑上不合理)
    in.Student::name = "Alice_student";
    in.Employee::name = "Alice_employee";
    std::cout << "修改后 Student name: " << in.Student::name << std::endl;
    std::cout << "修改后 Employee name: " << in.Employee::name << std::endl;

    return 0;
}

2. 虚继承 ------ 只保留一份公共基类实例

在 Student 和 Employee 继承 Person 时加上 virtual 关键字,Intern 中就只有一份 Person 子对象。

cpp 复制代码
#include <iostream>
#include <string>

class Person {
public:
    std::string name;
    Person(const std::string& n = "") : name(n) {}
};

// 虚继承 Person
class Student : virtual public Person {
public:
    int studentId;
    Student(const std::string& n, int id) : Person(n), studentId(id) {}
};

class Employee : virtual public Person {
public:
    int employeeId;
    Employee(const std::string& n, int id) : Person(n), employeeId(id) {}
};

class Intern : public Student, public Employee {
public:
    // 最底层派生类负责初始化虚基类
    Intern(const std::string& n, int sId, int eId)
        : Person(n), Student(n, sId), Employee(n, eId) {
    }
};

int main() {
    Intern in("Alice", 1001, 2001);

    // 直接访问 name,不再二义
    std::cout << "姓名: " << in.name << std::endl;

    // 也可以通过路径访问(都是同一个对象)
    std::cout << "通过 Student 路径: " << in.Student::name << std::endl;
    std::cout << "通过 Employee 路径: " << in.Employee::name << std::endl;

    // 修改后所有路径都反映同一值
    in.name = "Alice_new";
    std::cout << "修改后: " << in.name << std::endl;
    std::cout << "Student 路径: " << in.Student::name << std::endl;
    std::cout << "Employee 路径: " << in.Employee::name << std::endl;

    return 0;
}

虚基类的构造由最底层派生类负责,需在初始化列表中调用虚基类构造函数。

四、多态

多态意为"多种形态",它允许通过统一的接口操作不同类型的对象,从而提高代码的灵活性和可扩展性。C++ 多态分两种:编译时多态(静态多态) 和 运行时多态(动态多态)。

4.1 编译时多态

在编译阶段就决定调用哪个函数,主要包括:

1.函数重载:同一作用域内函数名相同,参数不同。

2.运算符重载:自定义操作符行为。

3.模板:通过泛型编程实现代码复用,函数模板和类模板。

函数重载示例:

cpp 复制代码
   void print(int i) { std::cout << "int: " << i; }
   void print(double d) { std::cout << "double: " << d; }

4.2 运行时多态(动态多态)

运行时多态通过 虚函数 实现,调用哪个函数在运行时根据对象的实际类型决定,核心机制是动态绑定。

4.2.1 虚函数与动态绑定

在基类中将成员函数声明为 virtual,派生类可以重写(override) 该函数。通过基类指针或引用调用虚函数时,会依据所指对象的真实类型调用对应版本的函数。

cpp 复制代码
#include <iostream>
#include <vector>
#include <memory>

// 基类:图形
class Shape {
public:
    // 虚函数:可被派生类重写
    virtual void draw() const {
        std::cout << "绘制一个通用图形" << std::endl;
    }

    // 虚析构函数(保证正确析构派生类对象)
    virtual ~Shape() = default;
};

// 派生类:圆形
class Circle : public Shape {
public:
    void draw() const override {        // override 明确表示重写
        std::cout << "绘制一个圆形 ○" << std::endl;
    }
};

// 派生类:矩形
class Rectangle : public Shape {
public:
    void draw() const override {
        std::cout << "绘制一个矩形 □" << std::endl;
    }
};

// 派生类:三角形
class Triangle : public Shape {
public:
    void draw() const override {
        std::cout << "绘制一个三角形 △" << std::endl;
    }
};

// 接受基类引用,内部调用虚函数(动态绑定)
void renderShape(const Shape& s) {
    s.draw();   // 运行时决定调用哪个 draw()
}

int main() {
    Circle c;
    Rectangle r;
    Triangle t;

    // 1. 通过基类引用调用虚函数
    renderShape(c);
    renderShape(r);
    renderShape(t);

    std::cout << "-----------" << std::endl;

    // 2. 通过基类指针调用虚函数,多态容器
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>());
    shapes.push_back(std::make_unique<Rectangle>());
    shapes.push_back(std::make_unique<Triangle>());

    for (const auto& p : shapes) {
        p->draw();   // 动态绑定,调用实际对象的 draw()
    }

    return 0;
}

4.2.2 虚函数表(vtable)与虚指针(vptr)

编译器为每个含有虚函数的类生成一个虚函数表(vtable),表中存放该类所有虚函数的实际地址。每个对象内部有一个不可见的指针(vptr),指向所属类的虚函数表。调用虚函数时,通过 vptr -> vtable 找到函数地址,实现动态分发。这就是动态多态的底层原理。

cpp 复制代码
#include <iostream>

// 基类:包含一个虚函数
class Base {
public:
    virtual void show() {                     // 虚函数
        std::cout << "Base::show()" << std::endl;
    }
    virtual ~Base() = default;                // 虚析构(保证正确释放)
};

// 派生类:重写虚函数
class Derived : public Base {
public:
    void show() override {                    // 重写
        std::cout << "Derived::show()" << std::endl;
    }
};

// 一个不含虚函数的普通类,用于对比大小
class Empty {};

int main() {
    // 1. 观察对象大小(vptr 的证据)
    std::cout << "sizeof(Empty)   = " << sizeof(Empty) << std::endl;   // 通常为 1
    std::cout << "sizeof(Base)    = " << sizeof(Base) << std::endl;    // 含 vptr,通常为 8(64位)
    std::cout << "sizeof(Derived) = " << sizeof(Derived) << std::endl; // 同样含 vptr,大小通常与 Base 相同

    Base b;
    Derived d;

    // 2. 通过基类指针调用虚函数(动态绑定,依赖 vtable)
    Base* ptr = &b;
    ptr->show();                // 输出 Base::show()

    ptr = &d;
    ptr->show();                // 输出 Derived::show(),多态行为

    // 3. 多个对象共享同一个 vtable
    Base b2;
    std::cout << "Address of b:  " << &b << std::endl;
    std::cout << "Address of b2: " << &b2 << std::endl;
    // 虽然地址不同,但它们内部的 vptr 指向同一个 Base 类的 vtable

    return 0;
}

4.2.3 纯虚函数与抽象类

纯虚函数是没有实现的虚函数,声明为 virtual void func() = 0;。包含纯虚函数的类称为抽象类,不能实例化。派生类必须实现所有纯虚函数才能成为具体类。

cpp 复制代码
#include <iostream>
#include <vector>
#include <memory>

// 抽象类:含有纯虚函数,不能实例化
class Shape {
public:
    // 纯虚函数:没有实现,要求派生类必须重写
    virtual void draw() const = 0;

    // 纯虚函数也可以有多个
    virtual double area() const = 0;

    // 虚析构(确保派生类资源正确释放)
    virtual ~Shape() = default;

    // 抽象类中也可以有普通成员函数
    void description() const {
        std::cout << "这是一个形状" << std::endl;
    }
};

// 具体派生类:实现所有纯虚函数
class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}

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

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

class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}

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

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

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

    // 通过基类指针操作具体对象(多态)
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>(5.0));
    shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0));

    for (const auto& sp : shapes) {
        sp->draw();                          // 动态绑定
        std::cout << "面积 = " << sp->area() << std::endl;
        sp->description();                   // 调用普通成员函数
        std::cout << "-----------" << std::endl;
    }

    return 0;
}

4.2.4 析构函数务必声明为虚函数

当基类指针指向派生类对象并通过 delete 释放时,若析构函数不是虚函数,则只调用基类析构,导致派生类资源无法正确释放(未定义行为)。将基类析构声明为虚函数可保证正确的析构顺序。

错误示例:非虚析构导致资源泄漏

cpp 复制代码
#include <iostream>

class Base {
public:
    Base() { std::cout << "Base 构造" << std::endl; }
    ~Base() {                            // 非虚析构!
        std::cout << "Base 析构" << std::endl;
    }
};

class Derived : public Base {
private:
    int* data;                           // 动态分配的资源
public:
    Derived() {
        data = new int[100];             // 申请堆内存
        std::cout << "Derived 构造,分配资源" << std::endl;
    }
    ~Derived() {
        delete[] data;                   // 释放资源
        std::cout << "Derived 析构,释放资源" << std::endl;
    }
};

int main() {
    Base* ptr = new Derived();           // 基类指针指向派生类对象
    delete ptr;                          // 危险!只调用 Base::~Base()
    return 0;
}

可以看到 Derived 的析构函数没有被调用,data 指向的内存永远泄漏,且没有"释放资源"的输出。

正确做法:将基类析构声明为虚函数

cpp 复制代码
#include <iostream>

class Base {
public:
    Base() { std::cout << "Base 构造" << std::endl; }
    virtual ~Base() {                    // 虚析构!
        std::cout << "Base 析构" << std::endl;
    }
};

class Derived : public Base {
private:
    int* data;
public:
    Derived() {
        data = new int[100];
        std::cout << "Derived 构造,分配资源" << std::endl;
    }
    ~Derived() override {
        delete[] data;
        std::cout << "Derived 析构,释放资源" << std::endl;
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;                          // 安全!先调 Derived::~Derived(),再调 Base::~Base()
    return 0;
}

规则与建议

1.只要一个类被设计为基类(或被继承),它的析构函数就应该是虚函数。

2.如果不打算让类被继承,可以使用 final 关键字阻止派生(C++11)。

3.即使基类的析构函数什么都不做,也请声明为 virtual ~ClassName() = default;,保证多态删除的安全。

4.这条规则同样适用于抽象类:即使无法实例化,虚析构仍然是必需的。

4.2.5 override 和 final 关键字(C++11)

override:显式声明派生类函数重写基类虚函数,编译器会检查函数签名是否正确匹配,避免错误。

final:阻止派生类继续重写某个虚函数,或阻止类被继承。

1. override:显式重写,编译器护航

用 override 修饰派生类的成员函数,明确表示该函数旨在重写基类虚函数。编译器会严格检查函数签名(函数名、参数列表、const 限定、引用限定等)是否与基类虚函数完全匹配。若基类没有对应的虚函数,或签名有误,直接产生编译错误。

cpp 复制代码
#include <iostream>

class Base {
public:
    virtual void draw() const {
        std::cout << "Base::draw() const" << std::endl;
    }
    virtual void resize(int w, int h) {
        std::cout << "Base::resize(" << w << ", " << h << ")" << std::endl;
    }
    virtual ~Base() = default;
};

class Derived : public Base {
public:
    // 正确重写,建议添加 override
    void draw() const override {
        std::cout << "Derived::draw() const" << std::endl;
    }

    // 错误示范:试图重写 resize 但参数列表不同,且无意中隐藏了基类版本
    // void resize(double factor) override;  // 编译错误!基类无此虚函数签名

    // 正确重写 resize
    void resize(int w, int h) override {
        std::cout << "Derived::resize(" << w << ", " << h << ")" << std::endl;
    }
};

int main() {
    Derived  d;
    Derived* p = &d;
    p->draw();
}

2. final:阻止进一步重写或继承

final 可以施加于虚函数或类,实现设计层面的"冻结"。

修饰虚函数:禁止任何派生类再次重写该虚函数。

修饰类:禁止该类被继承。

csharp 复制代码
#include <iostream>
#include <vector>
#include <memory>

class Animal {
public:
    virtual void speak() const = 0;
    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    // 重写 speak 并声明为 final,Cat 不能再重写它
    void speak() const override final {
        std::cout << "Woof!" << std::endl;
    }
};

// 尝试重写 final 虚函数 -> 编译错误
class Husky : public Dog {
public:
    // void speak() const override {   // 错误!Dog::speak 是 final
    //     std::cout << "Husky Woof!" << std::endl;
    // }
};

// 使用 final 阻止类被继承
class Cat final : public Animal {
public:
    void speak() const override {
        std::cout << "Meow!" << std::endl;
    }
};

// class Persian : public Cat {   // 错误!Cat 被声明为 final,不能作为基类
// };

int main() {
    Cat d;
    Cat* p = &d;
    p->speak();       
}

综合示例

cpp 复制代码
#include <iostream>
#include <vector>
#include <memory>

class Shape {
public:
    virtual void draw() const = 0;
    virtual std::string name() const { return "Shape"; }
    virtual ~Shape() = default;
};

class Circle : public Shape {
public:
    void draw() const override {
        std::cout << "○" << std::endl;
    }
    std::string name() const override final {  // 禁止后续派生类重写 name
        return "Circle";
    }
};

class ColoredCircle final : public Circle {   // ColoredCircle 不能被继承
public:
    void draw() const override {
        std::cout << "🌈○" << std::endl;
    }
    // 不能重写 name,因为它在 Circle 中标记为 final
};

int main() {
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>());
    shapes.push_back(std::make_unique<ColoredCircle>());

    for (const auto& s : shapes) {
        s->draw();
        std::cout << "Name: " << s->name() << std::endl;
    }
}

4.2.6 协变返回类型

重写虚函数时,若返回类型是类的指针或引用,派生类可以返回更具体的派生类指针/引用,称为协变。

csharp 复制代码
#include <iostream>
#include <memory>

// 基类:图形
class Shape {
public:
    // 虚函数,返回基类引用
    virtual const Shape& getSelf() const {
        std::cout << "Shape::getSelf()" << std::endl;
        return *this;
    }

    // 另一个虚函数,返回基类指针
    virtual Shape* clone() const {
        std::cout << "Shape::clone() -> Shape*" << std::endl;
        return new Shape(*this);
    }

    virtual void draw() const { std::cout << "Shape" << std::endl; }
    virtual ~Shape() = default;
};

// 派生类:圆形
class Circle : public Shape {
public:
    // 协变:返回 Circle& 而非 Shape&
    const Circle& getSelf() const override {
        std::cout << "Circle::getSelf()" << std::endl;
        return *this;
    }

    // 协变:返回 Circle* 而非 Shape*
    Circle* clone() const override {
        std::cout << "Circle::clone() -> Circle*" << std::endl;
        return new Circle(*this);
    }

    void draw() const override { std::cout << "Circle" << std::endl; }
};

int main() {
    Circle circle;
    const Shape& shapeRef = circle;

    // 通过基类引用调用 getSelf():动态绑定到 Circle::getSelf()
    const Shape& self1 = shapeRef.getSelf();  // 返回类型是 const Shape&,但实际对象是 Circle
    self1.draw();                              // 输出 Circle

    // 通过派生类对象调用:获得精确类型 Circle&
    const Circle& self2 = circle.getSelf();    // 返回 Circle&,无需转型
    self2.draw();                              // 输出 Circle

    // clone 测试
    Shape* clonedShape = circle.clone();       // Circle::clone() 返回 Circle* 赋值给 Shape*
    clonedShape->draw();                       // 输出 Circle
    delete clonedShape;

    // 同样可以使用派生类指针接收,避免转型
    Circle* clonedCircle = circle.clone();     // 直接获得 Circle*
    clonedCircle->draw();
    delete clonedCircle;

    return 0;
}

4.3 多态的综合示例

cpp 复制代码
#include <iostream>
#include <vector>

class Animal {
public:
    virtual void speak() const = 0;   // 纯虚函数
    virtual ~Animal() {}
};

class Dog : public Animal {
public:
    void speak() const override { std::cout << "Woof!\n"; }
};

class Cat : public Animal {
public:
    void speak() const override { std::cout << "Meow!\n"; }
};

int main() {
    std::vector<Animal*> animals;
    animals.push_back(new Dog());
    animals.push_back(new Cat());

    for (auto animal : animals) {
        animal->speak();   // 运行时多态,分别输出 Woof! Meow!
    }
    // 释放内存
    for (auto animal : animals) delete animal;
}

五、封装、继承、多态的协同

封装 让类的内部实现安全隐藏,只暴露接口,降低耦合。

继承 实现代码复用和层次化设计,表达 is-a 关系。

多态 允许通过基类接口调用不同派生类的行为,使得程序易扩展(开闭原则:对扩展开放,对修改封闭)。

设计良好的面向对象系统通常是:封装合理的数据,通过继承建立类的层次,再利用多态编写可以处理不同子类的通用逻辑。这三个特性相互配合,构建出可维护、可扩展的 C++ 程序。

通过上述细节和示例,可以透彻理解 C++ 类与对象的三大支柱特性,并在实际开发中灵活运用。

相关推荐
初阳7851 小时前
【Qt】系统相关(2)——文件
开发语言·qt
Mortalbreeze1 小时前
深入 Linux 线程机制(三):线程互斥——竞争条件与互斥锁的本质
linux·运维·服务器·c++·算法
Mr_Controll1 小时前
【运动控制——电子凸轮曲线设计(多项式)】
算法·自动化
Sam09271 小时前
【AI 算法精讲 15】余弦相似度:向量检索与归一化内积
人工智能·python·算法·ai
汉克老师1 小时前
GESP2026年6月认证C++一级( 第一部分选择题(1-7))精讲
c++·运算符·表达式·赋值·gesp1级·输入与输出·单循环
ALex_zry1 小时前
C++26 Freestanding 库扩展详解:无操作系统也能用标准库
java·jvm·c++
黎阳之光2 小时前
黎阳之光实时三维重构:重构智慧铁路全新管控范式
大数据·人工智能·物联网·算法·数字孪生
weixin_400005602 小时前
Vision-Language-Action:LMDrive项目架构与核心算法组件
人工智能·深度学习·算法·机器学习·架构·自动驾驶
码上有光2 小时前
c++:map和set的底层实现
android·java·c++·set·map