C++~类和对象基础(构造函数,析构函数,拷贝构造函数,赋值运算符重载详解)

C++~类和对象基础(构造函数,析构函数,拷贝构造函数,赋值运算符重载详解),第1张

目录

构造函数

1.概念

2.特性

Q.什么是默认构造函数?自动生成默认构造函数有什么用?

析构函数

1.概念

2.特性

3. 默认生成的析构函数有什么用?

拷贝构造函数

1.概念

2.特性

3.使用默认生成拷贝构造函数需要注意什么

赋值运算符重载

1.什么是运算符重载

2.赋值运算符重载

3.默认生成的赋值运算符重载

4.取地址及const取地址 *** 作符重载


构造函数 1.概念

        构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用。构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用

2.特性

1. 函数名与类名相同。

2. 没有返回值。

3. 对象实例化时编译器自动调用对应的构造函数。

4. 构造函数可以重载。

5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数(只要定义了就不会自动生成了),一旦用户显式定义编译器将不再生成。

6.无参的构造函数和全缺省的构造函数都称为默认构造函数(可以不用传参的构造函数就是默认构造参数),并且默认构造函数只能有一个(你定义一个无参的,再定义一个全缺省的,能通过编译,但是在调用的时候,不传入参数的情况下就会出现冲突)。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数(默认就是指不用传参就可以调用的构造函数)。

7.构造函数的主要任务并不是开空间创建对象,而是初始化对象。

Q.什么是默认构造函数?自动生成默认构造函数有什么用?

默认构造参数(不用传参就可以调用的构造函数就是默认构造函数)有三种:

  1. 我们不写,编译器默认生成的构造函数
  2. 我们写的无参构造函数
  3. 我们写的全缺省或者半缺省构造函数(全缺省最常用,因为使用的时候既可以传参数也可以不传参数

C++把类型分成内置类型(基本类型包括指针)和自定义类型。

        默认生成构造函数对于内置类型成员变量不做处理,对于自定义类型成员变量才会处理(会调用这个自定义类型成员的默认构造函数)

        如果调用了编译器自己生成的默认构造函数,则对内置类型不做处理,对自定义类型会在初始化列表中调用该类型的默认构造函数进行初始化(如果该类型没有默认构造函数,则需要显示地在初始化列表初始化自定义类型,否则编译会报错),如果调用的是自己写的构造函数,则编译器在初始化列表中也会调用该类型的默认构造函数


析构函数 1.概念

        析构函数也是一个特殊的成员函数,与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作(资源清理并不是笼统的指销毁对象,资源清理指的是如下图对栈的处理,对指针的空间进行释放,将指针置空,将栈的元素数量置0这种 *** 作)。        

2.特性

1. 析构函数名是在类名前加上字符 ~。

2. 无参数无返回值类型(所以自然也就无法重载)。

3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数(和自动生成的构造函数类似,对内置类型不做处理,自定义类型会去调用它的析构函数)。注意:析构函数不能重载

4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

3. 默认生成的析构函数有什么用?

        如果我们在类A里面定义了自定义类型B作为A的成员变量,此时我们在类B中有默认析构函数,那么我们在类A中可以不显示的写析构函数,因为编译器会为我们默认生成一个析构函数,当对象A的生命周期结束的时候,A中默认的析构函数对内置类型不处理,对B这样的自定义类型会去自动调用B默认的析构函数,从而将B的空间释放。


拷贝构造函数 1.概念

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

2.特性

1. 拷贝构造函数是构造函数的一个重载形式。

2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用

3.类中如果没有涉及资源申请(没有自己在堆上面new一片连续空间时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

4.拷贝构造函数典型调用场景:(编译器会自己调用拷贝构造函数来做这些事

  1. 使用已存在对象创建新对象
  2. 函数参数类型为类类型对象
  3. 函数返回值类型为类类型对象

3.使用默认生成拷贝构造函数需要注意什么

        一般的类,自己生成拷贝构造就够用了。只有像stack这样类,我们在初始化的时候需要自己在堆上面new一块空间的,自己直接管理资源的类需要自己实现深拷贝。

        默认生成的拷贝构造函数实际上是memcpy按字节拷贝值过去的,属于浅拷贝,所以如果栈里面存放的是内置类型的数组元素,则也不需要自己写拷贝构造,因为数组元素是存放在栈上的,通过memcpy的浅拷贝可以正确实现拷贝。


赋值运算符重载 1.什么是运算符重载

        内置类型可以使用基本的运算符,但是自定义类型不行,所以为了让自定义类型也能够使用基本的运算符,c++允许对运算符进行重载。

函数名字为:关键字operator后面接需要重载的运算符符号。

函数原型:返回值类型 operator *** 作符(参数列表)——参数列表个数由运算符决定

注意:

  1. 不能通过连接其他符号来创建新的 *** 作符:比如operator@
  2. 重载 *** 作符必须有一个类类型参数(不能改变内置类型的运算符,写在类里面的,默认有一个this,这里针对的是写在类外面的,至少要有一个类类型参数)类里面的像这样写在类里面的,和内置类型参数进行运算,默认有this
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义(改变含义虽然语法上没错,但是对人的理解和使用会有很大的影响)
  4. 作为类成员函数重载时,其形参看起来比 *** 作数数目少1,因为成员函数的第一个参数为隐藏的this
  5. .*   ::   sizeof   ?:  .  注意以上5个运算符不能重载。(点星,域作用限定符,sizeof,三目运算符,对象点)
  6. 如果operator定义在类外面,则==需要传入两个运算符,并且访问的时候需要考虑对象里面的成员是私有的情况。

  7. 一般来说,operator定义在类里面,定义在里面的时候,需要少写一个参数,因为有一个是* const this(注意这里左 *** 作数是this) d1==d2实际上是d1.operator(&d1,d2)

2.赋值运算符重载
  1. 参数类型:const T&,传递引用可以提高传参效率(可以防止原对象被改变)
  2. 返回值类型:T&,返回引用可以提高返回的效率(因为出了函数作用域以后,对象还在,所以可以用引用返回),有返回值目的是为了支持连续赋值
  3. 检测是否自己给自己赋值
  4. 返回*this :要符合连续赋值的含义(因为赋值表达式是有返回值的,i=j返回值是i赋值后的值,为了符合i=j=k的写法
  5. 赋值运算符只能作为类的成员函数重载( 赋值运算符在类中不显式实现时,编译器会生成一份默认的,此时用户在类外再将赋值运算符重载为全局的,就和编译器生成的默认赋值运算符冲突了,故赋值运算符只能重载成成员函数)

3.默认生成的赋值运算符重载

自动默认生成的拷贝构造函数:

1、内置类型的成员会完成值拷贝,浅拷贝。

2、自定义类型的成员,去调用这个成员的拷贝构造

        但是如果是用一个对象去初始化另一个对象,那么就算是d1=d2,也会识别成d1(d2)这种写法,调用拷贝构造函数。

4.取地址及const取地址 *** 作符重载

        这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容,比如不想让别人拿到真实的地址。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/3001997.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)

保存