目录
前言:类的6个默认成员函数
一, 构造函数
1. 概念
2. 特性
二, 析构函数
2.1 概念
2.2 特性
2.3 牛刀小试
三, 拷贝构造函数
3.1概念
3. 2 特点
四, 赋值运算符重载
4. 1 运算符重载
五, const成员函数
六,取地址及const取地址操作符重载
七,练习——实现日期计算器
结语
前言:类的6个默认成员函数
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
一, 构造函数
1. 概念
2. 特性
- 1. 函数名与类名相同。
- 2. 无返回值
- 3. 对象实例化时编译器自动调用对应的构造函数。
- 4. 构造函数可以重载。(后面拷贝构造函数会体现)
- 5. 如果存在未自定义默认构造函数,编译器不再生成默认构造函数。
下面是一段自定义构造函数的:
#include
using namespace std;
class Date
{
public:
Date(int year = 2023, int month = 5, int day = 9) // 自定义默认构造函数,设置全缺省参数,对数据进行初始化。
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date z;
Date z1(2012, 12, 12); // 由于我们自定义构造函数支持带参数设置数据初始化。
// 接下来,我们注释掉自定义默认构造函数,来测试一下编译器默认自动生成的构造函数。
}
当我们注释掉我们自定义的构造函数时,我们会发现z对象 的类成员变量,依旧是随机值。这里我们不禁会想,编译器自动生成的默认构造函数似乎什么也没做??
- 内置类型就是语言提供的数据类型,如:int/char…;
- 自定义类型就是我们使用class/struct/union等自己定义的类型。
#include
using namespace std;
class Date
{
public:
Date(int year = 2023, int month = 5, int day = 9) // 自定义默认构造函数,设置全缺省参数,对数据进行初始化。
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
class Time
{
public:
Date _t; // 自定义类型
private: // 内置类型
int _hour;
int _minute;
int _second;
};
int main()
{
Time k;
}
调试结果如下:
特点:
1. 内置函数不做处理。
2. 自定义类型会调用(自定义类型的)默认构造函数。
这里我们就有了两种对内置成员初始化的方法:
- 1. 通过C++补丁初始化;()
- 2. 自定义默认构造函数,同时给缺省参数。
二, 析构函数
2.1 概念
2.2 特性
- 1. 析构函数名是在类名前加上字符 ~。
- 2. 无参数无返回值类型。
- 3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
- 4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
首先我们来看下面代码:
#include
using namespace std;
class Date
{
public:
Date(int year = 2023, int month = 5, int day = 9) // 默认构造函数
{
_year = year;
_month = month;
_day = day;
}
~Date() // 默认析构函数
{
_year = _month = _day = 0;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date z;
return 0;
}
我们将显式析构函数注释掉,让我们测试一下编译器自动生成的默认析构函数的结果,下面的程序我们会看到:编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
#include
using namespace std;
class Date
{
public:
Date(int year = 2023, int month = 5, int day = 9)
{
_year = year;
_month = month;
_day = day;
}
~Date()
{
cout
结果可以看出:
2.3 牛刀小试
看下面代码,输出顺序是什么?
class A
{
public:
A(int a = 0)
{
_a = a;
cout " "
核心思路:遵循栈的思路,先进后出。
三, 拷贝构造函数
3.1概念
在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。
3. 2 特点
- 1. 拷贝构造函数是构造函数的一个重载形式。
- 2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
下面是会发生无穷递归代码:
class Date
{
public:
Date(int year = 2023, int month = 5, int day = 9)
{
_year = year;
_month = month;
_day = day;
}
Date(Date b1) // 正确代码:Date(Date& b1)
{
_year = b1._year;
_month = b1._month;
_day = b1._day;
}
private:
int _year;
int _month;
int _day;
};
无穷递归如图:
换成类类型引用即可解决无穷递归的问题。
class Time
{
public:
Time(int hour = 1 , int minute = 2, int second = 3)
{
_hour = hour;
_minute = minute;
_second = second;
}
Time(const Time& a)
{
_hour = a._hour;
_minute = a._minute;
_second = a._second;
cout
我们发现,编译器自动生成的拷贝构造函数,足够我们进行值拷贝,那么我们还需要自定义拷贝构造函数吗?
// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const DataType& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
DataType *_array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2(s1);
return 0;
}
所以类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,需要深度拷贝,则拷贝构造函数是一定要写的,否则就是浅拷贝。
四, 赋值运算符重载
4. 1 运算符重载
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型参数
- 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义。
- 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。
- .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
下面有有几个应用例子:
class Time
{
public:
Time(int hour = 1 , int minute = 2, int second = 3)
{
_hour = hour;
_minute = minute;
_second = second;
}
Time(const Time& a)
{
_hour = a._hour;
_minute = a._minute;
_second = a._second;
cout (const Time& a)
{
return _hour > a._hour &&
_minute > a._minute &&
_second > a._second;
}
Time& operator=(const Time& a) // 内成员赋值运算符重载
{
_hour = a._hour;
_minute = a._minute;
_second = a._second;
return *this;
}
~Time()
{
cout
这里对赋值运算符进行补充;
1.赋值运算符只能重载成类的成员函数不能重载成全局函数
// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const DataType& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2;
s2 = s1;
return 0;
}
结果分析:
五, const成员函数
让我们看看下面的实例代码吧:
class moss
{
public:
moss(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout
首先我们已经知道const具有限制权限的功能,比如 int this,如图:
对本次事例解析:
六,取地址及const取地址操作符重载
这个比较容易理解,这两个默认成员函数一般不用重新定义 ,编译器默认会生成。 看下面代码:
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
七,练习——实现日期计算器
用C++类编写一个日期计算器,利用今天的运算符重载知识,实现日期+天数,日期-天数,日期-日期的功能。
下面是代码分享:
头文件:
#pragma once
#include
using namespace std;
#include
class Date
{
public:
// 获取某年某月的天数
int GetMonthDay(int year, int month)
{
// 因为需要频繁调用,所以写成内联函数。
static int M[13] = { 0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day = M[month];
if (month == 2 &&
((year % 4 == 0 && year % 100 != 0) ||
(year % 400 == 0)))
{
day++;
}
return day;
}
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout 12 || _day > GetMonthDay(_year, _month))
{
return false;
}
else
{
return true;
}
}
// 拷贝构造函数
// d2(d1)
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
// 析构函数, 全是内置函数,没什么好清理的,调用编译器自动生成的就行。
~Date()
{
;
}
// 日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day);
// 日期-天数
Date operator-(int day);
// 日期-=天数
Date& operator-=(int day);
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 后置--
Date operator--(int);
// 前置--
Date& operator--();
// >运算符重载
bool operator>(const Date& d);
// ==运算符重载
bool operator==(const Date& d);
// >=运算符重载
bool operator >= (const Date& d);
// >(istream& cin, Date& a);
void Print()const // 本身是 Date* const this--是不能修改其指向的this,
//但可以修改其内容,为了能接受被const Date* const this的对象所以需要缩小权限(方法就是函数名后加const)
//反正print函数没有修改功能。
{
cout >(istream& cin, Date& a)
{
cin >> a._year >> a._month >> a._day;
assert(a.CheckDate());
return cin;
}
函数实现源文件:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
// 日期+=天数
Date& Date::operator+=(int day)
{
_day += day;
while ( Date::GetMonthDay(_year, _month) 运算符重载
bool Date::operator>(const Date& d)
{
if ((_year > d._year) ||
(_month > d._month) ||
(_day > d._day))
{
return true;
}
else
{
return false;
}
}
// ==运算符重载
bool Date::operator==(const Date& d)
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
// >=运算符重载
bool Date::operator >= (const Date& d)
{
return (*this > d) || (*this == d);
}
//
测试源文件:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
void Test()
{
const char* WeekRoom[7] = { "周天", "周一", "周二", "周三", "周四", "周五", "周六" };
do
{
cout > opertaton;
Date a1, a2;
int day;
switch (opertaton)
{
case 1:
cout > a1 >> day;
cout > a1 >> day;
cout > a1 >> a2;
cout
结语
本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论;如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net