保姆级 C++ 入门:彻底搞懂构造、析构等 6 大核心成员函数
·
刚学 C++ 的朋友,大概率都会被类和对象里的各种 “函数” 绕晕:构造函数、析构函数、拷贝构造、赋值重载…… 这些到底是干嘛的?为什么系统非要自动生成?自己写和不写有什么区别?
这篇文章我用最通俗的大白话 + 超多代码示例 + 生活类比,把 C++ 类中6 大核心成员函数一次性讲透,不管是新手入门、面试备考,还是复习巩固,看完都能彻底吃透。
全文无晦涩术语,循序渐进,建议收藏慢慢看!
一、先搞懂:什么是 C++ 的 “6 大核心成员函数”?
只要你定义了一个类(class),哪怕里面一行代码都不写,C++ 编译器也会自动给你生成 6 个函数,它们是对象生命周期的 “管家”,负责对象的创建、复制、赋值、销毁。
这 6 个函数就是:
- 构造函数 —— 对象出生时调用
- 析构函数 —— 对象死亡时调用
- 拷贝构造函数 —— 用一个对象复制出新对象
- 拷贝赋值运算符重载 —— 两个已存在对象互相赋值
- 移动构造函数 (C++11) —— 高效 “偷” 资源创建新对象
- 移动赋值运算符重载 (C++11) —— 高效 “偷” 资源赋值
二、构造函数:对象的 “出生仪式”
1. 通俗理解
类比:新生儿出生时的初始化(起名字、办户口、分配房间)
- 对象被创建时,自动调用
- 作用:给对象的成员变量初始化,避免野值、空指针、未初始化的 bug
- 没有返回值,连
void都不用写 - 函数名必须和类名完全一样
2. 语法规则
class 类名{
public:
// 构造函数
类名(参数列表){
// 初始化代码
}
};
3. 代码示例:无参构造 + 有参构造
#include <iostream>
#include <string>
using namespace std;
// 定义一个人类
class Person {
public:
// 1. 无参构造函数(默认构造)
Person() {
m_name = "无名氏";
m_age = 0;
cout << "无参构造调用:对象出生啦!" << endl;
}
// 2. 有参构造函数
Person(string name, int age) {
m_name = name;
m_age = age;
cout << "有参构造调用:对象出生啦!" << endl;
}
// 打印信息
void showInfo() {
cout << "姓名:" << m_name << " 年龄:" << m_age << endl;
}
private:
string m_name;
int m_age;
};
int main() {
// 创建对象1:调用无参构造
Person p1;
p1.showInfo();
// 创建对象2:调用有参构造
Person p2("张三", 20);
p2.showInfo();
return 0;
}
4. 关键知识点
- 一个类可以有多个构造函数(重载)
- 如果你不写构造函数,编译器会自动生成一个空的无参构造
- 一旦你自己写了构造函数,编译器就不会再自动生成默认构造了
- 构造函数不能被手动调用,只能在创建对象时自动执行
三、析构函数:对象的 “告别仪式”
1. 通俗理解
类比:人去世后的善后工作(注销户口、清理遗产、归还借的东西)
- 对象销毁(生命周期结束)时,自动调用
- 作用:释放对象占用的资源(内存、文件、网络连接、锁等)
- 函数名:
~类名,无参数、无返回值 - 一个类只能有一个析构函数,不能重载
2. 语法规则
~类名(){
// 释放资源代码
}
3. 为什么必须手写析构?(重点!)
如果你的类里没有动态分配内存(new/malloc),不写析构也没事;但如果用了new,必须手写析构用delete释放,否则会造成内存泄漏!
4. 代码示例:带动态内存的析构
#include <iostream>
#include <cstring>
using namespace std;
class Student {
public:
// 构造函数:用new分配内存
Student(const char* name) {
// 动态分配内存
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
cout << "构造函数:分配内存成功" << endl;
}
// 析构函数:释放new的内存
~Student() {
if (m_name != NULL) {
// 必须释放!
delete[] m_name;
m_name = NULL;
cout << "析构函数:内存已释放" << endl;
}
}
void show() {
cout << "姓名:" << m_name << endl;
}
private:
// 指针成员,指向堆内存
char* m_name;
};
int main() {
{
// 进入作用域,创建对象
Student s("小明");
s.show();
}
// 离开作用域,对象销毁,自动调用析构
cout << "主函数结束" << endl;
return 0;
}
5. 关键知识点
- 先构造的对象后析构,后构造的对象先析构(栈的规则:先进后出)
- 不写析构,编译器自动生成空析构
- 只要类里有指针 / 动态资源,必须手写析构!
四、拷贝构造函数:对象的 “克隆术”
1. 通俗理解
类比:用一张身份证复印件,复制出一张一模一样的新身份证
- 用已经存在的对象,创建一个同类型的新对象
- 属于构造函数的一种,创建对象时自动调用
- 参数必须是引用(一般加 const)
2. 语法规则
类名(const 类名& 其他对象){
// 拷贝成员变量
}
3. 什么时候会调用拷贝构造?
3 种最常见场景:
- 用旧对象初始化新对象
- 对象作为函数值传递参数
- 函数返回值类型的对象
4. 浅拷贝 vs 深拷贝(面试必考!)
这是新手最容易踩坑的地方!
- 浅拷贝(默认拷贝构造):只拷贝变量的值,指针只拷贝地址→ 两个对象指向同一块内存,析构时会重复释放内存,程序崩溃!
- 深拷贝(自己手写):重新分配内存,再拷贝内容→ 两个对象独立,安全无冲突
5. 代码示例:深拷贝拷贝构造
#include <iostream>
#include <cstring>
using namespace std;
class Person {
public:
Person(const char* name) {
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
// 手写拷贝构造:深拷贝
Person(const Person& p) {
// 1. 分配新内存
m_name = new char[strlen(p.m_name) + 1];
// 2. 拷贝内容
strcpy(m_name, p.m_name);
cout << "拷贝构造调用:克隆成功" << endl;
}
~Person() {
if (m_name) {
delete[] m_name;
m_name = NULL;
}
}
void show() {
cout << "姓名:" << m_name << endl;
}
private:
char* m_name;
};
int main() {
Person p1("李四");
// 调用拷贝构造
Person p2 = p1;
p1.show();
p2.show();
return 0;
}
6. 关键结论
只要类里有指针成员,必须手写拷贝构造(深拷贝)!否则一定会出现重复释放内存的崩溃 bug。
五、拷贝赋值运算符重载:对象的 “互相赋值”
1. 通俗理解
类比:把 A 笔记本里的所有内容,完整复制到 B 笔记本里(A、B 都已经存在)
- 两个已经创建好的对象之间用
=赋值 - 和拷贝构造的区别:
- 拷贝构造:创建新对象
- 赋值重载:给旧对象赋值
2. 语法规则
类名& operator=(const 类名& 其他对象){
// 赋值逻辑
}
3. 必须注意 3 个细节
- 检查自赋值(自己 = 自己)
- 先释放当前对象的旧内存
- 返回
*this,支持链式赋值(a=b=c)
4. 代码示例:赋值重载
// 在上面Person类中添加赋值重载
Person& operator=(const Person& p) {
// 1. 防止自赋值
if (this == &p) {
return *this;
}
// 2. 释放当前对象原有内存
if (m_name) {
delete[] m_name;
m_name = NULL;
}
// 3. 深拷贝
m_name = new char[strlen(p.m_name) + 1];
strcpy(m_name, p.m_name);
cout << "赋值重载调用" << endl;
// 4. 返回自身,支持链式赋值
return *this;
}
使用:
int main(){
Person p1("张三");
Person p2("李四");
// 赋值重载
p2 = p1;
// 链式赋值
Person p3 = p2 = p1;
return 0;
}
5. 核心规则
拷贝构造和赋值重载,要么都写,要么都不写!(有指针就必须都手写深拷贝)
六、移动构造 & 移动赋值:C++11 的 “偷家神器”
1. 通俗理解
前面的拷贝是 “复制”,慢;移动是 “搬家”—— 直接把别人的资源抢过来,不复制,速度极快。
类比:
- 拷贝:把一本书手抄一遍,费时费力
- 移动:直接把书拿过来,一秒完成
2. 适用场景
临时对象(即将销毁的对象)、函数返回值、std::move转移对象
3. 移动构造语法
类名(类名&& 其他对象){
// 偷走别人的资源
成员 = 其他对象.成员;
// 把别人的指针置空,防止被析构
其他对象.成员 = NULL;
}
4. 移动赋值语法
类名& operator=(类名&& 其他对象){
// 偷资源 + 置空源对象
}
5. 优势
- 不分配新内存
- 不拷贝数据
- 性能提升巨大(尤其字符串、容器、大对象)
七、终极总结:6 大函数一张表搞定
| 函数名称 | 调用时机 | 作用 | 必须手写场景 |
|---|---|---|---|
| 构造函数 | 创建对象时 | 初始化成员变量 | 需要自定义初始化 |
| 析构函数 | 对象销毁时 | 释放资源 | 有 new / 指针 / 文件 / 网络连接 |
| 拷贝构造函数 | 用对象创建新对象 | 克隆对象 | 类中有指针(深拷贝) |
| 拷贝赋值运算符重载 | 两个对象 = 赋值 | 对象赋值 | 类中有指针(深拷贝) |
| 移动构造函数 (C++11) | 临时对象创建新对象 | 高效偷取资源 | 优化性能、大对象 |
| 移动赋值运算符重载 (C++11) | 临时对象赋值给对象 | 高效偷取资源 | 优化性能、大对象 |
更多推荐



所有评论(0)