
个人主页 : 流年如梦
专栏 : 《零基础轻松入门C语言》 《数据结构:从入门到掌握》 《C++编程研习录》
文章目录
- [一.类的 6 个默认成员函数](#一.类的 6 个默认成员函数)
- 二.构造函数
- 三.析构函数
- 四.拷贝构造函数
-
- [4.1 作用](#4.1 作用)
- 4.2必须传引用,不能传值!
- 4.3浅拷贝与深拷贝
- [4.4Stack 深拷贝拷贝构造](#4.4Stack 深拷贝拷贝构造)
- 4.5拷贝构造调用场景
- 五.赋值运算符重载
-
- [5.1 作用](#5.1 作用)
- [5.2 特征](#5.2 特征)
- [5.3 标准写法](#5.3 标准写法)
- [5.4 拷贝构造 vs 赋值重载](#5.4 拷贝构造 vs 赋值重载)
- [5.5 Stack 深拷贝赋值重载](#5.5 Stack 深拷贝赋值重载)
- 六.运算符重载基础
-
- [6.1 什么是运算符重载](#6.1 什么是运算符重载)
- [6.2 不能重载的 5 个运算符](#6.2 不能重载的 5 个运算符)
- [6.3 前置 ++ 与 后置 ++ 区分](#6.3 前置 ++ 与 后置 ++ 区分)
- [七.const 成员函数](#七.const 成员函数)
- 八.取地址重载
- [九.日期类 Date(实践作业)](#九.日期类 Date(实践作业))
- 🎯总结
- ⚠️易错点
前言
类和对象是 C++ 面向对象编程的核心,而默认成员函数则是类的基石。本文将系统讲解构造、析构、拷贝构造、赋值重载四大核心函数,深入剖析浅拷贝与深拷贝、const 成员函数、运算符重载等高频考点,并附上可直接运行的日期类完整代码。内容由浅入深、通俗易懂
一.类的 6 个默认成员函数
如果我们不写任何函数,编译器会自动生成 6 个默认成员函数:
- 构造函数 --> 初始化对象
- 析构函数 --> 清理资源
- 拷贝构造函数 --> 用对象初始化新对象
- 赋值运算符重载 --> 对象之间赋值
- 普通对象取地址重载
const对象取地址重载
重点掌握前 4 个,后两个几乎不用自己实现
二.构造函数
2.1作用
对象创建时自动调用 ,完成初始化,替代 Init()
2.2特征
- 函数名 = 类名
- 无返回值(不用写 void)
- 对象实例化时自动调用
- 可以重载
- 不写则编译器生成无参默认构造
- 默认构造 = 无参 + 全缺省 + 编译器生成(只能存在一个)
2.3举例
cpp
class Date
{
public:
// 无参构造(默认构造)
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
// 带参构造
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
// 全缺省构造(默认构造)
// Date(int year=1, int month=1, int day=1)
// {}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1; // 调用无参构造
Date d2(2025,4,18); // 调用带参构造
// Date d3(); // 错误写法,会被当成函数声明
return 0;
}
代码没有报错 :

2.4编译器默认生成构造的行为
- 内置类型 (
int/char/ 指针):不处理,随机值 - 自定义类型(类对象):调用它的默认构造
三.析构函数
3.1作用
对象生命周期结束时自动调用 ,完成资源释放,替代 Destroy()
3.2特征
- 名字:
~类名- 无参、无返回值
- 一个类只能有一个
- 不写则编译器自动生成
- 先构造的对象后析构
3.3栈类示例(有资源必须写析构)
cpp
class Stack
{
public:
Stack(int n = 4)
{
_a = (int*)malloc(sizeof(int) * n);
_capacity = n;
_top = 0;
}
// 析构函数
~Stack()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
int* _a;
size_t _capacity;
size_t _top;
};
3.4什么时候必须写析构
只要类申请了动态资源 (malloc / new),就必须写析构,否则内存泄漏
四.拷贝构造函数
4.1 作用
用一个已存在的对象 去初始化创建一个新对象
写法:
cpp
类名(const 类名& other)
4.2必须传引用,不能传值!
传值会引发无穷递归,因为传值需要拷贝,拷贝又要调用拷贝构造......
cpp
// 错误写法(递归爆炸)
Date(Date d)
{}
// 正确写法
Date(const Date& d)
{}
4.3浅拷贝与深拷贝
浅拷贝(编译器默认生成)
- 逐字节拷贝
- 指针会指向同一块内存
- 析构两次 --> 崩溃
深拷贝(需要自己写)
- 重新开辟空间
- 拷贝内容
- 两个对象独立
4.4Stack 深拷贝拷贝构造
cpp
Stack(const Stack& st)
{
// 新开空间
_a = (int*)malloc(sizeof(int) * st._capacity);
memcpy(_a, st._a, sizeof(int) * st._top);
_top = st._top;
_capacity = st._capacity;
}
4.5拷贝构造调用场景
Date d2(d1);Date d2 = d1;- 函数传值传参
- 函数传值返回
五.赋值运算符重载
5.1 作用
让两个已经存在的对象可以用 = 赋值
5.2 特征
- 必须是成员函数
- 参数:
const 类名&- 返回值:
类名&(支持连续赋值)- 必须检查自己给自己赋值
5.3 标准写法
cpp
Date& operator=(const Date& d)
{
// 防止自己给自己赋值
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
5.4 拷贝构造 vs 赋值重载
cpp
Date d1(2024,7,5);
Date d2 = d1; // 拷贝构造(创建新对象)
Date d3;
d3 = d1; // 赋值重载(已存在对象赋值)
5.5 Stack 深拷贝赋值重载
cpp
Stack& operator=(const Stack& st)
{
if (this != &st)
{
free(_a);
_a = (int*)malloc(sizeof(int) * st._capacity);
memcpy(_a, st._a, sizeof(int) * st._top);
_top = st._top;
_capacity = st._capacity;
}
return *this;
}
六.运算符重载基础
6.1 什么是运算符重载
给类类型对象重新定义运算符的含义
写法:
cpp
返回值 operator运算符(参数)
6.2 不能重载的 5 个运算符
.* |
:: |
sizeof |
? : |
. |
|---|
6.3 前置 ++ 与 后置 ++ 区分
- 前置++:
Date& operator++() - 后置++:
Date operator++(int)(int 是占位参数)
七.const 成员函数
7.1作用
const 修饰 this指针 ,表示函数内不能修改成员变量
7.2写法
cpp
void Print() const //写在最右边
{
cout << _year << endl;
}
7.3权限规则
const对象只能调用const成员函数- 非
const对象可以调用任意函数
八.取地址重载
编译器默认生成即可,几乎不用自己写
cpp
Date* operator&()
{
return this;
}
const Date* operator&() const
{
return this;
}
九.日期类 Date(实践作业)
9.1Data.h
cpp
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
class Date
{
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1);
bool CheckDate();
int GetMonthDay(int year, int month);
bool operator<(const Date& d) const;
bool operator<=(const Date& d) const;
bool operator>(const Date& d) const;
bool operator>=(const Date& d) const;
bool operator==(const Date& d) const;
bool operator!=(const Date& d) const;
Date& operator+=(int day);
Date operator+(int day) const;
Date& operator-=(int day);
Date operator-(int day) const;
int operator-(const Date& d) const;
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
void Print() const;
private:
int _year;
int _month;
int _day;
};
9.2Data.cpp
cpp
#include "Data.h"
int Date::GetMonthDay(int year, int month)
{
static int arr[] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
return 29;
return arr[month];
}
bool Date::CheckDate()
{
if (_month < 1 || _month > 12 || _day < 1 || _day > GetMonthDay(_year, _month))
return false;
return true;
}
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
cout << "日期非法" << endl;
}
bool Date::operator<(const Date& d) const
{
if (_year < d._year) return true;
if (_year == d._year && _month < d._month) return true;
if (_year == d._year && _month == d._month && _day < d._day) return true;
return false;
}
bool Date::operator<=(const Date& d) const { return *this < d || *this == d; }
bool Date::operator>(const Date& d) const { return !(*this <= d); }
bool Date::operator>=(const Date& d) const { return !(*this < d); }
bool Date::operator==(const Date& d) const
{
return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator!=(const Date& d) const { return !(*this == d); }
Date& Date::operator+=(int day)
{
if (day < 0) return *this -= -day;
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
Date Date::operator+(int day) const
{
Date tmp = *this;
tmp += day;
return tmp;
}
Date& Date::operator-=(int day)
{
if (day < 0) return *this += -day;
_day -= day;
while (_day <= 0)
{
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day) const
{
Date tmp = *this;
tmp -= day;
return tmp;
}
int Date::operator-(const Date& d) const
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
void Date::Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日";
return out;
}
istream& operator>>(istream& in, Date& d)
{
cout << "请输入年月日:";
in >> d._year >> d._month >> d._day;
if (!d.CheckDate())
cout << "日期非法" << endl;
return in;
}
🎯总结
- 构造函数:创建对象自动调用,初始化,可重载
- 析构函数:对象销毁自动调用,释放资源,只能一个
- 拷贝构造:用对象初始化新对象,必须传引用,深浅拷贝是关键
- 赋值重载 :已存在对象赋值,必须检查自身赋值,返回
* this - 浅拷贝:编译器默认生成,指针类型会崩溃
- 深拷贝:自己实现,重新开空间,拷贝数据
- const 成员 :修饰
this,不能修改成员,const对象只能调用const函数 - 日期类:运算符重载最经典综合练习
⚠️易错点
- 拷贝构造传值导致无穷递归
- 有指针资源不写深拷贝,程序崩溃
- 赋值重载不判断自己给自己赋值
- 分不清拷贝构造 和赋值重载
const对象调用非const成员函数报错- 无参构造写成
Date d();被当成函数声明- 内置类型成员,编译器默认构造不初始化
👀 关注 我们一路同行,从入门到大师,慢慢沉淀、稳步成长
❤️ 点赞 鼓励原创,让优质内容被更多人看见
⭐ 收藏 收好核心知识点与实战技巧,需要时随时查阅
💬 评论 分享你的疑问或踩坑经历,一起交流避坑、共同进步