目录
1、概述
2、构造函数
构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
1、构造函数:完成对象的初始化
1、特性
1、函数名与类名相同
2、没有返回值
3、支持函数重载
4、对象实例化时,编译器会自动调用对应的构造函数
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year=0, int month=0, int day=0)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(2022, 10, 16);
return 0;
}
2、注意
1、如果类中没有显示定义构造函数,则C++编译器会自动生成一个无参的默认构造函数
2、一旦用户显示定义,编译器将不再生成
C++将数据类型分为了两类,内置类型和自定义类型。
内置类型:int、char、double、指针、内置类型数组等。
自定义类型:struct/class定义的类型。
3、我们不写编译器默认生成的构造函数,对内置类型不做处理;对自定义类型,会去调用它的默认构造函数进行初始化,如果没有对应的默认构造函数就会报错。
4、默认构造函数:不需要传参就可以调用的构造函数。
有三个:全缺省的、无参的、我们不写编译器默认生成的。
5、构造函数不能显示的调用,因为构造函数是特殊的成员函数,当对象实例化时,会自动调用。
6、无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
3、析构函数
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
1、析构函数——对象中的一些资源清理
1、特性
1、析构函数名是在类名前加上~
2、没有参数,也没有返回值
3、一个类有且只有一个析构函数
4、对象生命周期结束时,会自动调用析构函数,完成对象中一些资源的清理
2、注意
1、若未显示定义,编译器会自动生成默认的析构函数
2、如果我们不写,默认生成的析构函数与构造函数类似,对内置类型成员变量不做处理;对自定义类型成员变量,会去调用它的析构函数。
4、拷贝构造函数
拷贝构造函数是构造函数的一个重载形式
1、拷贝构造函数——使用同类对象初始化创建对象
1、特性
1、拷贝构造函数是构造函数的一个重载形式
2、拷贝构造函数的参数只有一个,且必须使用引用传参。
注意:如果使用传值传参,会发生无穷递归调用。
3、若未显示定义,系统默认生成的拷贝构造函数,对内置类型会按字节序进行拷贝(浅拷贝);对自定义类型成员,会调用它的拷贝构造。
5、赋值运算符重载——将一个对象赋值给另一个对象
1、运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
C/C++中,内置类型可以使用运算符。
默认情况下,C++不支持自定义类型对象使用运算符。
为了让自定义类型对象能像内置类型一样使用运算符,C++推出了运算符重载。
1、定义
函数名:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
下面通过日期类进行举例:
1、运算符重载函数放在全局域
注意:d1>d2;编译器看到后,在vs下会先去类域中找,找不到再去全局域去找(不同的编译器实现不同);这里的d1>d2;会被编译器转化为operator>(d1,d2);
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year=0, int month=0, int day=0)
{
_year = year;
_month = month;
_day = day;
}
//private:
int _year;
int _month;
int _day;
};
//d1 > d2;
bool operator>(const Date& d3, const Date& d4)
{
if (d3._year > d4._year)
{
return true;
}
else if (d3._year == d4._year && d3._month > d4._month)
{
return true;
}
else if (d3._year == d4._year && d3._month == d4._month && d3._day > d4._day)
{
return true;
}
else
{
return false;
}
}
int main()
{
Date d1(2022, 10, 17);
Date d2(d1);
cout<<(d1 > d2)<<endl;
return 0;
}
2、放到类域中
注意:放到类域中,做成员函数时,有一个固定的形参this指针
d1>d2;编译器看到后,先到类中去找,找不到再去全局中找。将其转换为d1.operaror>(d2);
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year=0, int month=0, int day=0)
{
_year = year;
_month = month;
_day = day;
}
//d1>d2
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;
}
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 10, 17);
Date d2(d1);
cout<<(d1 > d2)<<endl;
return 0;
}
2、特性
1、不能通过连接其他符号来创建新的操作符
2、重载操作符必须有一个类类型参数
3、用于内置类型的运算符,其含义不能改变,不能改变操作数个数
4、. .* :: sizeof ?: 这5个运算符不能重载
2、赋值运算符重载
1、连续赋值的返回值问题
对自定义类型的连续赋值也是如此:d1=d2=d3;
所以赋值重载是具有返回值的,因为是定义在类中,作为成员函数,通过对象进行调用,出了函数作用域对象还在,所以可以定义为传引用返回,比传值返回少一次调用拷贝构造。
2、赋值重载函数的实现
//d3=d1
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
如果是自己给自己赋值,那就没有意义,所以进行改动为:
//d3=d1
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
总结:赋值运算符主要有四点:
1. 参数类型
2. 返回值
3. 检测是否自己给自己赋值
4. 返回*this
5. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
注意:编译器默认生成的赋值重载跟拷贝构造做的事完全类似:
1、对内置类型成员,会完成字节序拷贝——浅拷贝
2、对自定义类型成员,会调用它的operator=