类的默认成员函数
1.构造函数:主要完成初始化(构造函数是一个特殊的成员函数)
对对象的实例化时初始化对象。特性:
1.函数名与类名相同
2.无返回值(不用写void)
3.对象实例化时系统会自动调用对应的构造函数
当我们在对象的实例化时,构造函数会自动调用,就代替了init函数,并且保证了我们的对象都是实例化之后的。对于没有形参的构造函数,在实例化时不能加()如果加()会跟函数声明冲突。
4.构造函数可以重载(函数重载)
什么是默认构造函数:1.无参构造函数 2.全缺省构造函数 3.我们不写编译器默认生成的构造函数。
对于这3个默认构造函数是有且只有一个存在。总结一下:不传实参就可以调用的函数就是默认构造函数。
5.如果类中没有显示的定义构造函数,则c++编译器会自动生成一个无参的默认构造函数,一旦显示定义就不会再生成。
6.我们不写编译器自动生成的默认构造函数对内置类型成员变量的初始化没有要求,也就是说是否初始化是不确定的,看编译器。对于自定义类型的成员变量,要求这个成员变量的默认构造函数初始化。如果这个成员变量没有默认构造函数就会报错。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Data
{
public:
void print()
{
cout << _year <<"/"<< _month <<"/" <<_day << endl;//对于输入输出,只能输出同一类型的数据
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Data d1;
d1.print();
return 0;
}
2.析构函数:主要完成清理工作
析构函数跟构造函数的功能相反,析构函数不是对对象本身的销毁,完成对象中资源的清理释放工作。类比destroy
1.析构函数的函数名是类名前加~;
2.无返回值(也不用写void)也没有参数
3.一个类只能有一个析构函数,若未显示定义,系统自动生成。
4.对象生命周期结束时自动调用
5.编译器自动生成的析构函数对内置类型成员不做任何处理,自定类型成员会调用她自己的析构函数。
6.就算我们写了析构函数,对于自定义类型成员也会调用他的析构函数,也就是说自定义类型无论什么情况都自动调用析构函数。
总结:如果我们在类中显示申请了资源才需要自己实现析构。
3.拷贝构造:
用已经初始化的对象去初始化另一个对象。
拷贝构造跟构造函数构成重载,对于拷贝构造的参数是对应类类型的引用。
为什么必须是引用呢?c++规定类类型传值传参必须调用拷贝构造,所以拷贝构造本身就不能是传值传参,如果构造函数就是传值传参就会无穷递归了.
注意:这里可以使用指针构造,但是她不是拷贝构造,而是普通构造。
拷贝构造还可以这样写:Data d4=d1;完全等价于Data d4(d1);
c++规定自定义类型对象进行拷贝的行为必须调用拷贝构造,所以这里自定义类型传值传参跟传值返回都会调用拷贝构造。
若未显示定义拷贝构造,编译器会自动生成拷贝构造函数。自动生成的拷贝构造函数对内置类型成员变量进行值拷贝/浅拷贝(一个字节一个字节的拷贝),对于自定义类型的成员变量会调用她自己的拷贝构造。如果需要深拷贝的就要自己写拷贝构造。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Data
{
public:
Data(int year = 1, int month = 1,int day=1)
{
_year = year;
_month = month;
_day = day;
}
Data(const Data& d)//这是拷贝构造,其中隐含着this指针;注意:这里最好加上const避免权限放大。
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Data(Data* p)//这里是指针形式的拷贝构造
{
_year = p->_year;
_month = p->_month;
_day = p->_day;
}
void print()
{
cout << _year <<"/"<< _month <<"/" <<_day << endl;//对于输入输出,只能输出同一类型的数据
}
~Data()
{
cout << "~Data(析构函数调用)"<< endl;
}
private:
int _year;
int _month;
int _day;
};
void func(Data d)//c++规定传值传参需要调用拷贝构造来完成拷贝的工作
{
}
int main()
{
Data d1(1,2,3);
d1.print();
Data d2(d1);
d2.print();
Data d3(&d2);
d3.print();
func(d2);//这里进入函数会先调用拷贝构造传参,出函数会调用析构函数,拷贝到形参,析构的也是形参
return 0;
}
运行结果如下:
4.赋值运算符重载:
4.1运算符重载:
对于内置类型可以直接使用运算符,但是对于自定义类型则不能直接使用运算符。
运算符重载就是一个函数,只不过我们在使用的时候不需要写函数,就直接写运算符就可以了,编译器会自动转换成函数。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Data
{
public:
Data(int year = 1, int month = 1,int day=1)
{
_year = year;
_month = month;
_day = day;
}
int _year;//
int _month;
int _day;
};
//注意到我们这样写就一定是将成员变量共有了,如何解决呢?
//1.提供对应的getxxx函数。
//2.友元--后面再解释
//3.重载为成员函数。(所以我们可以将操作符重载函数放到类中)
bool operator<(const Data& d1, const Data& d2)
{
if (d1._year < d2._year)
return true;
else if (d1._year == d2._year &&
d1._month < d2._month)
return true;
else if (d1._year == d2._year &&
d1._month == d2._month &&
d1._day < d2._day)
return true;
return false;
}
int main()
{
Data d1(2024,8,12);
Data d2(2024, 8, 13);
cout << (d1 < d2) << endl;
cout << operator<(d1, d2) << endl;
return 0;
}
将运算符重载函数改为成员函数:
#include<iostream>
using namespace std;
class Data
{
public:
Data(int year = 1, int month = 1,int day=1)
{
_year = year;
_month = month;
_day = day;
}
bool operator<(const Data& d2)//但是当我们将运算符重载函数变成成员函数之后,他会默认带有this指针,所以传参会少一个。
{
if (_year < d2._year)
return true;
else if (_year == d2._year &&
_month < d2._month)
return true;
else if (_year == d2._year &&
_month == d2._month &&
_day < d2._day)
return true;
return false;
}
private:
int _year;//
int _month;
int _day;
};
//注意到我们这样写就一定是将成员变量共有了,如何解决呢?
//1.提供对应的getxxx函数。
//2.友元--后面再解释
//3.重载为成员函数。(所以我们可以将操作符重载函数放到类中)
int main()
{
Data d1(2024,8,12);
Data d2(2024, 8, 13);
cout << d1.operator<(d2) << endl;//这是显示的调用
cout << (d1 < d2) << endl;//这是隐式调用
return 0;
}
结果同上。