1. 6个默认成员函数
----初始化和清理{ 1.构造 2.析构 }
-----拷贝复制{ 1.拷贝构造 2.赋值重载 }
------取地址重载{ 1.普通对象 2.const对象取地址 }
注:构造函数的目的是初始化成员变量,而不是创造变量
析构函数的目的是清理需要清理的资源(指的是malloc,new,流,链接),而不是销毁变量
2.构造函数
1.分类:
1.自己写
1.自己写带参构造或半缺省构造,必须通过外界传值初始化成员变量(非默认构造)
2.自己写无参构造或全缺省构造,可以不传参。例:实现栈可以全缺省让外界决定是否开始就确定容量。(默认构造)
(使用注意:1.在前面用public访问限定符修饰,因:默认private
2.一般无参构造和全缺省构造只写其一,因:外部调用不传参会构成歧义(是否重载与歧义无关)
3.初始化调用不需传参的不能写空括号,因:test n1(),类名+对象名+()与函数声明歧义。)
2.默认生成
不写构造时,编译器会为你生成一个构造,默认生成的为公有的无参构造,编译器可能处理或不处理你的内置类型变量。(同C)。会调用你的自定义类型变量的默认构造,如果没有默认构造就报错。通过层层调用,直到全剩内置类型结束。(默认构造)
注意:关于自定义类型初始化处理,编译器无论在哪种情况下,都尝试调用此类型的默认构造,所以不必纠结,只当作 初始化外面类的对象时 要初始化其成员变量,内置类型不做处理,一旦初始化到了自定义类型,这个变量就会自己调用默认构造类型。(你无法在初始化外部对象时给内部自定义类型的要传参的构造函数传参,编译器不知道你给哪个传,也没有这个语法。)
例:外部类构造由编译器生成
外部构造由自己写
(验证编译器是否会调用内部自定类型默认构造函数与外部类型的构造无关,只要初始化到自定义类型了,就会调用)
2.默认构造的定义:
无需传参的构造函数:1.无参构造。2.全缺省参数构造。3.编译器生成的构造。
(一般三者取其一)
3.总结:
什么时候用:
1.缺省:涉及顺序表等可传参确定初始容量的,或其他可能在某些场景需要手动初始化特定值的。
2.手动无参:内置类型需要初始化(也可以换为给内置类型变量给缺省值),或者需要动态开辟的。
3.编译器构造:全为自定义类型,只需要在自定义类型里手动写构造,外部直接调。例子:双栈一类。
什么时候不用:
1.成员全是自定义类型,可以不用手写。(原因往上翻)(极少情况)
内置类型少,不需外部给特殊值,给缺省值就行的,可以不用手写。
2.大多数需要通过各种方式初始化内部内置类型变量的要手写。
补充:给内置类型变量缺省值(C++11的补丁)
定义:在定义类型时就给予内置类型变量一个默认值。对象初始化时,其优先对其内容改变。
来源:编译器给的构造函数不能满足大部分程序员,C++委员会就新提供了一种给内置类型变量默认值的方式。
名称来源:
因为1.此时类没有任何一个空间已开辟,不能叫赋值,
2.而这种行为又可以通过构造函数覆盖使得默认值改变,
所以叫 给予变量缺省值。
使用注意:如果同时有构造和缺省值,类实例化(对象初始化)时,先给内置类型变量赋默认的缺省值,再调构造覆盖它。(编译器构造不会理睬已赋缺省值的变量)
3.析构函数
使用注意:由于析构函数一般由编译器调用(也可以自己调用,但编译器在这之后会自己调用一次,往往会造成许多错误:free两次同样的空间。。。),不允许传参,也就不允许重载,只能有一个析构。
1.编译器的析构:
唯一作用是对自定义类型变量调用其析构,内置类型不管。(本身对象要销毁,内变量都要销毁,到自定义类型变量销毁时,自然就会自己调用析构,不用纠结编译器析构的用处)
2.什么时候需要析构:
1.类里对成员变量有malloc,fopen,new,链接等操作的,需手动写
2.没有上述的,不用写。(内部自定义类型变量有....在变量类里写)
4.拷贝构造
1.属于
构造函数的一个重载形式,但编译器会在不写的情况下生成,也就是说一个类里至少会有两个构造。
拷贝构造十分特殊,需与构造区分。
2.作用
对象初始化时可以通过拷贝一个相同类的对象来初始化。
例子:1.将对象以实参形式传给形参(形参是实参的拷贝,会调用拷贝构造来初始化形参)
2.外部调用拷贝构造初始化一个一模一样的对象。
3.规定
参数只能是一个接受实参的同类引用
Qt:1.为什么不能是对象接受而是引用接受。
Ans:对象接受会造成无限递归调用,使用拷贝构造要传参,将实参对象给形参要调用拷贝构造拷贝,使用拷贝构造要传参......
(编译器这里会当作语法错误检查)。
Qt:2.为什么不能用指针接收?
Ans:指针是会达到与引用同样效果的,但是由于拷贝构造在1.对象间相互赋值 2.传参 时会自动调用拷贝构造,所以需要一定的格式,引用更为方便,所以编译器认为:指针做参数的构造不是拷贝构造。不会调用指针实现的拷贝效果。
4.分类
1.编译器默认生成的为浅拷贝,或叫值拷贝。
内置类型会按地址一字节一字节地拷贝给对象(值拷贝),内置类型会调用它的拷贝构造函数拷贝(可能是深拷贝或浅拷贝)。(同构造,层层调用)
2.自己写的,分为浅拷贝和深拷贝
1.浅拷贝
只会把值拷贝给对应对象。
缺点:如果对象里有指针,那么两个对象的指针变量会指向同一块空间,空间被共享,而不是拷贝!
2.深拷贝
如果被拷贝对象里有一块空间,深拷贝后,新对象会自己另外申请一块空间。(必须手动完成)
图为栈的深拷贝。
3.什么时候要用深拷贝?
在类中有指针(包括数组)时:
1.外部以对象初始化另一对象时
2.对象赋值时
4.什么时候不用深拷贝?
没有指针就不用
1.形参接受不用(形参接收如果不需要改变值可以考虑const引用接收,节省成本还不用考虑被修改)
注:一般不写析构函数就不写拷贝构造,写了拷贝构造就要写(满足条件同,有指针或一些值指向资源,如:malloc,流,new)
5.初识运算符重载
引子:如果一个类为记录日期,怎么比较类的两个对象的日期先后。Date1>Date2外部可以这样实现吗?
定义:
为对象间的运算符赋予自己的含义。
摘自菜鸟教程:
关键字:operator
语法:operator运算符 构成函数名,前加返回值,后加参数列表,当作正常函数在类中定义;
图为类外运算符重载,可以定义两个不同或同类对象运算符比较。