构造函数的作用:对象刚被创建出来的瞬间,自动给对象里面的成员变量设置初始值,保证对象创建完成后,数据就是合法有效的,不会是随机垃圾值。
如果不写构造函数:创建对象后,对象里面的成员变量的值是内存里残留的随机乱码(垃圾值),很容易程序出错。
完整示例代码(继续沿用 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
class:C++ 关键字,告诉编译器,接下来要定义一个类(图纸)。MobilePhone:类名,自定义图纸名称。{}:大括号,类的全部内容写在里面;类定义结束,末尾必须写};
public:
public:访问修饰符。写在这里的构造函数、成员函数对外公开,main 函数可以调用。
MobilePhone(string brand, int storage)
{
m_brand = brand;
m_storage = storage;
}
MobilePhone:构造函数名,必须和类名完全相同。编译器识别这个特殊函数的标记。没有写任何返回类型(不能写 void、int),这是构造函数硬性语法。
(string brand, int storage):参数列表string brand:形参 brand,字符串类型,临时局部变量。接收我们创建对象时传入的手机品牌,只在这个构造函数执行期间存在。int storage:形参 storage,整型临时变量,接收传入的存储大小。
{}:函数体,大括号内,就是对象创建时自动执行的初始化代码。m_brand = brand;m_brand:私有成员变量,属于新建出来的这个对象身上的变量。每个对象都有独立一份。前缀m_代表 member 成员。=:赋值运算符,把右边临时变量 brand 的值,拷贝保存到对象自己的 m_brand 里面。- 含义:把传入的品牌名字,存进当前手机对象的品牌属性。
m_storage = storage;把传入的存储数值,保存到当前对象的 m_storage 成员变量。
这两行就是构造函数核心工作:对象刚创建,立刻给属性赋值。
void showInfo()
{
cout << "手机品牌:" << m_brand << ",存储:" << m_storage << "GB" << endl;
}
void:返回值类型,函数执行完毕不返回任何数据。showInfo:普通成员函数名字,功能是打印当前手机对象的信息。():圆括号,无参数,调用时不需要传入数据。cout:控制台输出,打印文字与变量的值。
private:
string m_brand;
int m_storage;
private:私有修饰符。这两个成员变量只能在当前类内部的函数访问,main 函数不能直接读写。string m_brand;:成员变量,用来保存手机品牌。int m_storage;:成员变量,保存手机存储容量。
第三部分 main 函数逐词拆解,重点:MobilePhone phone1("华为", 256);
MobilePhone phone1("华为", 256);
MobilePhone:类名(图纸),告诉编译器,使用 MobilePhone 这个图纸创建对象。phone1:对象名(实例名)。在内存开辟一块空间,用来存放 phone1 这个手机对象。("华为", 256):实参,传给构造函数。"华为"传递给构造函数形参brand256传递给构造函数形参storage
写这一行代码的瞬间,自动调用构造函数,不需要我们手动调用。
;分号,C++ 语句结束标记。
整句含义:按照 MobilePhone 图纸,在内存创建 phone1 对象,自动执行构造函数,把品牌、存储存入 phone1 对象的成员变量。
phone1.showInfo();
phone1:对象名称,代表我们操作 phone1 这个手机实体。.点号(成员访问运算符),读作 “的”,用来访问对象里面的成员函数。showInfo:成员函数名字。():函数调用标记,带括号才代表执行函数内部代码。;语句结束。
含义:调用 phone1 对象的 showInfo 函数,读取 phone1 已经初始化好的成员变量,打印信息。
MobilePhone phone2("小米", 512);
phone2.showInfo();
- 创建全新独立对象 phone2,开辟新内存,和 phone1 互不干扰。
- 自动调用构造函数,将 phone2 的 m_brand 赋值 "小米",m_storage 赋值 512。
- 调用 showInfo 打印 phone2 自己的数据。
return 0;
main 函数执行结束,栈上创建的 phone1、phone2 对象自动销毁,内存释放。
第四部分 完整执行流程,一步一步跟着走
- 执行
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 对象初始化完成,数据是有效的,不再是乱码。 - 执行
phone1.showInfo();访问 phone1 对象,读取已经初始化好的成员变量,打印内容。 - 执行
MobilePhone phone2("小米", 512);开辟新内存创建 phone2 对象,自动调用构造函数完成初始化。phone2 和 phone1 完全独立。 - 执行
phone2.showInfo();打印 phone2 的数据。 return 0;main 结束,phone1、phone2 销毁,内存回收。
第五部分 核心总结:构造函数的价值(回答你的问题)
- 自动初始化:创建对象自动执行,不用我们创建对象后,再手动调用函数赋值。 不写构造的写法(麻烦,容易忘记):
MobilePhone phone1; phone1.init("华为",256); // 需要手动调用,很容易漏掉这一行 - 保证对象合法:对象创建完成,成员变量就有有效值,避免内存垃圾值引发异常。
- 强制初始化:带参数的构造函数,强制用户创建对象的时候必须传入数据,不能创建一个空的、无效的手机对象。
第六部分 新手易错点
- 构造函数不能手动调用,不能写
phone1.MobilePhone("苹果",128);,语法错误。 - 只要手写了带参构造,编译器不会再自动生成默认无参构造,不能直接写
MobilePhone p;,编译报错。 - 构造函数名字必须和类名完全一致,不能自定义别的名字。
- 构造函数没有返回值,不能加 void。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/zhanghaha1314/article/details/165749014




