大皓皓非常然头像
C++为什么类里面还要加构造函数?例程【逐词逐句保姆级详解】封面图

C++为什么类里面还要加构造函数?例程【逐词逐句保姆级详解】

构造函数的作用:对象刚被创建出来的瞬间,自动给对象里面的成员变量设置初始值,保证对象创建完成后,数据就是合法有效的,不会是随机垃圾值。

如果不写构造函数:创建对象后,对象里面的成员变量的值是内存里残留的随机乱码(垃圾值),很容易程序出错。

完整示例代码(继续沿用 MobilePhone 手机类,保持前后教程连贯)

#include <iostream>
#include <string>
using namespace std;

// 定义手机类
class MobilePhone
{
public:
    // ========== 构造函数 ==========
    MobilePhone(string brand, int storage)
    {
        m_brand = brand;
        m_storage = storage;
    }

    // 成员函数:打印手机信息
    void showInfo()
    {
        cout << "手机品牌:" << m_brand << ",存储:" << m_storage << "GB" << endl;
    }

private:
    string m_brand;    // 成员变量:手机品牌
    int m_storage;     // 成员变量:存储容量
};

int main()
{
    // 创建对象,自动调用构造函数初始化
    MobilePhone phone1("华为", 256);
    phone1.showInfo();

    MobilePhone phone2("小米", 512);
    phone2.showInfo();

    return 0;
}

运行输出:

手机品牌:华为,存储:256GB
手机品牌:小米,存储:512GB

第一部分:对比【没有构造函数】的坏情况,看懂为什么必须要有

下面这段没有手动写构造函数的代码,用来做对比:

#include <iostream>
#include <string>
using namespace std;

class MobilePhone
{
public:
    void showInfo()
    {
        cout << "手机品牌:" << m_brand << ",存储:" << m_storage << "GB" << endl;
    }
private:
    string m_brand;
    int m_storage;
};

int main()
{
    MobilePhone phone1;
    phone1.showInfo();
    return 0;
}

这里编译器会自动生成一个空的默认构造函数,空函数里面什么都不做。 m_brand、m_storage不会被赋值。m_storage里面的值是内存残留的随机数字(垃圾值),程序会打印乱码,这就是风险! ✅ 所以我们手写构造函数,就是为了杜绝这种随机垃圾值,对象一诞生,数据就准备好。

第二部分 类内部代码逐词逐句拆解

class MobilePhone
  1. class:C++ 关键字,告诉编译器,接下来要定义一个类(图纸)。
  2. MobilePhone:类名,自定义图纸名称。
  3. {}:大括号,类的全部内容写在里面;类定义结束,末尾必须写 };
public:
  • public:访问修饰符。写在这里的构造函数、成员函数对外公开,main 函数可以调用。
MobilePhone(string brand, int storage)
{
    m_brand = brand;
    m_storage = storage;
}
  1. MobilePhone:构造函数名,必须和类名完全相同。编译器识别这个特殊函数的标记。

    没有写任何返回类型(不能写 void、int),这是构造函数硬性语法。

  2. (string brand, int storage):参数列表
    • string brand:形参 brand,字符串类型,临时局部变量。接收我们创建对象时传入的手机品牌,只在这个构造函数执行期间存在。
    • int storage:形参 storage,整型临时变量,接收传入的存储大小。
  3. {}:函数体,大括号内,就是对象创建时自动执行的初始化代码。
  4. m_brand = brand;
    • m_brand:私有成员变量,属于新建出来的这个对象身上的变量。每个对象都有独立一份。前缀m_代表 member 成员。
    • =:赋值运算符,把右边临时变量 brand 的值,拷贝保存到对象自己的 m_brand 里面。
    • 含义:把传入的品牌名字,存进当前手机对象的品牌属性。
  5. m_storage = storage; 把传入的存储数值,保存到当前对象的 m_storage 成员变量。

这两行就是构造函数核心工作:对象刚创建,立刻给属性赋值。

void showInfo()
{
    cout << "手机品牌:" << m_brand << ",存储:" << m_storage << "GB" << endl;
}
  1. void:返回值类型,函数执行完毕不返回任何数据。
  2. showInfo:普通成员函数名字,功能是打印当前手机对象的信息。
  3. ():圆括号,无参数,调用时不需要传入数据。
  4. cout:控制台输出,打印文字与变量的值。
private:
    string m_brand;
    int m_storage;
  1. private:私有修饰符。这两个成员变量只能在当前类内部的函数访问,main 函数不能直接读写。
  2. string m_brand;:成员变量,用来保存手机品牌。
  3. int m_storage;:成员变量,保存手机存储容量。

第三部分 main 函数逐词拆解,重点:MobilePhone phone1("华为", 256);

MobilePhone phone1("华为", 256);
  1. MobilePhone:类名(图纸),告诉编译器,使用 MobilePhone 这个图纸创建对象。
  2. phone1:对象名(实例名)。在内存开辟一块空间,用来存放 phone1 这个手机对象。
  3. ("华为", 256):实参,传给构造函数。
    • "华为" 传递给构造函数形参brand
    • 256传递给构造函数形参storage

    写这一行代码的瞬间,自动调用构造函数,不需要我们手动调用。

  4. ; 分号,C++ 语句结束标记。

整句含义:按照 MobilePhone 图纸,在内存创建 phone1 对象,自动执行构造函数,把品牌、存储存入 phone1 对象的成员变量。

phone1.showInfo();
  1. phone1:对象名称,代表我们操作 phone1 这个手机实体。
  2. . 点号(成员访问运算符),读作 “的”,用来访问对象里面的成员函数。
  3. showInfo:成员函数名字。
  4. ():函数调用标记,带括号才代表执行函数内部代码。
  5. ; 语句结束。

含义:调用 phone1 对象的 showInfo 函数,读取 phone1 已经初始化好的成员变量,打印信息。

MobilePhone phone2("小米", 512);
phone2.showInfo();
  1. 创建全新独立对象 phone2,开辟新内存,和 phone1 互不干扰。
  2. 自动调用构造函数,将 phone2 的 m_brand 赋值 "小米",m_storage 赋值 512。
  3. 调用 showInfo 打印 phone2 自己的数据。
return 0;

main 函数执行结束,栈上创建的 phone1、phone2 对象自动销毁,内存释放。

第四部分 完整执行流程,一步一步跟着走

  1. 执行 MobilePhone phone1("华为", 256); ① 在内存开辟一块内存空间,准备存放 phone1 对象。此时对象里面 m_brand、m_storage 的值还是随机垃圾值。 ② 自动调用构造函数 MobilePhone (string brand, int storage) ③ 实参 "华为" 复制给形参 brand;实参 256 复制给形参 storage ④ 执行 m_brand = brand; → phone1 对象的 m_brand = "华为" ⑤ 执行 m_storage = storage; → phone1 对象的 m_storage = 256 ⑥ 构造函数执行完毕,临时变量 brand、storage 销毁。 ✅ 现在 phone1 对象初始化完成,数据是有效的,不再是乱码。
  2. 执行 phone1.showInfo(); 访问 phone1 对象,读取已经初始化好的成员变量,打印内容。
  3. 执行 MobilePhone phone2("小米", 512); 开辟新内存创建 phone2 对象,自动调用构造函数完成初始化。phone2 和 phone1 完全独立。
  4. 执行 phone2.showInfo(); 打印 phone2 的数据。
  5. return 0; main 结束,phone1、phone2 销毁,内存回收。

第五部分 核心总结:构造函数的价值(回答你的问题)

  1. 自动初始化:创建对象自动执行,不用我们创建对象后,再手动调用函数赋值。 不写构造的写法(麻烦,容易忘记):
    MobilePhone phone1;
    phone1.init("华为",256); // 需要手动调用,很容易漏掉这一行
    
  2. 保证对象合法:对象创建完成,成员变量就有有效值,避免内存垃圾值引发异常。
  3. 强制初始化:带参数的构造函数,强制用户创建对象的时候必须传入数据,不能创建一个空的、无效的手机对象。

第六部分 新手易错点

  1. 构造函数不能手动调用,不能写 phone1.MobilePhone("苹果",128);,语法错误。
  2. 只要手写了带参构造,编译器不会再自动生成默认无参构造,不能直接写MobilePhone p;,编译报错。
  3. 构造函数名字必须和类名完全一致,不能自定义别的名字。
  4. 构造函数没有返回值,不能加 void。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/zhanghaha1314/article/details/165749014

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--