1.类的6个默认成员函数
如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情 况下,都会自动生成下面6个默认成员函数。
2.构造函数
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次。
class Date
{
public:
Date(int year = 0,int month = 0,int day = 0)//构造函数(全缺省)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 2, 15);
d1.Print();
Date d2;
d2.Print();
return 0;
}
构造函数的特性:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。
- 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。编译器自动生成的默认构造函数不会初始化内置类型(基本类型),如:int , char , double等。但是会初始化我们的自定义类型。如我们用class,struct等定义的类型。
- 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。
哪些属于默认构造函数?
- 我们没写编译器自己生成的
- 我们写的无参的
- 我们写的全缺省的
3.析构函数
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而 对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特性:析构函数是特殊的成员函数。其特征如下:
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值。
- 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
- 析构函数的调用也符合先进后出
4.拷贝构造函数
拷贝构造函数也是特殊的成员函数,其特征如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
- 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷,贝这种拷贝我们叫做浅拷贝或者值拷贝。
从此以后注意,函数传参,自定义类型的对象,一般推荐引用传参。如果还要继续使用传值传参,也不是不可以,但是每次调用都会调用拷贝构造
5.运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型或者枚举类型的操作数
3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义
4.作为类成员的运算符重载函数时,操作符有一个默认的形参this,限定为第一个形参
5…* 、:: 、sizeof 、?: 、. 注意这5个运算符不能重载。
6.&重载和const&重载
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
7.练习编写默认成员函数
我们以实现一个日期类为例,练习一下
Date.h
#pragma once
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
bool IsLeapYear(const int year);
int GetMonthDay(const int year, const int month);
class Date
{
public:
Date(int year = 0, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//Date类成员只有三个int,不需要写析构函数和拷贝构造
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this; //这个函数之所以要有返回值,是为了解决 d3 = d2 = d1 这种情况
}
&重载和const&重载 这两个默认成员函数绝大多数情况下不需要自己写
//Date* operator&()
//{
// return this;
//}
//const Date* operator&()const
//{
// return this;
//}
//日期的比较函数 这几个函数都比较短,直接定义在类里面了,在类里面定义的函数默认是内联函数
bool 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 operator==(const Date& d)const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool operator<(const Date& d)const
{
if (*this > d || *this == d)
return false;
return true;
}
bool operator>=(const Date& d)const
{
if (*this < d)
return false;
return true;
}
bool operator<=(const Date& d)const
{
if (*this > d)
return false;
return true;
}
bool operator!=(const Date& d)const
{
if (*this == d)
return false;
return true;
}
//日期+-天数
Date& operator+=(int day);
Date operator+(int day)const;
Date& operator-=(int day);
Date operator-(int day)const;
//日期前置++--和后置++--
//为了区分前置和后置,有这样一个约定,在重载后置++--多一个int参数
//由于前置++返回的是++后的值,可以传引用返回,而后置返回的是++之前的值,需要先创建一个临时变量保存++之前的值,在传值返回
//前置++--相比于后置少了两次拷贝构造,效率稍微高一些
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
//两个日期差多少天
int operator-(const Date& d)const;
void Print()const //这个const修饰的是this指针,使this指针指向的内容不能被修改的
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
Date.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "Date.h"
bool IsLeapYear(const int year)
{
if (year % 4 == 0 && year % 100 != 0)
return true;
if (year % 400 == 0)
return true;
return false;
}
int GetMonthDay(const int year, const int month)
{
const static int arr[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int days = arr[month];
if (IsLeapYear(year) && month == 2)
days += 1;
return days;
}
Date& Date::operator+=(int day)
{
while (day--)
{
_day++;
if (_day > GetMonthDay(_year, _month))
{
_month++;
_day = 1;
if (_month > 12)
{
_year++;
_month = 1;
}
}
}
return *this;
}
Date Date::operator+(int day)const
{
//我们知道i+12不影响i的值,所以为了不改变原来的Date的值,我们先拷贝构造一个,并且这里不能传引用返回,因为copy是局部变量
Date copy = *this;
copy += day;
return copy;
}
Date& Date::operator-=(int day)
{
while (day--)
{
_day--;
if (_day == 0)
{
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
_day = GetMonthDay(_year, _month);
}
}
return *this;
}
Date Date::operator-(int day)const
{
Date copy = *this;
copy -= day;
return copy;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date Date::operator++(int)
{
Date copy = *this;
*this += 1;
return copy;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date copy = *this;
*this -= 1;
return copy;
}
int Date::operator-(const Date& d)const
{
//让小的日期++,直到和大的日期相等为止
int count = 0;
int flag = -1;
Date min = *this;
Date max = d;
if (min > max)
{
min = d;
max = *this;
flag = 1;
}
while (min < max)
{
min++;
count++;
}
return flag * count;
}