类的六个默认成员函数
如果一个类中一个默认成员函数也没有那这个类我们叫作空类。
但是如果一个类中每有默认成员函数的话,会怎样呢?答案是编译器会帮我们自动实现类。
拷贝构造
构造函数是一个特殊的成员函数,函数名和类名相同没有返回值。创建类类型对象时,由编译器自动调用。并且在对象整个生命空间周期类只调用一次。
构造函数作用在于初始化对象类似于int a=1;
给啊初始化为1;
我们看一下具体的代码片段:
cpp
//重点关注注释部分
//代码是我做题通过后进行拷贝下来的代码,可以适当理解一下,题目最后放在末尾
class date {
public:
date(int year, int month, int day , int days) //构造函数,没有返回值,但是不需要void
{
_year = year;
_month = month;
_day = day;
_days = days;
}
date(int year = 2025, int month = 4 , int day =23 , int days = 100) //函数重载《也是无参构造函数,无参构造函数一个类中只能存在一个》
{
_year = year;
_month = month;
_day = day;
_days = days;
}
int Get_month(int month)
{
int arr_month[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month==2 && _year % 4 == 0 && _year % 100 != 0 || _year % 400 == 0)
{
arr_month[2] = 29;
}
return arr_month[month];
}
date datecount()
{
int i = Get_month(_month);
_days += _day;
while (_days > i)
{
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
_days -= i;
i= Get_month(_month);
}
_day = _days;
return *this;
}
void Print()
{
//打印年
cout << _year << "-";
//打印月份
if (_month < 10)
{
cout << 0 << _month << "-";
}
else
cout << _month << "-";
//打印日
if (_day < 10)
{
cout << 0 << _day << endl;
}
else
cout << _day << endl;
}
private:
int _year;
int _month;
int _day;
int _days;
};
int main()
{
int n;
cin >> n;
while (n--)
{
int year, month, day;
int days;
cin >> year >> month >> day >> days;
date a(year, month, day, days); //编译器自动调用构造函数,用来初始化对象。
a.datecount().Print();
//日期打印
}
return 0;
}
还有一点是,构造函数跟其他成员函数一样可支持重载。
你可能会问,如果我没有写构造函数会怎样?
这个时候编译器就会有自己的想法,编译器会自己生成一个无参的构造函数,并进行调用。但是这有时候我们并不知道编译器构造的成员函数是怎样的,不知道是否能正确的完成初始。所以在C++11中打了一个补丁。即在内置类型成员变量在类中声明时可以给默认值
cpp
private:
int _year = 2025; //这时声明不是初始化
int _month = 4; //这时声明不是初始化
int _day = 23; //这时声明不是初始化
int _days = 24; //这时声明不是初始化
析构函数
析构函数 也是一个特殊的成员函数,函数名和类名前加一个 ~
符号 ,同样没有返回值,也不需要写 void
。
它的作用是:在对象生命周期结束时自动调用,用来进行清理工作,比如释放内存、关闭文件、断开网络连接等。
基本格式:
cpp
~类名()
{
// 清理操作
}
对于这段日期代码来说,我们的类中没有使用 new
动态分配内存,因此不写析构函数也没关系,编译器会默认生成一个"空析构函数"。
但出于规范,我们可以手动写一个出来作为示例:
cpp
class date {
public:
// 构造函数略
~date()
{
// 目前这个类没有资源需要手动释放
// 这里只是说明析构函数的位置
cout << "析构函数被调用,日期对象被销毁" << endl;
}
// 其他成员函数略
private:
int _year;
int _month;
int _day;
int _days;
};
注意事项:
- 析构函数不能有参数、不能被重载。
- 如果类中有用
new
申请了堆空间(如int* p = new int
),必须写析构函数释放资源,否则会内存泄漏。 - 析构函数通常不需要手动调用,在对象生命周期结束时自动调用。
示例输出位置:
当 main()
函数中执行完这一行:
cpp
date a(year, month, day, days);
对象 a
在这个作用域结束时就会被销毁,自动调用析构函数。为了验证我们可以加入一段测试:
cpp
int main()
{
{
date a(2025, 4, 23, 100);
a.datecount().Print();
} // 在这个大括号结束的位置,a 被销毁,会调用析构函数
return 0;
}
运行时会输出:
2025-08-01
析构函数被调用,日期对象被销毁
小结
函数 | 特点 | 何时调用 |
---|---|---|
构造函数 | 用于初始化对象 | 创建对象时 |
析构函数 | 用于清理资源 | 销毁对象时 |
你可以简单记住它们是"出生与销毁"这一对搭档。
拷贝构造函数
拷贝构造函数用于创建一个新对象,并用一个已有对象进行初始化。
格式:
cpp
date(const date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
_days = d._days;
cout << "调用拷贝构造函数" << endl;
}
特点:
- 参数是一个同类对象的引用 ,并且通常加上
const
。 - 如果不写,编译器会默认生成一个"浅拷贝"的拷贝构造函数。
示例:
cpp
date d1(2025, 4, 23, 100);
date d2 = d1; // 调用拷贝构造函数
当我们传值或返回对象时,也可能调用拷贝构造:
cpp
date foo(date d) // 这里传值会调用拷贝构造
{
return d; // 返回也可能触发拷贝
}
赋值运算符重载
用于对象赋值,即:一个已经存在的对象被另一个对象赋值。
格式:
cpp
date& operator=(const date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
_days = d._days;
}
cout << "调用赋值运算符重载" << endl;
return *this;
}
注意:
- 需要返回
*this
的引用 ,以支持链式赋值a = b = c;
- 自赋值检查很重要:
if (this != &d)
- 如果类中有资源分配(如
new
),一定要手动写赋值运算符重载,避免浅拷贝问题。
const 成员函数
cpp
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
特点:
- 在函数后加
const
表示这个函数不会修改对象的成员变量。 - 只能调用其他
const
成员函数。 - 常用于
const
对象,如:
cpp
const date d(2025, 4, 23, 100);
d.Print(); // 只能调用 const 函数
取地址及 const 取地址操作符重载
普通取地址:
cpp
date* operator&()
{
cout << "调用取地址操作符&" << endl;
return this;
}
const 取地址:
cpp
const date* operator&() const
{
cout << "调用 const 取地址操作符&" << endl;
return this;
}
这两个可以帮助我们在调试或做特殊封装时了解对象的地址行为。