目录
一.默认成员函数
二.构造函数
三.析构函数
四.拷贝构造函数
五.赋值运算符重载
什么是运算符重载?
赋值运算符重载
六.取地址运算符重载
附件:日期类的实现
一.默认成员函数
在类中,有我们自己实现的成员函数。但是,还有一些“隐而不现”的成员函数,由编译器自己生成,称为默认成员函数。顾名思义,用户无须实现,编译器自动生成。
那么,这些隐而不现的函数是什么,有什么功能呢?
在C++98的标准下,默认生成了6个成员函数:
C++11增加了移动构造和移动赋值两个成员函数(不作讲解)。
我们要明确两点:1.我们不显示实现,这些默认生成的函数行为是什么?
2.我们显示实现后,编译器就不会自动生成了,那该如何实现?
二.构造函数
功能:构造函数,完成的是在用类实例化出对象时,完成对象的初始化工作。
用代码来理解:
typedef int STDataType;
class Stack
{
public:
//Init
void Init(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (_a == nullptr)
{
perror("malloc fail");
return;
}
_top = 0;
_capacity = 0;
}
//构造函数
Stack(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType)*n);
if (_a == nullptr)
{
perror("malloc fail");
return;
}
_top = 0;
_capacity = 0;
}
private:
STDataType* _a;
int _top;
int _capacity;
};
int main( )
{
//手动调用Init函数初始化
Stack st1;
st1.Init();
//自动调用构造函数初始化
//Stack st2;
return 0;}
构造函数的功能相当于代替了Init函数,它的便捷性在于,在创建对象时自动调用。因此,以后便不在需要实现Init函数,转而实现构造函数。
构造函数的特点:
1.函数名:与类名相同
2.无返回值(注意:不是void)
3.可以构成函数重载
4.对象实例化时,自动调用
认识三个默认构造函数(零实参构造函数):
无参构造,全缺省构造,编译器自动生成的构造
我们在对象实例化的过程中,构造函数的调用是自动的,如果我们不传实参,就能调用的构造函数,称为默认构造函数。
Date类样例:
class Date
{
public:
//无参构造
Date()
{
_year = 2024;
_month = 1;
_day = 1;
}
//带参构造
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
//全缺省构造
//Date(int year = 2024, int month = 1, int day = 1)
//{
// _year = year;
// _month = month;
// _day = day;
//}
private:
int _year;
int _month;
int _day;
};
int main( )
{
//假设Date类中,我们实现了一个无参构造和代参构造
Date d1;
Date d2(2000,1,1);
}
由于d1要自动调用构造函数,因此我们不传实参时,它会去找到它的默认构造函数(无参构造)。
int main( )
{
//假设Date类中,我们实现了一个全缺省构造
Date d1;
Date d2(2000,1,1);
}
上述d1去调用类中的全缺省构造,由于我们不传实参,d1会使用缺省值。
注意,全缺省构造和无参构造不能同时存在,否则会产生调用歧义。
可以认为,全缺省构造 = 无参构造 + 带参构造。
用户不显示实现构造函数,编译器默认生成的构造函数的行为:
1.对于内置类型的初始化没有要求。
2.对于自定义类型,调用这个成员变量的默认构造函数。
自定义类型:语言提供的原生数据类型,如char/int/double/指针等。
内置类型:我们使用class/struct定义的类类型。
三.析构函数
功能:与构造函数功能相反,完成对象资源的清理和释放工作。
析构函数的特点:
1.函数名:~类名,如~Date
2.无参,无返回值
3.一个类只能有一个析构函数
4.对象生命周期结束时,自动调用析构函数
5.一个局部域的多个对象,C++规定后定义的先析构
编译器默认生成的析构函数的行为:
1.对于内置类型,不作处理。
2.对于自定义类型,自动调用成员变量的析构函数。需要注意的是,自定义类型无论什么情况都会调用它的析构函数。
四.拷贝构造函数
功能:实例化对象时,用已存在的对象拷贝构造新对象。
拷贝构造函数的特点:
1.函数名与类名相同
2.第一个参数必须是当前类类型的引用
3.无返回值
4.C++规定,自定义类型的传值传参和传值返回,都会去调用拷贝构造
编译器默认生成的拷贝构造函数 的行为:
1.对于内置类型成员变量会完成值拷贝/浅拷贝
2.对于自定义类型的成员变量,会去调用它的拷贝构造
五.赋值运算符重载
什么是运算符重载?
C++语言允许通过运算符重载的形式,给运算符定义新的含义,使得运算符能够适用于自定义类型。
运算符重载本质上是一个函数,函数名有特别的规定:operator运算符,例如:operator+,operator-。函数参数必须包含至少一个自定义类型的参数。
对于二元运算符,运算符左侧的操作数默认作为第一个参数 ,右侧操作数作为第二个参数。如果重载为成员函数,参数个数比运算符操作数少一个;左侧操作数传入隐藏的this指针,右侧操作数传入(第一个)形参位置。
前置++/--与后置++/--的区分:以++为例,重载前置++和后置++时,函数名都是operator++,无法做出很好的区分。于是,C++规定,重载后置++时,参数列表多一个int类型的形式参数。
重载<< 和 >> 时,如果重载成成员函数,隐藏的this指针占据了左操作数的位置,会出现 对象<<cout 或 对象 >> cin 的用法,不符合习惯。所以,上述运算符需要重载为全局函数,把ostream/istream放到第一个形参的位置。
五个无法重载的运算符:.* :: sizeof ?: .
赋值运算符重载
功能:用于两个已存在对象的直接拷贝赋值。
赋值运算符重载的特点:
1.函数名:operator=
2.参数: 建议写成const当前类类型的引用,减少拷贝构造
3.返回类型:建议写成当前类类型的引用,减少拷贝构造
编译器默认生成的赋值运算符重载函数的行为:
1.对于内置类型,完成值拷贝/浅拷贝赋值
2.对于自定义类型,调用自定义类型成员变量的赋值运算符重载
六.取地址运算符重载
功能:不让别人取到当前类对象的(真实)地址,返回一个任意地址
分为const取地址运算符重载和普通取地址运算符重载。
一般无需自己实现,使用编译器默认生成的即可。
扩展:
const 成员函数,cosnt修饰的是隐藏的this指针,因此在const成员函数内,无法修改当前类类型的成员变量。
附件:日期类的实现
功能:实现日期加减天数,日期-日期等功能
Date.h
#pragma once
#include <iostream>
using namespace std;
class Date
{
//友元声明
//流插入<<
friend ostream& operator<<(ostream& out, const Date& d);
//流提取
friend istream& operator>>(istream& in, Date& d);
public:
//类中定义的成员函数,默认inline
int GetMonthDay(int year, int month)
{
static int monthDayArray[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)))
{
return 29;
}
return monthDayArray[month];
}
bool CheckDay(int year, int month, int day)
{
if (month <= 0 || month >= 13 || day > GetMonthDay(year, month) || day <= 0)
return false;
return true;
}
//构造函数
Date(int year = 2024, int month = 1, int day = 1);
//逻辑运算符
bool operator==(const Date& d);
bool operator!=(const Date& d);
bool operator>=(const Date& d);
bool operator<=(const Date& d);
bool operator>(const Date& d);
bool operator<(const Date& d);
//算术运算符
Date& operator+=(int day);
Date& operator-=(int day);
Date operator+(int day);
Date operator-(int day);
Date operator++(int);//后置++
Date& operator++();//前置++
Date operator--(int);//后置--
Date& operator--();//前置--
//d1 - d2
int operator-(const Date& d);
private:
int _year;
int _month;
int _day;
};
//流插入<<
ostream& operator<<(ostream& out, const Date& d);
//流提取>>
istream& operator>>(istream& in, Date& d);
Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
//日期合法性
if (!CheckDay(year, month, day))
{
cout << "非法日期:" << *this;
}
}
//逻辑运算符
bool Date::operator==(const Date& d)
{
if (_year == d._year)
{
if (_month == d._month)
{
return _day == d._day;
}
else
{
return false;
}
}
else
{
return false;
}
}
bool Date::operator!=(const Date& d)
{
return !(*this == d);
}
bool Date::operator>=(const Date& d)
{
return (*this == d) && (*this > d);
}
bool Date::operator<=(const Date& d)
{
return !(*this > d);
}
bool Date::operator>(const Date& d)
{
if (_year > d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month > d._month)
{
return true;
}
else if (_month == d._month)
{
return _day > d._day;
}
}
return false;
}
bool Date::operator<(const Date& d)
{
return !(*this >= d);
}
//算术运算符
Date& Date::operator+=(int day)
{
if (day < 0)
*this -= (-day);
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_month = 1;
_year++;
}
}
return *this;
}
Date& Date::operator-=(int day)
{
if (day < 0)
*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)
{
Date tmp = *this;
tmp += day;
return tmp;
}
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
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;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//日期-日期
int Date::operator-(const Date& d)
{
int cnt = 0;
Date greater = *this;
Date less = d;
int flag = 1;
if (greater < less)
{
greater = d;
less = *this;
flag = -1;
}
while (less != greater)
{
++less;
cnt++;
}
return cnt * flag;
}
//流插入<<
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" << d._day << endl;
return out;
}
//流提取>>
istream& operator>>(istream& in, Date& d)
{
while (1)
{
cout << "请输入年月日:";
in >> d._year >> d._month >> d._day;
if (!d.CheckDay(d._year, d._month, d._day))
{
cout << "日期非法,请重新输入!!!" << endl;
}
else
{
break;
}
}
return in;
}
今天的内容就到这里,感谢大家的支持 !!!