当前位置: 首页 > news >正文

五、重学C++—类(封装继承)

上一章节:

四、重学C++—CPP基础-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/146298585?spm=1001.2014.3001.5501

本章源代码:

cpp · CuiQingCheng/cppstudy - 码云 - 开源中国https://gitee.com/cuiqingcheng/cppstudy/tree/master/cpp

cppClass.cpp

一、类:C++ 世界的 “事物模板”

类就像现实中 “汽车图纸”,把汽车的颜色、速度、尺寸等数据,以及加速度、制动,空调智能,娱乐方式等操作整合在一起。引入类的概念,是为了让编程模拟现实:将数据(属性)和操作(方法)封装,让复杂事物抽象化,便于管理与复用。

类定义关键字为“class

class [类名]//类定义 
{
    ...
}

实例代码:



// 定义“car”类 
class Car { 
    std::string m_brand;    // 数据成员:品牌 
    std::string m_color;    // 颜色
    int m_width;            // 宽
    int m_heigh;            // 高
    double m_speed;         // 速度
public: 
    // 设置默认值
    void init(std::string bStr, std::string sColor, int w, int h, double sp){
        m_brand = bStr;
        m_color = sColor;
        m_width = w;
        m_heigh = h;
        m_speed = sp;
    }
    // 成员函数:设置品牌 
    void setBrand(std::string b) { m_brand = b; } 
    // 成员函数:显示Car信息 
    void showInfo() { 
        std::cout << "Brand:" << m_brand << ", color:" << m_color << " W:" <<  m_width << " H:" << m_heigh  << " speed:" << m_speed << std::endl; 
    } 
}; 

补充:类的大小,根据其内部定义的属性的大小决定,比如上面定义的这个类,大小为:80字节

若是一个空类,如下:其大小为1字节

class emptyClass{};
std::cout << "class emptyClass size:" << sizeof(emptyClass) << std::endl; // 为1个字节

二、封装的核心角色:构造函数与析构函数

1、构造函数

对象的 “诞生礼”如同新生儿出生时登记信息,构造函数在对象创建时自动执行,完成成员变量初始化

构造函数名与类名相同;

代码示例:



class Book { 
    std::string m_title; 
    int m_pages; 
public: 
    // 构造函数:初始化书籍信息 
    Book(std::string t, int p) { 
        m_title = t;
        m_pages = p;

        std::cout << "构造函数调用:"<<" 新书《" << m_title << "》诞生,共" << m_pages << "页" << std::endl; 
    } 
}; 

子类构造的顺序:父类构造——>子类构造

2、析构函数

对象的 “退场仪式”

对象生命周期结束时,析构函数释放资源,如同离开房间后收拾物品。

class ImageLoader { 
    std::string m_imageName; 
public: 
    ImageLoader(std::string nM) { 
        m_imageName = nM;
        std::cout << "构造函数调用,加载图像,分配内存" << std::endl; 
    } 
    ~ImageLoader() {  // 析构函数释放内存 
        std::cout << "析构函数调用图像卸载,内存释放" << std::endl; 
    } 
}; 

2.1、虚析构函数

多态世界的 “安全锁”

在多态场景下,防止内存泄漏。如同不同包装的快递,拆开时确保清理所有包装。(主要用于基类的指针指向派生类的对象时,用于析构时,需要释放基类的内存)

例如:

class Figure { 
public: 
    virtual ~Figure() {}  // 虚析构函数 
}; 
class Rectangle : public Figure { 
    int* width; 
public: 
    Rectangle() { width = new int(10); } 
    ~Rectangle() { delete width; } 
}; 
// 多态使用与安全释放 
Figure* fig = new Rectangle(); 
delete fig;  // 调用虚析构,避免内存泄漏 

注意:由以上代码中,可以发现,类的声明定义,构造函数/析构函数并不是必须的,类在没有构造函数的时候,会调用默认的构造,但是无法在构造阶段对成员变量进行初始化;析构函数同样的,也不是必须要定义的,其定义的目的是对变量的退出,进行一个资源释放。

子类对象析构的顺序:子类析构——>父类析构

3、访问控制:类的 “门禁系统”

public(公共区):像商场大门,类内外都能访问。

private(私密区):如同卧室,只有类内部成员能进入。

protected(家族区):类似家族内部场地,类和子类成员可访问。

默认控制:类中成员默认 private,结构体默认 public。

class Apartment { 
private:    // 私有权限,默认也是private 
    std::string m_bedroom;  // 卧室(外人不可见) 
protected:  // 保护权限 
    std::string m_study;    // 书房(子类可访问) 
public:     // 公共权限 
    std::string m_livingRoom;  // 客厅(谁都能访问) 
    void cleanRooms() { 
        // 类内可访问所有成员 
        m_bedroom = "整理卧室"; 
        m_study = "打扫书房"; 
        m_livingRoom = "清扫客厅"; 
    } 
    virtual ~Apartment()
    {
        std::cout << "Apartment 析构函数:" << m_bedroom << " " << m_study << " " << m_livingRoom << std::endl;
    }
}; 
class SmallApartment : public Apartment { 
public: 
    void useStudy() { 
        m_study = "在书房工作";  // 子类可访问protected成员 
    } 
}; 

三、继承:代码的 “遗传密码”

继承如同孩子继承父母的特性,作用是复用代码 + 扩展功能。

1、按照基础权限分

三种继承权限

public 继承:父类的 public/protected 成员,在子类保持原样,像贵族血统传承。

protected 继承:父类 public 成员变 protected,如同家族手艺只传内部。

private 继承:父类成员全变 private,像秘密只藏在当前类。

代码示例:

// 基类:交通工具 
class Vehicle { 
public: 
    virtual ~Vehicle(){}
    void move() { 
        std::cout << "Vehicle 移动中... " << m_model << std::endl;
        } 
protected: 
    std::string m_model; 
}; 
    
// public继承:汽车类 
class ChildCar : public Vehicle { 
public: 
    void setModel(std::string m) { 
        m_model = m;  // public继承可访问protected成员 
        move();     // 可访问public成员 
    } 
}; 
    
// protected继承:公交车类 
class Bus : protected Vehicle { 
public: 
    void run() { 
        m_model = "Bus";
        move();  // protected继承,类内可访问 
    } 
}; 
    
// private继承:玩具车类 
class ToyCar : private Vehicle { 
public: 
    void play() { 
        m_model = "ToyCar";
        move();  // private继承,仅类内可用 
    } 
}; 

2、按照继承数量分

单继承:(略)上面列举均是单继承,即只继承了一个父类

多继承:一个子类继承多个基类,用于融合多种不同特性。

class Flyable { 
public:
    void func(){
        fly();
    }
private:
    void fly() {
        std::cout << "我可以飞" << std::endl;
    } 
};

class Swimable { 
public:
    void func(){
        swim();
    }
private:
    void swim() {
        std::cout << "我可以游泳" << std::endl;
    } 
};

class FlyingFish : public Flyable, public Swimable {  // 多继承
public:
    void func()
    {
        Flyable::func();
        Swimable::func();
    }
};

多继承可能引发 “菱形继承” 等问题,需谨慎使用。

菱形继承:

菱形继承存在问题

a、数据冗余:A 的成员在 D 中存储两次

b、访问二义性:调用 A 的成员时,编译器不知道该选哪一份(如 D d; d.fun(); 若 A 有 fun,编译器无法确定路径)。

通过 

virtual 关键字让基类在继承体系中只保留一份实例。语法上,子类声明继承时使用 virtual,确保最终派生类(如上述 D)只包含基类(A)的一个副本,消除冗余和二义性,同时,子类对象在释放时,避免了释放两次基类的现象

// 基类 A
class A {
public:
    ~A(){
        std::cout << "A 析构" << std::endl;
    }
public:
    int value;
};
    
    // B 虚继承 A
class B : virtual public A {
public:
    ~B(){
        std::cout << "B 析构" << std::endl;
    }
};
    
    // C 虚继承 A
class C : virtual public A {
public:
    ~C(){
        std::cout << "C 析构" << std::endl;
    }
};
    
    // D 继承 B 和 C
class D : public B, public C {
public:
    ~D(){
        std::cout << "D 析构" << std::endl;
    }
};

// 菱形继承
D d;
d.value = 10;  // 直接访问,无歧义,因虚继承后只有一份 A 的 value
std::cout << d.value << std::endl;  // 输出 10

相关文章:

  • QT实现WPS功能
  • AI:如何用 MeloSpyGUI 和 MeloSpySuite 生成爵士音乐文件
  • 如何让自动驾驶汽车“看清”世界?坐标映射与数据融合概述
  • Java学习路线(便于理解)
  • “统计视角看世界”专栏阅读引导
  • 接上一主题,直接对二进制进行加密,密钥不写入电脑。
  • .NET 9 彻底改变了 API 文档:从 Swashbuckle(Swagger) 到 Scalar
  • AI比人脑更强,因为被植入思维模型【17】万物联系思维模型
  • 低功耗蓝牙(BLE)方案设计实战指南
  • 程序代码篇---SQLite数据库存储信息
  • 操作系统的特征
  • 程序设计语言的分类和特点
  • 学习本地部署DeepSeek的过程(基于ollama)
  • 产品经理如何管理需求池
  • Spring AOP 核心概念与实践指南
  • 图解模糊推理过程(超详细步骤)
  • DeepSeek、Grok 与 ChatGPT 4.5:新一代大模型架构与推理能力深度解析
  • 计算机网络——数据链路层的功能
  • [快乐学坊_2] 后端api测试
  • 如何修改进程的优先级
  • 从良渚到三星堆:一江水串起了5000年的文明对话
  • 上百家单位展示AI+教育的实践与成果,上海教育博览会开幕
  • 会谈时间迟迟未定、核心议题存在分歧,俄乌“土耳其谈判”一波三折
  • 俄代表团:16日上午将继续“等候乌代表团”
  • 盛和资源海外找稀土矿提速:拟超7亿元收购匹克,加快推动坦桑尼亚项目
  • 媒体:“西北大学副校长范代娣成陕西首富”系乌龙,但她的人生如同开挂