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

类对象作为类成员

输出结果将会是这样的:

代码:

#include <iostream>

class B {
public:
    B() {
        std::cout << "B constructor called." << std::endl;
    }
    void show() {
        std::cout << "Class B function called." << std::endl;
    }
};

class A {
private:
    B b;  // A 类的成员是 B 类对象

public:
    // A 类的构造函数
    A() : b() {  // 通过初始化列表显式调用 B 的构造函数
        std::cout << "A constructor called." << std::endl;
    }

    void callB() {
        b.show();
    }
};

int main() {
    A a;  // 创建 A 类对象时,B 类的对象也会被构造
    a.callB();  // 调用 A 类的方法,通过 A 类的成员对象调用 B 类的方法

    return 0;
}

输出:

B constructor called.
A constructor called.
Class B function called.

输出解释:

  1. B constructor called.:当创建 A 类的对象时,首先会调用 B 类的构造函数。因为 A 类有一个 B 类类型的成员变量 b,所以 B 的构造函数会被自动调用。

  2. A constructor called.:接着,A 类的构造函数被调用。这是在 A 对象的构造过程中输出的信息。

  3. Class B function called.:然后,调用 a.callB(),这会通过 A 类的成员对象 b 调用 B 类的 show() 方法,输出这行信息。

总结起来,程序按照这个顺序执行并输出相应的构造函数信息。

好的,我们可以通过一个更复杂的示例来详细分析初始化列表的使用,尤其是在 B 类有多个成员属性的情况下。

示例:B 类具有多个属性

假设 B 类有多个成员变量(例如整数和字符串),我们希望在 A 类的构造函数中初始化这些属性。

代码示例:
#include <iostream>
#include <string>

class B {
private:
    int x;
    std::string y;

public:
    // B 类的构造函数,带有参数
    B(int _x, const std::string& _y) : x(_x), y(_y) {
        std::cout << "B constructor called: x = " << x << ", y = " << y << std::endl;
    }

    void show() {
        std::cout << "B::show() called: x = " << x << ", y = " << y << std::endl;
    }
};

class A {
private:
    B b;  // A 类成员变量是 B 类对象

public:
    // A 类的构造函数,使用初始化列表初始化 B 类成员
    A(int x, const std::string& y) : b(x, y) {
        std::cout << "A constructor called." << std::endl;
    }

    void callB() {
        b.show();  // 调用 B 类的成员函数
    }
};

int main() {
    A a(10, "Hello");  // 创建 A 类对象时,B 类对象将被初始化
    a.callB();  // 调用 A 类的方法,通过 A 类的成员对象调用 B 类的方法

    return 0;
}

输出结果:

B constructor called: x = 10, y = Hello
A constructor called.
B::show() called: x = 10, y = Hello

解析:

1. B 类构造函数
  • B 类有两个成员:xy。我们通过构造函数 B(int _x, const std::string& _y) 来初始化这两个成员。
  • A 类的构造函数中,使用初始化列表 : b(x, y) 来调用 B 类的构造函数并传递相应的参数。
2. A 类构造函数
  • A 类的构造函数中,B 类成员 b 通过初始化列表进行初始化,b(x, y) 调用 B 的构造函数,将 xy 的值传递给 B 类的构造函数。
  • 如果我们没有显式地使用初始化列表,B 类的默认构造函数(如果有的话)将会先被调用,然后再对 xy 进行赋值。这样做的效率较低,并且不太符合我们需要的初始化顺序。
3. callB 函数
  • callB() 函数调用了 B 类的 show() 方法,它输出 B 类成员的值:xy
4. 输出解释
  • 当我们创建 A 类的对象 a 时,首先会调用 B 类的构造函数来初始化 B 类的成员 xy,输出 B constructor called: x = 10, y = Hello
  • 然后会输出 A constructor called.,表明 A 类的构造函数被调用。
  • 最后,通过 a.callB(),会输出 B::show() called: x = 10, y = Hello,显示 B 类成员的值。

为什么使用初始化列表?

  1. 初始化效率

    • A 类构造函数的初始化列表中,我们直接初始化了 B 类的成员 xy,这比在构造函数体内先默认构造 B 对象然后再赋值要高效得多。
  2. 避免不必要的默认构造

    • 如果不使用初始化列表,C++ 会先使用 B 类的默认构造函数(如果有的话)构造对象,然后再进行赋值。对于像 intstd::string 这样的类型,默认构造和赋值都是有效的,但这会多进行一次不必要的赋值操作,浪费了效率。
  3. 显式控制初始化顺序

    • B 类的成员变量 xy 在初始化列表中明确初始化,这样可以控制它们的初始化顺序,确保它们在对象构造时按照正确的顺序被初始化。
  4. 必要性

    • 如果 B 类的成员包含 constreference 类型(例如 const intint&),它们就无法在构造函数体内通过赋值进行初始化,而必须通过初始化列表来初始化。

总结:

  • 对于包含多个成员变量(特别是对象类型成员)的类,使用初始化列表是最佳实践。
  • 初始化列表能够提高效率,避免多余的构造和赋值操作。
  • 通过初始化列表可以确保成员变量按照正确的顺序初始化,并且对于 const 或引用类型成员,初始化列表是唯一可行的方式。

http://www.kler.cn/a/471227.html

相关文章:

  • 【机器学习】机器学习的基本分类-自监督学习(Self-supervised Learning)
  • TCP 如何获取端口信息
  • 【WPF】使用BitmapImage给Image的Source赋值,并释放原占用资源,避免删除原文件时导致程序崩溃
  • WebSocket底层原理及 java 应用
  • 小白学Pytorch
  • 基于transformer的目标检测:DETR
  • 在AI浪潮中,RSS3为何会被低估其价值?有何潜力
  • donet (MVC)webAPI 的接受json 的操作
  • 一则问答:211集成电路专业,转互联网还是FPGA?
  • 小程序未来趋势预测:技术革新与市场前景
  • 任务调度之Quartz(二):Quartz体系结构
  • 基于SpringBoot的乐器商城购物推荐系统
  • R语言的网络编程
  • 计算机接口实验报告:8255并行接口实验
  • 【每日学点鸿蒙知识】跳转三方地图、getStringSync性能、键盘避让模式等
  • 51单片机——8*8LED点阵
  • 前端图像处理实战: 基于Web Worker和SIMD优化实现图像转灰度功能
  • MongoDB-文章目录
  • R语言的语法糖
  • 深入浅出Node.js-1(node.js入门)
  • 如何提升RAG系统整体效果:从索引构建-问句理解-混合搜索+语义排序着手,评估系统
  • 编排式 Saga 模式
  • WLAN基本原理与配置
  • C++ 数据结构与算法——寻找最大素数因子的整数
  • FPGA实现UART对应的电路和单片机内部配合寄存器实现的电路到底有何区别?
  • Hadoop解决数据倾斜方法