开个学习c++的记录,本人还是更喜欢c++这一门语言,可能因为个人的学习态度与兴趣,我更喜欢严谨的语法结构和明确的编译方式,所以准备在C++这一条路上深入地走下去,Java的引用和常量还有各种序列化虽然用起来很方便,但是我经常不太喜欢这种方式,我还是喜欢严谨,一个变量名是局部变量还是指针还是引用,指向的是哪一块地址,管理堆内存的释放与开辟,虽然对初学者有些难度,但学起来其实还是有趣得多。(本期集合适合有一定c++基础的人预览和借鉴)
所以这里是我本期记录第一站,关于C++类的默认函数。
学习到C++,首先遇到的第一个概念就是类,class这一属性很神奇,当创建一个class时,哪怕里面只有一个变量没有任何方法,甚至连变量都没有,编译器都会默认地为这个class创建几个函数(适用c++高版本编译器),分别是:
- 默认构造函数
- 默认析构函数
- 默认拷贝构造函数
- 默认拷贝赋值函数
- 默认移动构造函数
- 默认移动赋值函数
(若有遗漏欢迎指正)
前面两个相信学习C++的类概念后都不会陌生,简单回顾一下,当类内部没有任何构造函数时,编译器会为我们自动创建一个构造函数:ClassName::ClassName() ,不做任何操作,不含有任何参数。这一个默认函数当类内存在我们手动创建的有参或无参构造函数时,默认的构造函数会被自动删除,我们就无法使用默认构造函数了。当然为了严谨,也可以这样
ClassName()=delete;
代表这个类默认构造函数已经被我们删除了。
第二个析构函数相信也不陌生,在类基础里基本都会讲到,析构函数是对象退出变量范围时会运行的函数,其作用一般是删除构造时或运行时动态开辟的堆内存,或者进行连接关闭与互斥解锁的收尾工作,当然这些在默认析构函数是做不到的,需要我们自己去手写,而且析构函数具有只能有一个,必须无参数等特性。如以下特征
~ClassName(){}
接下来就是扩展的内容了,一般的基础课可能并不会教这个东西(反正我没教),关于拷贝构造函数和拷贝赋值函数,在类没有重写这两个方法时,类会存在默认的拷贝构造函数和拷贝赋值函数,默认的拷贝函数会对类内基本属性进行复制,大部分时候可以达到效果,但是当类内属性存在指针与引用时,就达不到我们的预期了(比如动态指针如果用默认拷贝会把动态的地址直接拷贝到另一个指针里,然后两个对象析构的时候就。。。报错)这种情况我们希望在新的对象里重新创建动态数组并把元素对应的copy到新对象里,就需要手动地重写拷贝函数。拷贝函数的形式:
ClassName(const ClassName& CopySource);
ClassName& operator=(const ClassName& CopySource);
第一个是拷贝构造函数,第二个是拷贝赋值函数,在以下情况下触发:
ClassName Copy1(CopySource);
ClassName Copy2=CopySource;
第一种情况触发拷贝构造函数,第二种情况触发拷贝赋值函数。
尽管这两种函数在编写时语法结构可能很相似,但必须都分别实现,需要注意的是,当我们自己重写了拷贝构造函数或者拷贝赋值函数中的任何一个时,另外一个都会被编译器删除,其默认的函数就用不了了。
虽然拷贝构造函数和拷贝赋值函数可以满足我们大部分需求,但有时候这似乎会让我们大量地浪费性能,例如我们本来只要把一个对象内部的东西延长它的生存周期,但是拷贝函数每一次执行时我们大部分会让对象内动态堆内存重新申请并copy,然后原来的对象生命周期过期时又会把原来的堆内存析构掉。(相当于把我们把西瓜复制了一个给需要的人,然后我们自己把原来的西瓜丢掉了,这纯纯有毛病,不如直接把咱们的自己的西瓜给他就省的造了)
至此,移动构造函数和移动赋值函数应运而生,把它可以单独理解为也是一个默认的拷贝函数,因为在默认函数的形式上和拷贝函数一般无二。但是我们在重写和引用移动函数时是完全与拷贝函数不一样的,单独定义移动函数时我们会直接把源对象的堆内存指针直接赋值给对应的对象,然后把源对象的堆内存指针置空,防止重复delete(注意这个时候就要注意析构函数在delete之前一定要进行判空)。
移动构造函数和移动赋值函数的形式如下:
ClassName(ClassName&& MoveSource) noexcept; //移动构造函数
ClassName& operator=(ClassName&& MoveSource) noexcept; //移动赋值函数
注意到移动函数必须要noexcept,禁止在移动函数中抛出错误,需要程序员自己在重写移动函数时自己处理错误。
调用情况:
ClassName Object2(std::move(MoveSource2)); //移动构造函数
ClassName Object1=std::move(MoveSource1); //移动赋值函数
这个std::move就是为了区别拷贝函数的
!!注意,在移动后原来的MoveSource1和MoveSource2就不能再使用了,因为对象里面的指针应该就被置空了,并且注意析构函数在delete之前一定要进行判空,否则会造成重复delete错误。
第一站默认函数学习差不多就这些内容了