前言
📚作者简介:爱编程的小马,是一名大厂后端c++程序员。
📚本文收录于C++系列,本专栏主要是分享我所了解的c++知识,带领大家慢慢从了解c++到认识c++,持续更新!
📚相关专栏Linux正在发展,敬请期待!
📚本文主要内容:认识类和对象六个默认成员函数
目录
[2.1 定义](#2.1 定义)
[2.2 特征](#2.2 特征)
[3.1 定义](#3.1 定义)
[3.2 特性](#3.2 特性)
[4.1 定义](#4.1 定义)
[4.2 特性](#4.2 特性)
1.类的6个默认成员函数
如果一个类中什么成员都没有,简称为空类。空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员 函数。默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
分为构造函数,析构函数,拷贝构造函数,赋值重载函数,取地址重载函数(两个,一个是普通对象,一个是const对象),今天介绍后面四个。
2.拷贝构造函数
2.1 定义
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用。
2.2 特征
拷贝构造函数也是特殊的成员函数,其特征如下:
1、拷贝构造函数是构造函数的一个重载函数
2、拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
我先就这两点给大家举个例子
cpp
class Date
{
public:
//构造函数
Date(int year =1 , int month =1, int day=1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Date(Date& d)
{
_year = d._year;
_month =d._month;
_day = d._day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
//内置类型
int _year;
int _month;
int _day;
};
int main()
{
//构造函数什么时候会调用,类实例化一个新的对象就是之前没有的
Date d1(2024, 8, 2);
//什么时候会调用拷贝构造,第一就是函数传参,第二就是用已经存在的对象
//去实例化一个新的对象
Date d2 (d1);
d1.Print();
d2.Print();
return 0;
}
3、如果拷贝构造函数没有显式定义,编译器就会生成一个默认的拷贝构造函数,默认的拷贝构造函数是浅拷贝。
大家看,我把拷贝构造函数屏蔽了,还是可以用d1去初始化d2,那拷贝构造没有什么存在的意义呀,那编译器自己就能把拷贝构造写出来并且可以使用,其实不是这样的,编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗? 当然像日期类这样的类是没必要的。那么下面的栈类呢?验证一下试试?
代码崩了,我调试给大家找找原因,这块难度比较大,我讲解详细一些。
同一块空间,释放两次,是不是就崩了?这就是C++为了补C语言浅拷贝(值拷贝)的一个坑,C++用了一个拷贝构造函数来解决问题,就是你函数传参之前,先去调用拷贝构造函数完成深拷贝,再调用函数完成功能。这块的深拷贝怎么写?
cpp
Stack(Stack& s)
{
STDataType* tmp = (STDataType*)malloc(sizeof(STDataType) * s._size);
_a = tmp;
memcpy(_a, s._a, sizeof(STDataType) * s._size);
_size = s._size;
_capacity = s._capacity;
}
思路:重新开辟一个新的空间,将原空间的内容拷贝过来,即可。
5. 拷贝构造函数典型调用场景:
使用已存在对象创建新对象
函数参数类型为类类型对象
函数返回值类型为类类型对象
3.赋值运算符重载
3.1 定义
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
3.2 特性
1. 赋值运算符重载格式
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值 检测是否自己给自己赋值
cpp
//拷贝构造函数
Date(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;
}
2. 赋值运算符只能重载成类的成员函数不能重载成全局函数
是因为定义为全局函数,编译器会产生调用歧义。
大家可能觉得,有了拷贝构造,这俩功能不是一样吗?为什么要需要复制重载呢?其实是下面这个原因:
3、用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注 意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符 重载完成赋值。
4.取地址重载函数
4.1 定义
返回类的地址
4.2 特性
1、一个是普通对象,一个是const对象
2、这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
3、这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需 要重载,比如想让别人获取到指定的内容!
日期类简单代码:
cpp
class Date
{
public:
//构造函数
Date(int year =1 , int month =1, int day=1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Date(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* operator& ()
{
return nullptr;
}
const Date* operator& ()const
{
return this;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
//内置类型
int _year;
int _month;
int _day;
};
int main()
{
const Date d1(2024, 8, 2);
Date d3(2023, 7, 4);
Date d4(2010, 7, 4);
cout << d1.operator& () << endl;;
cout<<&d3<<endl;
cout << &d4 << endl;
return 0;
}
总结
今天介绍了拷贝构造函数,赋值重载函数,取地址重载函数(两个,一个是普通对象,一个是const对象),明天给大家更新完整的日期类,学到这个地方就算入门了。
如果这份博客对大家有帮助,希望各位给小马一个大大的点赞鼓励一下,如果喜欢,请收藏一下,谢谢大家!!!
制作不易,如果大家有什么疑问或给小马的意见,欢迎评论区留言