构造函数
意义:对对象进行初始化。
** 如果不显示定义,编译器会默认生成一个构造函数**
系统生成的构造函数会初始化自定义类型(调用它的构造函数进行初始化)对内置类型不做处理。
c++补丁:可以为内置成员函数设定一个初始值。
特性:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。(无参和全缺省以及系统生成的构造函数统称为默>认构造函数,默认构造函数只能存在一个)
补充:内置类型/基本类型:语言本身定义的基础类型如 int char double float,任何类型的指针自定义类型 struct class 联合体union等等
编译器自动生成的构造函数对内置类型不会做处理,自定义类型会自动调用它的默认构造函数
一般情况下有内置类型,则需要我们自己写构造函数来对成员函数进行初始化。如果成员函数都是自定义类型则可以考虑使用编译器默认生成的构造函数。
定义一个日期类构造函数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
/*Date()
{
_year = 2020;
_month = 1;
_day = 1;
}*/
void Print()
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date p(2025, 4, 21);
p.Print();
return 0;
}
析构函数
析构函数:释放对象调用的内存空间
(与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。)
特征:
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值类型。
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
- 对象生命周期结束时,C++编译系统系统自动调用析构函数
如果不显示定义,系统会自动生成析构函数,对内置类型不处理,对自定义类型调用其默认的析构函数。
如果没有开辟内存空间,或者对象里面全是自定义类型的成员变量则不需要写析构函数。
tips:调用构造函数是顺序调用,析构函数是逆序调用。如果有static静态和全局,则按照以下规则:
1.类的析构函数调用一般按照构造函数调用的相反顺序进行调用,但是要注意static对象的存在, 因为static改变了对象的生存作用域,需要等待程序结束时才会析构释放对象
2、全局对象先于局部对象进行构造
3、局部对象按照出现的顺序进行构造,无论是否为static
4、析构的顺序按照构造的相反顺序析构,只需注意static改变对象的生存作用域之后,会放在局部 对象之后进行析构.
C c;
int main()
{
A a;
B b;
static D d;
return 0;
}
构造顺序,c,a,b,d//全局优先构造,局部对象构造按照顺序进行。
析构顺序,b,a,d,c//因为static改变了对象的生存作用域需要等待程序结束时才会析构释放对象,c,d在mian函数结束后析构,按照d,c的顺序。
析构函数代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
a = (int*)malloc(sizeof(int) * 4);
}
/*Date()
{
_year = 2020;
_month = 1;
_day = 1;
}*/
void Print()
{
cout << _year << " " << _month << " " << _day << endl;
}
~Date()
{
free(a);
a = nullptr;
cout << "调用析构" << endl;
}
private:
int* a;
int _year;
int _month;
int _day;
};
int main()
{
Date p(2025, 4, 21);
p.Print();
return 0;
}
拷贝构造
c++规定自定义类型传值传参要调用拷贝构造(参数只有一个并且是该自定义类型,前面最好加const防止初始化的时候出错,引用可以权限缩小)
内置类型直接拷贝
传递的参数只有一个且必须是类对象的引用(如果传的不是引用,会造成无限循环)
如果不显示定义,系统会自动生成拷贝构造.
系统自动生成的拷贝构造特性:
系统生成的构造函数执行浅拷贝(值拷贝)拷贝内置类型和自定义类型的数据
tips:栈如果使用系统生成的拷贝构造,会导致同一块空间被析构两次(浅拷贝的锅,两个数组指针指向同一块空间)如果和c语言一样直接拷贝,就会导致这样的问题。
拷贝构造代码:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
a = (int*)malloc(sizeof(int) * 4);
}
Date(const Date& b)
{
_year = b._year;
_month = b._month;
_day = b._day;
cout << "ok" << endl;
}
/*Date()
{
_year = 2020;
_month = 1;
_day = 1;
}*/
void Print()
{
cout << _year << " " << _month << " " << _day << endl;
}
~Date()
{
free(a);
a = nullptr;
cout << "调用析构" << endl;
}
private:
int* a;
int _year;
int _month;
int _day;
};
int main()
{
Date p(2025, 4, 21);
Date b(p);
p.Print();
b.Print();
return 0;
}
运算符重载
1 运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型参数
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this
.5.* :: sizeof ?: . 注意以上5个运算符不能重载
赋值运算符重载
默认成员函数之一,默认赋值运算符
默认生成赋值重载跟拷贝构造行为一样
对内置类型进行值拷贝,自定义类型调用其自己的赋值运算重载
赋值运算只能重载为类,不能重载全局函数(写全局里面会和编译器自动生成的冲突)
cout 可以支持内置类型是因为库里面实现了,可以·支持自动识别类型是因为函数重载。
类型转换会产生临时变量
Date类型的实现代码
一,头文件
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <assert.h>
using namespace std;
class Date
{
friend ostream& operator<<(ostream& out, Date& d);
friend istream& operator>>(istream& cin, Date& d);
public:
Date(int year, int month, int day);
// 获取某年某月的天数
int GetMonthDay(int year, int month);
// 全缺省的构造函数
// 日期+=天数
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);
<运算符重载
bool operator < (const Date& d);
<=运算符重载
//bool operator <= (const Date& d);
!=运算符重载
bool operator != (const Date& d);
// 日期-日期 返回天数
int operator-(const Date& d);
//void operator<<(ostream&out);
private:
int _year=2000;
int _month=1;
int _day=1;
};
ostream& operator<<(ostream& out, Date& d);
istream& operator>>(istream& cin, Date& d);
二,源文件
#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"
int Date::GetMonthDay(int year, int month)
{
int arr[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;
}
else
return arr[month];
}
Date::Date(int year, int month, int day)
{
if (day > 0 && day <= GetMonthDay(year, month) && month < 13 && month>0)
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "false" << endl;
assert("false");
}
}
Date&Date::operator+=(int day)
{
if (day < 0)
{
return *this -= -day;
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
}
Date Date::operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += day;
}
_day -= day;
while (_day <= 0)
{
--_month;
_day += GetMonthDay(_year, _month);
if (_month == 0)
{
--_year;
_month = 12;
}
}
return *this;
}
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
Date& Date::operator--()
{
*this = *this - 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp = *this;
*this = *this - 1;
return tmp;
}
bool Date::operator != (const Date& d)
{
return !(*this == d);
}
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)
{
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 Date::operator>(const Date& d)
{
return !(*this < d);
}
int Date::operator-(const Date& d)
{
int flag = 1;
Date max = (*this); Date min = d;
if (max < min)
{
max = d;
min = *this;
flag = -1;
}
int n=0;
while (min != max)
{
min++;
n++;
}
return n * flag;
}
ostream& operator<<(ostream& out, Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
//不符合习惯,因为这样写左操作数必须是Date类型,所以不能写进成员函数。
//void Date::operator<<(ostream& out)
//{
// cout << _year << "年" << _month << "月" << _day << "日" << endl;
//}
istream& operator>>(istream& cin, Date& d)
{
int year; int month; int day;
cin >>year >>month >> day;
if (day > 0 && day <= d.GetMonthDay(year, month) && month < 13 && month>0)
{
d._year = year;
d._month = month;
d._day = day;
}
else
{
cout << "false" << endl;
assert(false);
}
return cin;
}
int main()
{
Date d1(2000,12,30);
Date d2(2000, 11,30);
cout << d2 - d1 << endl;
//cout << d1 - d2 << endl;
//d1 << cout;
cout << d1<<d2;
Date d3(1,1,1);
cin >> d3;
cout << d3;
return 0;
}