1.构造函数——完成对象成员变量的初始化
2.析构函数——完成空间(主要是堆)的释放
3.拷贝构造——用一个已初始化的对象初始另一个正在初始化的对象
4.赋值重载——用一个已初始化的对象赋值给另一个已经初始化的对象
5.取地址重载——对一个不加const的对象取地址
6.const修饰的取地址重载——对一个加const的对象取地址
举例代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace::std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)//类名就是函数名,无返回值
{
_year = year;
_month = month;
_day = day;
}
Date(int year, int month = 1, int day = 1)//构成重载
{
_year = year;
_month = month;
_day = day;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
Date A;//自动调用析构函数
//Date A();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace::std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
//Date A;
return 0;
}
#include
using namespace::std;
class Date
{
public:
private:
int _day;
int _month;
int _year;
};
int main()
{
Date A;
//Date A();
return 0;
}
#include
using namespace::std;
class Date
{
public:
private:
int _day = 1;
int _month = 1;
int _year = 1;
};
int main()
{
Date A;
return 0;
}
using namespace::std;
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
Date A(2023,5,3);//这就跟函数调用差不多了,就多了个类型。
return 0;
}
析构函数的定义:析构函数于构造函数相对应,构造函数是对象创建的时候自动调用的,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
我们手写一个栈的初始化和释放
#include
using namespace::std;
class Stack
{
public:
Stack(int capacity = 4)
{
cout << "Stack()" << endl;
int* tmp = (int*)malloc(sizeof(int) * capacity);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
_arr = tmp;
_top = 0;
_capacity = capacity;
}
}
~Stack()
{
cout << "~Stack()" << endl;
free(_arr);
_arr = NULL;
}
private:
int* _arr;
int _top;
int _capacity;
};
int main()
{
Stack stack1;
return 0;
}
当我们创建一个栈对象时,会完成对象的初始化,当main函数执行结束时会完成堆空间的释放。
调试看结果:
此时mainj函数差一步结束:
继续调试:
继续执行程序结束,对象才进行释放。
#include
using namespace::std;
class Stack
{
public:
Stack(int capacity = 4)
{
cout << "Stack()" << endl;
int* tmp = (int*)malloc(sizeof(int) * capacity);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
_arr = tmp;
_top = 0;
_capacity = capacity;
}
}
private:
int* _arr;
int _top;
int _capacity;
};
int main()
{
Stack stack1;
return 0;
}
我们定义一个对列:
#include
using namespace::std;
class Stack
{
public:
Stack(int capacity = 4)
{
cout << "Stack()" << endl;
int* tmp = (int*)malloc(sizeof(int) * capacity);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
_arr = tmp;
_top = 0;
_capacity = capacity;
}
}
~Stack()
{
cout << "~Stack()" << endl;
free(_arr);
_arr = NULL;
}
private:
int* _arr;
int _top;
int _capacity;
};
class MyQueue
{
Stack stack_push;
Stack stack_pop;
};
int main()
{
MyQueue Q;
return 0;
}
那什么时候用写析构函数?什么时候不用写析构函数呢?
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
};
class B
{
public:
B()
{
cout << "B()" << endl;
}
~B()
{
cout << "~B()" << endl;
}
};
class C
{
public:
C()
{
cout << "C()" << endl;
}
~C()
{
cout << "~C()" << endl;
}
};
class D
{
public:
D()
{
cout << "D()" << endl;
}
~D()
{
cout << "~D()" << endl;
}
};
class E
{
public:
E()
{
cout << "E()" << endl;
}
~E()
{
cout << "~E()" << endl;
}
};
A a;
B b;
int main()
{
C c;
D d;
static E e;
return 0;
}
这里我们列举了三种对象——全局对象,局部对象,static修饰的局部对象
话不多说,开始调试:
接着调试直到程序结束:
我们按照语法分析:用已存在的类类型对象创建新对象时由编译器自动调用(拷贝构造的调用条件)。
代码:
#include
using namespace::std;
class Date
{
public:
//构造函数
Date(int year = 1949, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date(const Date& A)
{
_year = A._year;
_month = A._month;
_day = A._day;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
Date d;
Date B(d);
return 0;
}
void Fun(Date A)
{
}
#include
using namespace::std;
class Date
{
public:
//构造函数
Date(int year = 1949, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
Date d;
Date B(d);
return 0;
}
再给出一份代码:
#include
using namespace::std;
class Stack
{
public:
Stack(int capacity = 4)
{
cout << "Stack()" << endl;
int* tmp = (int*)malloc(sizeof(int) * capacity);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
_arr = tmp;
_top = 0;
_capacity = capacity;
}
}
void PushBack(int data)
{
_arr[_top++] = data;
}
~Stack()
{
cout << "~Stack()" << endl;
free(_arr);
_arr = NULL;
}
private:
int* _arr;
int _top;
int _capacity;
};
int main()
{
Stack s1;
Stack s2(s1);
return 0;
}
进行调试:
其只是简单的把地址copy过去了,但仍没有完成任务。
此代码实现的图解:
如何实现:
#include
using namespace::std;
class Stack
{
public:
Stack(int capacity = 4)
{
cout << "Stack()" << endl;
int* tmp = (int*)malloc(sizeof(int) * capacity);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
_arr = tmp;
_top = 0;
_capacity = capacity;
}
}
Stack(const Stack& A)
{
int* tmp = (int*)malloc(sizeof(int) * A._capacity);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
memcpy(tmp, A._arr, sizeof(int) * A._capacity);
_arr = tmp;
_top = A._top;
_capacity = A._capacity;
}
}
void PushBack(int data)
{
_arr[_top++] = data;
}
~Stack()
{
cout << "~Stack()" << endl;
free(_arr);
_arr = NULL;
}
private:
int* _arr;
int _top;
int _capacity;
};
int main()
{
Stack s1;
Stack s2(s1);
return 0;
}
#include
using namespace::std;
class Stack
{
public:
Stack(int capacity = 4)
{
cout << "Stack()" << endl;
int* tmp = (int*)malloc(sizeof(int) * capacity);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
_arr = tmp;
_top = 0;
_capacity = capacity;
}
}
Stack(const Stack& A)
{
int* tmp = (int*)malloc(sizeof(int) * A._capacity);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
memcpy(tmp, A._arr, sizeof(int) * A._capacity);
_arr = tmp;
_top = A._top;
_capacity = A._capacity;
}
}
void PushBack(int data)
{
_arr[_top++] = data;
}
~Stack()
{
cout << "~Stack()" << endl;
free(_arr);
_arr = NULL;
}
private:
int* _arr;
int _top;
int _capacity;
};
Stack Func()
{
static Stack A;
return A;
}
int main()
{
Stack A = Func();
return 0;
}
对象里面是自定义类型的拷贝构造,如何拷贝呢?
class Date
{
public:
//构造函数
Date(int year = 1949, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date(const Date& A)
{
_year = A._year;
_month = A._month;
_day = A._day;
}
private:
int _day;
int _month;
int _year;
};
class Dates
{
Date A;
Date B;
};
int main()
{
Dates A;
Dates B(A);
return 0;
}
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
注意:
#include
using namespace::std;
class Date
{
public:
//构造函数
Date(int year = 1949, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date(const Date& A)
{
_year = A._year;
_month = A._month;
_day = A._day;
}
//private:
int _day;
int _month;
int _year;
};
bool operator== (const Date& A, const Date& B)
{
return A._day == B._day
&& A._month == B._month
&& A._year == B._year;
}
int main()
{
Date A;
Date B;
cout <<( A == B )<< endl;
//这里括号不可以省去,因为流插入的优先级比较高所以我们需要加括号让表达式先计算。
return 0;
}
返回的bool值为1,所以为真,因此相等。
全部的显然不是很好有没有办法写到局部呢?
答案是肯定的,写到类里面不就好了么?
#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace::std;
class Date
{
public:
//构造函数
Date(int year = 1949, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date(const Date& A)
{
_year = A._year;
_month = A._month;
_day = A._day;
}
bool operator== (const Date& B)
{
return _day == B._day
&& _month == B._month
&& _year == B._year;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
Date A;
Date B;
cout <<( A == B )<< endl;
//A==B 其实本质上就是A.operator==(B)
return 0;
}
我们单独把这里的重载函数列出来:
bool operator== (const Date& B)
//其实就是bool operator== (Date *const this,const Date& B)
{
return _day == B._day
&& _month == B._month
&& _year == B._year;
}
#include
using namespace::std;
class Date
{
public:
//构造函数
Date(int year = 1949, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date(const Date& A)
{
_year = A._year;
_month = A._month;
_day = A._day;
}
Date& operator++(int)//加一个参数以示区分,别的到没什么用处,
{
_day++;
return *this;
}
Date operator++()
{
Date tmp(*this);
_day++;
return tmp;
}
private:
int _day;
int _month;
int _year;
};
#include
using namespace::std;
class Date
{
public:
//构造函数
Date(int year = 1949, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date(const Date& A)
{
_year = A._year;
_month = A._month;
_day = A._day;
}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
Date A;
Date B(2023,5,3);
A = B;
return 0;
}
赋值操作符返回类型是左值本身,因此我们返回引用比较合适可以减少空间上的开销。
当赋值为本身时,可以不进行此操作,结果也是一样的。
看下面的代码:
#include
using namespace::std;
class Date
{
public:
//构造函数
Date(int year = 1949, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date(const Date& A)
{
_year = A._year;
_month = A._month;
_day = A._day;
}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
Date A;
Date B = A;//这个代码不是赋值重载而是拷贝构造
//这是已初始化的对象对另一个正在初始化的对象的赋值——拷贝构造。
return 0;
}
如何证明:
#include
#include
#include
using namespace::std;
class Date
{
public:
// 获取某年某月的天数
bool is_leap_year(int year)
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
return true;
}
return false;
}
int GetMonthDay(int year, int month)
{
int day[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (is_leap_year(year) && month == 2)
{
return 29;
}
else
{
return day[month];
}
}
//检查一下输入或者赋值的时候是否日期非法
bool is_legal_Date()
{
if (_month >= 1 && _month <= 12 && _day >= 1 && _day <= GetMonthDay(_year,_month))
{
return true;
}
else
{
return false;
}
}
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 拷贝构造函数
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 赋值运算符重载
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
// 析构函数
~Date()
{
;
}
// 日期+=天数
Date& operator+=(int day)
{
if (day < 0)
{
return *this -= -day;
}
_day += day;
while (_day >= GetMonthDay(_year, _month))
{
int tmp = GetMonthDay(_year, _month);
_day -= tmp;
_month += 1;
if (_month == 13)
{
_year += 1;
_month = 1;
}
}
return *this;
}
// 日期+天数
Date operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
// 日期-天数
Date operator-(int day)
{
Date tmp(*this);
tmp._day -= day;
while (tmp._day <= 0)//等于0不能忘了
{
int tmp1 = GetMonthDay(tmp._year, tmp._month - 1);
if (tmp._month == 1)
{
tmp1 = GetMonthDay(tmp._year, 12);
}
tmp._day += tmp1;
tmp._month--;
if (tmp._month == 0)
{
tmp._year--;
assert(tmp._year);
tmp._month = 12;
}
}
return tmp;
}
// 日期-=天数
Date& operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
*this = *this - day;
return *this;
}
// 前置++
Date& operator++()
{
*this += 1;
return *this;
}
// 后置++
Date operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
// 后置--
Date operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
// 前置--
Date& operator--()
{
*this -= 1;
return *this;
}
// >运算符重载
bool operator>(const Date& d)
{
if (_year > d._year)
{
return true;
}
else if (_year == d._year && _month > d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day > d._day)
{
return true;
}
else
{
return false;
}
}
// ==运算符重载
bool operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
// >=运算符重载
bool operator >= (const Date& d)
{
return *this == d || *this > d;
}
// <运算符重载
bool operator < (const Date& d)
{
return !(*this >= d);
}
// <=运算符重载
bool operator <= (const Date& d)
{
return !(*this > d);
}
// !=运算符重载
bool operator != (const Date& d)
{
return !(*this == d);
}
// 日期-日期 返回天数
int operator-(const Date& d)
{
int day = 0;
if (*this >= d)
{
Date tmp(d);
while (*this != tmp)
{
++tmp;
day++;
}
return day;
}
else
{
Date tmp(*this);
while (tmp != d)
{
++tmp;
day++;
}
return -day;
}
}
private:
int _year;
int _month;
int _day;
};
不妨看这样一段代码:
#include
using namespace::std;
class Date
{
public:
void Print()
{
cout << _year << _month << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
const Date A;
A.Print();
return 0;
}
定义一个变量int * const this
因为:const具有就近原则
所以:放在this前this本身不能修改
补充:放在*前说明*this不能被修改
再看我们传进去的参数是什么类型的——const Date(说明Date不能修改)
void Print() const
{
cout << _year << _month << _day << endl;
}
适用场景:
class Date
{
public :
Date* operator&()
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
class Date
{
public :
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};