【C++】类(中):类中的默认成员函数

【C++】类(中):类中的默认成员函数,第1张

C++类与对象知识点合集:

【C++】(上):初识类和对象
【C++】类(中):类中的默认成员函数
【C++】类(下):类成员与explicit、const、volatile、static、friend关键字


目录
  • 6个默认成员函数
  • 构造函数
    • 什么是构造函数
    • 默认构造函数
    • 重载构造函数
      • 为什么要自定义构造函数?
      • 一般的构造函数
      • ==带**初始化列表**的构造函数==
  • 析构函数
    • 什么是析构函数
    • 默认析构函数
    • 自定义析构函数
  • 拷贝构造函数
    • 什么是拷贝构造函数
    • 默认拷贝构造函数
    • 重载拷贝构造函数
      • 拷贝构造函数调用场景
  • 运算符重载函数
    • 什么是运算符重载函数
    • 自增自减单目运算符重载函数
  • 赋值运算符重载函数
    • 赋值运算符重载函数
    • 自定义赋值运算符重载函数
  • 取地址运算符重载函数
    • 取地址符重载 (两种)

6个默认成员函数

在C++中,定义任何一个类后,初始编译器都会为该类定义6个默认成员函数:

【注意】这6个默认的成员函数

  • 用户也能自定义实现更符合自己需求的同名函数(即支持重载);
  • 如果用户自己不定义,编译器就会自动生成默认函数并使用;
  • 而一旦用户显式提供了那么编译器不再生成,直接使用用户定义的函数;
构造函数 什么是构造函数

类的默认定义的函数之一
一个类在实例化对象时,由编译器自动调用该函数来初始化对象的数据成员。

  • 构造函数在类对象创建时编译器自动调用,并完成对象成员的初始化工作;
  • 构造函数并不为对象开辟空间,只是用来初始化对象(编译系统为对象分配内存空间);
  • 构造函数在对象的生命周期内只调用一次;
  • 构造函数可以重载;
  • 构造函数函数名与类名同名;
  • 构造函数无返回值(无返回值不是说为void,void是空类型返回值);

默认构造函数

编译器默认构造函数不显示出现:

  • 一般C++编译器会自动生成一个无参的默认构造函数;
  • 默认构造函数不会显式定义;
  • 一旦用户显式重载定义编译器将不再生成;
  • 默认的构造函数是一个无参构造函数;
    【注意】如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
    直接Test t;就是定义一个对象并调用了无参构造函数;
    Test t();这不就是一个函数名为t,返回值为Test的无参函数的声明么,因此为了避免这种误解,编译器制订了上述规则;
  • 编译器默认的构造函数会为对象成员赋随机值(就像一个变量定义后初始内部值无意义)

重载构造函数 为什么要自定义构造函数?

定义一个变量,如果不初始化,该变量内部值无意义;
在C++中一个变量可以定义初始化同时执行:

同理,一个类要使用必须要(像定义变量一样)实例化出对象,而这个对象要有意义,就必须要初始化确定的值;

一般的构造函数
  1. 无参的构造函数

  2. 带参构造函数

  3. 全缺省的构造函数

带初始化列表的构造函数

上述的带参构造函数(包括缺省),其实并不算真正意义上的构造函数,真正的构造函数只能为构造函数赋值一次

因此自定义构造函数时,应避免在函数体内为对象成员赋值,基于这一点就有了初始化列表

【注意】

  • 初始化列表规则只存在于构造函数(包括拷贝构造函数);

  • 编译器一般会为默认构造函数构建最基本的初始化列表;

  • 每个成员变量在初始化列表只能出现一次(初始化只能初始化一次);

  • 类中以下成员必须在初始化列表初始化处理引用成员变量const成员变量自定义类型成员(该类没有默认构造函数),只能在类中声明;

    【原因】
    如果用户不构建初始化列表,编译器会自动为每个构造函数(包括带参构造函数)增加最基本的初始化列表;
    这样就导致进入函数体前,const变量、引用变量、自定义类对象已提前初始化了(自定义对象会调用默认无参构造函数);
    因此,在进入函数体后,构造函数内能做的只有赋值,而const类型和引用类型是不可以赋值的;
    而如果自定义类中没有无参构造函数,编译器在默认的初始化队列就直接报错;

  • 成员变量在类中的定义顺序就是初始化顺序,与初始化队列里的次序无关;


析构函数 什么是析构函数

类的默认定义的函数之一
一个对象使用结束后,由编译器自动调用析构函数回收该对象的成员数据。

  • 对象生命周期结束时,C++编译系统系统自动调用析构函数;
  • 一个类有且仅有一个析构函数(注意,析构函数不能重载);
  • 但析构函数可以自定义实现,若未显式定义,系统会自动生成默认的析构函数;
  • 默认析构函数和自定义析构函数都是系统自动调用;
  • 析构函数名是在类名前加上字符 ~ :~类名(){...}
  • 析构函数无参数无返回值;

默认析构函数

编译器默认析构函数不显示出现:

  • 一般C++编译器会自动生成一个最基础的回收对象普通成员变量的析构函数;
  • 该默认析构函数不是显示定义;
  • 若类B的某些成员是另一个类A的对象,且这类B没有自定义析构函数,那么类B对象结束时,默认的析构函数中会自动调用类A的析构函数;
  • 默认析构函数无法处理在堆上动态开辟的对象成员变量

    为了解决这种问题,C++运行用户自定义实现析构函数,内部可以自己实现释放对象在堆上开辟的空间;

自定义析构函数

为什么不说重载析构函数,因为一个类只能有一个析构函数,一旦用户定义了,编译器就不会实现默认的析构函数;

自定义析构函数主要用来处理对象在堆上动态开辟的空间,如果对象的初始化不涉及堆的空间,用户无需定义析构函数,使用默认析构函数即可;

通过自定义析构函数可以看到析构函数都在这三种场景被调用:


拷贝构造函数 什么是拷贝构造函数

类的默认定义的函数之一
一个类可以通过编译器自动调用该函数,让一个已存在的对象实例化一个新的对象。

  • 拷贝构造函数由编译器自动调用;
  • 拷贝构造函数其实算是一种构造函数的重载;
  • 拷贝构造函数函数名同类名一样,参数为一个该类对象的引用(注意形参对象是引用类型,且一般用const修饰,避免修改原对象);
  • 拷贝构造函数可以重载,若未显式定义,系统会自动生成默认的拷贝构造函数;

默认拷贝构造函数

编译器默认拷贝构造函数不会显示出现;

  • 若未显示定义,系统生成默认的拷贝构造函数;
  • 默认拷贝构造函数的拷贝方式是浅拷贝,而浅拷贝在某些情况下会产生双重释放问题;

重载拷贝构造函数

为了避免默认构造函数的浅拷贝问题,C++允许用户自定义实现拷贝构造函数;

  • 自定义拷贝构造函数就是要实现深拷贝解决动态开辟成员的浅拷贝问题(如无必要直接使用默认函数);
  • 参数对象一般为引用类型,且用const修饰(防止被拷贝对象被修改);
  • 拷贝构造函数的参数对象必须采用引用参数(常用)或指针参数,不能直接值传参!!
拷贝构造函数调用场景
  1. 通过一个对象实例化开辟一个新的对象;
  2. 函数参数为类对象;
  3. 函数返回值为类对象;

运算符重载函数 什么是运算符重载函数

为了代码的可读性更强,C++允许通过运算符重载函数实现各种运算符的特殊化。

  • C++中允许对各种运算符进行重载(类内、类外都可以),但参数一定要有自定义类型(类类型或者枚举类型);

  • 运算符重载本质上是一种特殊的函数;

  • 采用关键字operator,后接需要重载的运算符符号(=、&、*、+、-、+=、-=、>、<… );
    例:一个相等运算符的重载函数 bool operator==(const Data& left,const Data& right){ ... }(定义在类外的例子)

  • 不能重载不存在的 *** 作符(比如operator@);

  • 用于内置类型的 *** 作符,其含义不能改变,例如:内置的整型 +,不能改变其含义为-;

  • 当作为类成员的重载函数时,函数第一个参数是隐藏的形参this;

  • 注意以下5个运算符不能重载

  • == 与 != 的自定义重载一般是成对出现;

  • C++中每个类有两个默认的运算符重载:赋值运算符(=)重载、取地址运算符(&)重载

自增自减单目运算符重载函数

如果现在需要为一个类添加重载自增运算符:

  • 单目 *** 作符只有一个参数;
  • 需要有前置++,和后置++两种;
  • 为了区分前置++与后置++,一般让后置++重载函数带一个无意义的占位参数(仅作为函数区分);

赋值运算符重载函数 赋值运算符重载函数

类的默认定义的函数之一
通过重载 = *** 作符来实现特殊化 *** 作,常用做对象间的赋值。

  • 该方法编译器会自动生成;
  • 编译器生成的赋值运算符方法本质是浅拷贝;

自定义赋值运算符重载函数

为避免类中默认的赋值运算符重载函数引发的浅拷贝问题,C++允许用户自定义实现该函数;

  • 参数类型与最好是引用+const修饰,提高效率,传值效率太慢;
  • 返回值类型设为该类的引用,以此实现连续赋值;
  • 函数内必须进行检测,避免自己给自己赋值;

取地址运算符重载函数 取地址符重载 (两种)

对普通类& *** 作、对const类& *** 作,这两个默认成员函数一般不用重新定义 ,编译器默认会生成


关于类和对象的概念仍有一些好玩的机制,具体见下一篇博客~

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

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

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

发表评论

登录后才能评论

评论列表(0条)

保存