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

C++中类对象作为类成员(对象成员/成员对象)的一些注意事项

目录

一、对象成员的定义与特点

1.1 对象成员的定义 

1.2 对象成员的特点

二、单个对象成员的构造/析构顺序

三、多个对象成员的构造/析构顺序

四、对象成员的初始化

情况 1:成员对象有默认构造函数

情况 2:成员对象没有默认构造函数

五、总结


一、对象成员的定义与特点

1.1 对象成员的定义 

在 C++ 中,类的成员可以是另一个类的对象,这样的成员称为 对象成员(或者称为 成员对象)。这是一种 组合(Composition) 关系。

例如:

#include<iostream>

class A{};
class B{
    A a;
};

// B类中有对象 a 作为成员,a 为对象成员
// 那么当创建 b 对象时,a 与 b 的构造函数和析构函数调用顺序是什么样的呢?

1.2 对象成员的特点

  1. 成员对象在包含类构造时,按声明顺序自动构造,在析构时按声明顺序的逆序自动析构。
  2. 若成员对象无默认构造函数,必须通过初始化列表初始化。
  3. 成员对象的构造函数先于本类构造函数调用,析构顺序相反。
  4. 构造顺序(析构顺序)由声明顺序(声明顺序的逆序)决定,而非初始化列表顺序。

二、单个对象成员的构造/析构顺序

#include <iostream>

class Engine {
public:
    Engine() { std::cout << "Engine 构造函数调用" << std::endl; }
    ~Engine() { std::cout << "Engine 析构函数调用" << std::endl; }
};

class Car {
private:
    Engine engine; // Car 包含一个 Engine 对象

public:
    Car() { std::cout << "Car 构造函数调用" << std::endl; }
    ~Car() { std::cout << "Car 析构函数调用" << std::endl; }
};

int main() {
    Car myCar;
    return 0;
}

输出结果:

Engine 构造函数调用
Car 构造函数调用
Car 析构函数调用
Engine 析构函数调用

解释

  1. Engine 对象是 Car 的成员,所以在 Car 的对象 myCar 构造时,Engine 会先被构造
  2. 析构顺序与构造顺序相反,所以 Car 的析构函数先执行,然后才是 Engine 的析构函数。

总结:

✅ 对象成员的构造函数先于本类的构造函数执行。
✅ 对象成员的析构顺序与构造顺序相反。

🚀 掌握这个规则对于 C++ 组合关系、资源管理(RAII)等编程至关重要!

汽车(Car)和发动机(Engine)的构造与析构顺序

构造顺序(组装一辆汽车 🚗)

想象你在汽车工厂里造一辆汽车,主要涉及两个关键部件:

  1. 发动机(Engine)
  2. 汽车车身(Car)

🚀 造车的流程

  1. 先安装发动机
    你不能在没有发动机的情况下造一辆完整的汽车,所以发动机必须先安装
  2. 再组装车身
    车身是整个汽车的框架,它依赖于发动机,所以要等发动机装好后才能组装车身。
  3. 汽车完整并准备出厂

🔹 顺序:发动机 → 车身 → 汽车组装完成(对象成员 Engine 先构造,Car 后构造)


析构顺序(报废一辆汽车 🚗)

当汽车使用多年,最终到了报废的时候,拆解过程是:

  1. 先拆解车身
    你不会直接把发动机拆走,而是先把车身拆掉,以便暴露发动机。
  2. 再移除发动机
    车身拆掉后,才能安全地取出发动机。
  3. 汽车完全报废

🔹 顺序:车身 → 发动机 → 完全报废Car 先析构,Engine 后析构)


总结

  • 构造顺序:先装发动机,再装车身,最终汽车完成成员对象先构造,包含类后构造)。
  • 析构顺序:先拆车身,再拆发动机,最终汽车报废包含类先析构,成员对象后析构)。

这个顺序是固定的,因为汽车(Car)依赖于发动机(Engine),必须先有发动机,汽车才能存在! 🚗


三、多个对象成员的构造/析构顺序

 多个对象成员的构造顺序是按照它们在类中声明的顺序与初始化列表的顺序无关

#include <iostream>

class Engine {
public:
    Engine() { std::cout << "Engine 构造" << std::endl; }
    ~Engine() { std::cout << "Engine 析构" << std::endl; }
};

class Transmission {
public:
    Transmission() { std::cout << "Transmission 构造" << std::endl; }
    ~Transmission() { std::cout << "Transmission 析构" << std::endl; }
};

class Car {
private:
    Engine engine;        // 先声明 Engine
    Transmission gearbox; // 后声明 Transmission

public:
    Car() : gearbox(), engine() {  // 初始化列表顺序:先 gearbox 再 engine(但不会影响构造顺序)
        std::cout << "Car 构造" << std::endl;
    }
    
    ~Car() {
        std::cout << "Car 析构" << std::endl;
    }
};

int main() {
    Car myCar;
    return 0;
}

🚗 输出结果:

Engine 构造
Transmission 构造
Car 构造
Car 析构
Transmission 析构
Engine 析构

🚀 解析

  1. 构造阶段

    • 尽管初始化列表顺序是 gearbox(), engine(),但 Engine 仍然会先构造,因为它在 Car 类中先声明
    • 然后 Transmission 才会构造。
    • 最后 Car 构造完成。
  2. 析构阶段

    • Car 先执行析构函数。
    • Transmission 先析构(因为它后构造)。
    • Engine 最后析构(因为它先构造)。

📌 关键点

成员对象的构造顺序与它们在类中声明的顺序一致而不是初始化列表的顺序
成员对象的析构顺序与构造顺序相反

这就像现实中造汽车:不管装配工人想先装变速箱还是发动机,组装流程是固定的——发动机必须先装,然后才是变速箱! 🚗

 

四、对象成员的初始化

情况 1:成员对象有默认构造函数

如果成员对象有默认构造函数,可以不使用初始化列表:

#include<iostream>
class Engine {
    public:
        Engine() { std::cout << "Engine 默认构造" << std::endl; }
    };
    
    class Car {
    private:
        Engine engine; // Engine 具有默认构造函数
    public:
        Car() { std::cout << "Car 构造" << std::endl; }
    };


int main(){
    Car mycar;
    return 0;
}

// 输出:
// Engine 默认构造
// Car 构造
    

会自动调用 Engine 的默认构造函数


情况 2:成员对象没有默认构造函数

如果 Engine 没有默认构造函数,换言之,假设Engine有一个有参构造函数,那么必须用 初始化列表为成员对象初始化

#include<iostream>
class Engine {
    private:
        int power;
    public:
        Engine(int p) : power(p) { std::cout << "Engine 初始化,功率:" << power << std::endl; }
    };
    
    class Car {
    private:
        Engine engine;  // 这里的 engine 需要初始化
    public:
        Car(int power) : engine(power) {  // 通过初始化列表初始化 engine
            std::cout << "Car 初始化" << std::endl;
        }
    };

int main(){
    Car mycar(150);
    return 0;
}
 
// 输出:
// Engine 初始化,功率:150
// Car 初始化

💡 初始化顺序与成员定义顺序一致,不管初始化列表的顺序如何。


五、总结

成员对象在包含类构造时,按声明顺序自动构造,在析构时按声明顺序的逆序自动析构。
若成员对象无默认构造函数,必须通过初始化列表初始化。
成员对象的构造函数先于本类构造函数调用,析构顺序相反。
构造顺序(析构顺序)由声明顺序(声明顺序的逆序)决定,而非初始化列表顺序。

这种方式常用于组合模式(Composition),在 C++ 面向对象设计中非常重要! 🚀

相关文章:

  • vue2的webpack(vue.config.js) 怎么使用请求转发 devServer.proxy
  • AGI大模型(5):提示词工程
  • ubuntu20.04
  • 铁人三项(第五赛区)_2018_rop题解
  • 《算法笔记》8.1小节——搜索专题->深度优先搜索(DFS)问题 D: 【递归入门】n皇后 问题(原始的8皇后问题)
  • 我又又又又又又更新了~~纯手工编写C++画图,有注释~~~
  • 【C#】使用DeepSeek帮助评估数据库性能问题,C# 使用定时任务,每隔一分钟移除一次表,再重新创建表,和往新创建的表追加5万多条记录
  • USER与多组织关联的SQL查询以及几个关键函数用法
  • ​面向对象与面向过程编程:从概念到实战的深度解析
  • ROS学习过程(一)
  • unity几种设计模式(自用)
  • 【复习】补充
  • Cookie与Session详解
  • C++ string
  • ES6(1) 简介与基础概念
  • 计算机二级——Python:Day1
  • 力扣215.数组中的第K个最大元素--堆排序法(java)
  • 透过安全事件看软件组成分析SCA
  • 26岁赵露思病好后大变样,穿披肩染奶奶灰意外惊艳,复工后美回巅峰
  • 专题|Python贝叶斯金融数据应用实例合集:随机波动率SV模型、逻辑回归、参数更新、绩效比较BEST分析亚马逊股票、普尔指数...
  • 网站开发体会范文/二十条优化疫情措施
  • 厦门 外贸商城网站制作/成都网站seo厂家
  • 四川做网站设计哪家好/灰色关键词怎么做排名
  • 如何打开建设网站后台/如何网络媒体推广
  • 塔式服务器主机建网站/市场营销方案
  • wordpress oss/seo综合查询站长工具怎么用