0


『C++成长记』构造函数和析构函数

🔥博客主页:小王又困了

📚系列专栏:C++

🌟**人之为学,不日近则日退 **

❤️感谢大家点赞👍收藏⭐评论✍️


一、类的六个个默认成员函数

如果一个类中什么成员都没有,简称为空类。

空类不是没有任何用,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

📒1.1认识默认成员函数

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

  • 构造函数:完成初始化工作。
  • 析构函数:完成对空间的清理工作。
  • 拷贝构造:使用同类对象初始化创建对象。
  • 赋值重载:把一个对象赋值给另外一个对象。
  • 取地址重载:普通对象和const对象取地址,这两个很少自己实现。

二、构造函数

📒2.1为什么要有构造函数

  1. class Date
  2. {
  3. public:
  4. void Init(int year, int month, int day)
  5. {
  6. _year = year;
  7. _month = month;
  8. _day = day;
  9. }
  10. private:
  11. int _year;
  12. int _month;
  13. int _day;
  14. };
  15. int main()
  16. {
  17. Date d1;
  18. d1.Init(2022, 7, 5);//调用初始化函数
  19. return 0;
  20. }
  1. 以前我们创建对象后,都要自己调用初始化函数,这样的操作十分麻烦,而且当对象增多时,有些对象我们可能会忘记初始化,这时程序就会出现错误。那么能否在创建对象的时候,就自动对它初始化。于是,就有了构造函数。

📒2.2构造函数的概念

  1. 构造函数是一个特殊的成员函数,**名字与类名相同,创建类类型对象时由编译器自动调用**,以保证每个数据成员都有 一个合适的初始值,并且在**对象整个生命周期内只调用一次**。
  1. class Date
  2. {
  3. public:
  4. //构造函数
  5. Date(int year, int month, int day)
  6. {
  7. _year = year;
  8. _month = month;
  9. _day = day;
  10. }
  11. private:
  12. int _year;
  13. int _month;
  14. int _day;
  15. };
  16. void TestDate()
  17. {
  18. Date d1(2015, 1, 1); // 自己调用构造函数
  19. }

📒2.3构造函数的特性

  1. **构造函数**是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并**不是开空间创建对象**,而是**初始化对象**。

📝特性:

  • 函数名与类名相同。
  • 无返回值。
  • 对象实例化时编译器自动调用对应的构造函数。
  • 构造函数可以重载。
  1. class Date
  2. {
  3. public:
  4. // 1.无参构造函数
  5. Date()
  6. {}
  7. // 2.带参构造函数
  8. Date(int year, int month, int day)
  9. {
  10. _year = year;
  11. _month = month;
  12. _day = day;
  13. }
  14. private:
  15. int _year;
  16. int _month;
  17. int _day;
  18. };
  19. void TestDate()
  20. {
  21. Date d1; // 调用无参构造函数
  22. Date d2(2015, 1, 1); // 调用带参的构造函数
  23. Date d3();//错误写法
  24. }

** 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明。**

**📒2.4编译器生成的构造函数 **

  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
  1. class Date
  2. {
  3. public:
  4. /*
  5. // 如果用户显式定义了构造函数,编译器将不再生成
  6. Date(int year, int month, int day)
  7. {
  8. _year = year;
  9. _month = month;
  10. _day = day;
  11. }
  12. */
  13. private:
  14. int _year;
  15. int _month;
  16. int _day;
  17. };
  18. int main()
  19. {
  20. Date d1;
  21. return 0;
  22. }
  1. Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数。 Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再 生成默认构造函数,Date d1; 没有可以调用的无参构造函数,所以会失败。

📒2.5编译器生成构造函数的作用

  1. d1对象调用了编译器生成的默认构造函数,但是对象_year_month_day,是随机值。那这里编译器生成的默认构造函数有什么用呢?

解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int、char...,自定义类型就是我们使用class、struct、union等自己定义的类型。(所有类型的指针都属于内置类型)。编译器生成的默认构造函数,对内置类型不做处理,对自定义类型,会去调用它的默认构造函数。

  1. //先定义一个时间类
  2. class Time
  3. {
  4. public:
  5. Time()
  6. {
  7. cout << "Time()" << endl;
  8. _hour = 0;
  9. _minute = 0;
  10. _second = 0;
  11. }
  12. private:
  13. int _hour;
  14. int _minute;
  15. int _second;
  16. };
  17. //再定义一个日期类
  18. class Date
  19. {
  20. private:
  21. // 基本类型(内置类型)
  22. int _year;
  23. int _month;
  24. int _day;
  25. // 自定义类型
  26. Time _t;
  27. };
  28. int main()
  29. {
  30. Date d;
  31. return 0;
  32. }

  1. 如上代码 ,定义了一个日期类Date,有四个成员变量,其中_year_month_day都是内置类型,_t 是自定义类型,它是一个时间类TimeTime类中写了一个构造函数。运行程序,自定义类型调用了自己的默认构造函数,对值进行了初始化。这意味着,在创建对象的时候,会去使用编译器生成的无参默认构造函数。
  1. C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:**内置类型成员变量在类中声明时可以给默认值。 **

**🎀示例: **

  1. class Date
  2. {
  3. private:
  4. int _year = 2023;
  5. int _month = 11;
  6. int _day = 16;
  7. };
  8. int main()
  9. {
  10. Date d1;
  11. return 0;
  12. }

📒2.6默认构造函数

  1. **编译器自动生成的构造函数**、**无参构造函数**、**全缺省构造函数**,这三种都叫做**默认构造函数**,它们都有一个共同的特点:**可以不用传参**。默认构造函数**只能有一个**,后面俩,在语法上可以构成函数重载,但是在无参调用的时候,会发生歧义,出现调用不明确。

  1. //无参构造函数
  2. Date()
  3. {
  4. _year = 2023;
  5. _month = 11;
  6. _day = 16;
  7. }
  8. //全缺省构造函数
  9. Date(int year = 2023, int month = 11, int day = 16)
  10. {
  11. _year = year;
  12. _month = month;
  13. _day = day;
  14. }

注意:要把默认构造函数和默认成员函数区分清楚,默认成员函数是我们不写编译器会自动生成的,默认构造函数是不需要传参的构造函数。编译器生成的构造函数,既是默认构造函数,同时也是默认成员函数。

三、析构函数

📒3.1析构函数的概念

  1. 析构函数与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而**对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。**

📒3.2析构函数的特性

📝特性:

  • 析构函数名是在类名前加上字符 ~。
  • 无参数无返回值类型。
  • 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  • 对象生命周期结束时,C++编译系统系统自动调用析构函数。
  1. typedef int DataType;
  2. class Stack
  3. {
  4. public:
  5. Stack(size_t capacity = 3)
  6. {
  7. _array = (DataType*)malloc(sizeof(DataType) * capacity);
  8. if (NULL == _array)
  9. {
  10. perror("malloc申请空间失败!!!");
  11. return;
  12. }
  13. _capacity = capacity;
  14. _size = 0;
  15. }
  16. void Push(DataType data)
  17. {
  18. // CheckCapacity();
  19. _array[_size] = data;
  20. _size++;
  21. }
  22. // 其他方法...
  23. ~Stack()//析构函数
  24. {
  25. if (_array)
  26. {
  27. free(_array);
  28. _array = NULL;
  29. _capacity = 0;
  30. _size = 0;
  31. }
  32. }
  33. private:
  34. DataType* _array;
  35. int _capacity;
  36. int _size;
  37. };
  38. void TestStack()
  39. {
  40. Stack s;
  41. s.Push(1);
  42. s.Push(2);
  43. }
  1. Stack中的成员变量_array是在堆区上申请的空间,这块空间在程序结束后不会自动还给操作系统,当_array清理后,就找不到动态申请的那块空间,会造成内存泄漏,因此在对象销毁前,要通过析构函数去释放成员变量_array指向的空间,这就是析构函数的作用。

注意:析构函数不能重载

📒3.3编译器生成析构函数的作用

  1. 编译器会自动生成的析构函数,对内置类型不做处理,对自定义类型会去调用它的析构函数。
  1. class Time
  2. {
  3. public:
  4. ~Time()
  5. {
  6. cout << "~Time()" << endl;
  7. }
  8. private:
  9. int _hour;
  10. int _minute;
  11. int _second;
  12. };
  13. class Date
  14. {
  15. private:
  16. // 基本类型(内置类型)
  17. int _year = 1970;
  18. int _month = 1;
  19. int _day = 1;
  20. // 自定义类型
  21. Time _t;
  22. };
  23. int main()
  24. {
  25. Date d;
  26. return 0;
  27. }

  1. main函数中创建了 Date对象d,而d中包含4个成员变量,其中_year, _month,_day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_tTime类对象,所以在 d 销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。但是: main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即**当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁。**

📝总结:

  1. 一般情况下,有动态申请资源,就需要显式的写析构函数来释放资源,比如Stack类。没有动态申请的资源,可以不写析构函数,直接使用编译器生成的默认析构函数,比如 Date类。需要释放资源的成员都是自定义类型,也不需要写析构函数。

🎁**结语: **

** 本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者三连支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。你们的支持就是博主最大的动力。**

标签: c++ 开发语言 算法

本文转载自: https://blog.csdn.net/2301_76207836/article/details/134429763
版权归原作者 小王又困了 所有, 如有侵权,请联系我们删除。

“『C++成长记』构造函数和析构函数”的评论:

还没有评论