你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

c++之析构函数和const总结

[复制链接]
gaosmile 发布时间:2020-8-7 21:07

大家好,今天再次写c++的文章,首先给各位网友说明一下这段时间为啥都是写c++的文章,没有Linux的文章;原因是这样的,自己立了一个flag,八月份把c++的基本语法全部过完(目前完成三分之一),所以文章过程中我写的可能没有很详细的解释一些概念;所以文章中有不理解的地方,可以私聊我,我会尽力解答好。同时昨天遇到一位刚高考完的网友,人家都这么努力学习,你还打酱油吗?

微信图片_20200807210432.png
微信图片_20200807210435.png
微信图片_20200807210438.png

一、对象的析构顺序:

在上一篇文章里面,已经介绍过对象的构造顺序,既然有对象的产生,那么就会有对象的消亡;换句话说,有资源的分配,最终系统就要收回这些分配出去的资源。所以也就有了对象的析构顺序用法了。下面我们来看一下对象析构顺序的分类,不过这里我们先来看构造对象调用构造函数的来引出对象析构的顺序:

(1)单个对象创建时构造函数的调用顺序:

  • 调用父类的构造过程(这个概念暂时还没学,先不用管)

  • 调用成员变量的构造函数(调用顺序与声明顺序相同)

  • 调用类自身的构造函数


引出:析构函数与对应的构造函数的调用顺序相反,也就是对象构造与对象析构(消亡)的顺序相反。

(2)代码演示:

  1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">
  2. #include <stdio.h>

  3. class Test
  4. {
  5.   const char * i;
  6.   public:
  7.      Test(const char *s)
  8.      {
  9.         printf("Test(const char *s)  is %s\n",s);
  10.         i=s;
  11.      }
  12.      ~Test()
  13.      {
  14.           printf("~Test() is %s\n",i);
  15.      }
  16. };

  17. class Test2
  18. {
  19.     Test mA;
  20.     Test mB;
  21. public:
  22.     Test2():mB("mB"),mA("mA")
  23.         {
  24.            printf("Test2()\n");
  25.         }
  26.     ~Test2()
  27.     {
  28.         printf("~Test2()\n");
  29.     }
  30. };

  31. Test gA("gA");

  32. int main()
  33. {
  34.     Test2 t;

  35.     return 0;
  36. }
  37. </font></font></font>
复制代码

演示结果:

  1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">
  2. Test(const char *s)  is gA
  3. Test(const char *s)  is mA
  4. Test(const char *s)  is mB
  5. Test2()
  6. ~Test2()
  7. ~Test() is mB
  8. ~Test() is mA
  9. ~Test() is gA</font></font></font>
复制代码

说明:从运行结果,我们可以看到先触发全局对象的构造函数,然后是触发初类Test2里面的mA和mB对象的构造函数,最后 触发局部对象的构造函数,然后进行析构,我们可以看到,析构顺序完全和构造顺序相反。非常类似于栈的操作规则,栈是先入栈,却是最后出栈。

二、const关键字可以修饰类的对象嘛?

1、这个问题答案肯定是可以修饰的,为啥这么说呢?不知大家还是记得c语言里面的struct关键字不,在之前的文章里面也学习过,使用struct关键字也可以来构造类名,只不过他的所有成员都是公开的(public);换句大家好理解的话,就是结构体,那么在c语言里面,const关键字肯定是可以修饰结构体变量的,当然在c++里面肯定也是可以的,c++不是取代c语言的,而是对c语言进行扩展着,并且兼容c语言的。不过const修饰的对象有啥特性呢?

const修饰的对象特性:

  • const修饰的对象为只读对象

  • 只读对象的成员变量不允许被改变

  • 只读对象是编译阶段的概念,运行时无效


代码示例:

  1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">
  2. #include <stdio.h>

  3. class Test
  4. {
  5.     int mi;
  6. public:
  7.     int mj;
  8.     Test(int i);
  9.     Test(const Test& t);
  10.     int getMi();
  11. };

  12. Test::Test(int i)
  13. {
  14.     mi = i;
  15. }

  16. Test::Test(const Test& t)
  17. {

  18. }

  19. int Test::getMi()
  20. {
  21.     return mi;
  22. }

  23. int main()
  24. {


  25.         const Test t1(1);

  26.         t1.mj = 100;
  27.         printf("the t1.mj is %d\n",t1.mj);

  28.         return 0;
  29. }</font></font></font>
复制代码

运行结果:


  1. test.cpp: In function ‘int main()’:
  2. test.cpp:34:10: error: assignment of member ‘Test::mj’ in read-only object
  3.   t1.mj = 100;
复制代码

分析:我们可以看出public里面的属性mj是不能修改的,为只读的,因为类对象被const修饰了。

2、const成员函数:

其实以前在写初始化列表的文章之前,以前提到过const修饰的成员属性(不过这个属性是私密的),我们只能用初始化列表对其进行初始化;提这个的原因是;也算是复习一下;同时说的直白一点,咋们刚才讲解完了const修饰的对象,同时用该对象访问的类成员属性是不能修改的;现在我们当然会想到const成员函数了,一环扣一环嘛(我不说,你看到这里也会有好奇心!);下面我们来看一下const成员函数的定义和规则:

(1)定义:

  1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">
  2. Type ClassName::function(Type p)const

  3. 类中的函数声明与实际函数定义中都必须带const关键字,注意const是函数结尾</font></font></font>
复制代码

(2)规则:

const对象只能调用const的成员函数,不能调用普通的成员函数,例如:

  1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">
  2. #include <stdio.h>

  3. class Test
  4. {
  5.     int mi;
  6. public:
  7.     int mj;
  8.     Test(int i);
  9.     Test(const Test& t);
  10.     int getMi();
  11. };

  12. Test::Test(int i)
  13. {
  14.     mi = i;
  15. }

  16. Test::Test(const Test& t)
  17. {

  18. }

  19. int Test::getMi()
  20. {
  21.     return mi;
  22. }

  23. int main()
  24. {


  25.         const Test t1(1);

  26.         printf("the mi is %d\n",t1.getMi());

  27.         return 0;
  28. }</font></font></font>
复制代码

演示结果:


  1. test.cpp: In function ‘int main()’:
  2. test.cpp:34:42: error: passing ‘const Test’ as ‘this’ argument discards qualifiers [-fpermissive]
  3.          printf("the mi is %d\n",t1.getMi());
  4.                                           ^
  5. test.cpp:23:6: note:   in call to ‘int Test::getMi()’
  6.   int Test::getMi()
  7.       ^~~~
复制代码


我们可以看到const修饰的类对象调用普通成员函数报错,现在我们把普通成员函数getMi()改成const成员函数:


  1. #include <stdio.h>

  2. class Test
  3. {
  4.     int mi;
  5. public:
  6.     int mj;
  7.     Test(int i);
  8.     Test(const Test& t);
  9.     int getMi() const;
  10. };

  11. Test::Test(int i)
  12. {
  13.     mi = i;
  14. }

  15. Test::Test(const Test& t)
  16. {

  17. }

  18. int Test::getMi() const
  19. {
  20.     return mi;
  21. }

  22. int main()
  23. {


  24.         const Test t1(1);

  25.         printf("the mi is %d\n",t1.getMi());

  26.         return 0;
  27. }
复制代码

这样的话就可以编译通过了,哈哈。

const成员函数不能直接修改成员变量的值:


  1. #include <stdio.h>

  2. class Test
  3. {
  4.     int mi;
  5. public:
  6.     int mj;
  7.     Test(int i);
  8.     Test(const Test& t);
  9.     int getMi() const;
  10. };

  11. Test::Test(int i)
  12. {
  13.     mi = i;
  14. }

  15. Test::Test(const Test& t)
  16. {

  17. }

  18. int Test::getMi() const
  19. {
  20.     mj = 89;
  21.     return mi;
  22. }

  23. int main()
  24. {


  25.         const Test t1(1);

  26.         printf("the mi is %d\n",t1.getMi());

  27.         return 0;
  28. }
复制代码

运行结果:


  1. root@txp-virtual-machine:/home/txp# g++ test.cpp
  2. test.cpp: In member function ‘int Test::getMi() const’:
  3. test.cpp:25:10: error: assignment of member ‘Test::mj’ in read-only object
  4.      mj = 89;
复制代码

const 成员函数中只能调用 const 成员函数:

  1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">
  2. #include <stdio.h>

  3. class Test
  4. {
  5.     int mi;
  6. public:
  7.     int mj;
  8.     Test(int i);
  9.     Test(const Test& t);
  10.     int getMi() const;
  11.     int getMj() const;
  12. };

  13. Test::Test(int i)
  14. {
  15.     mi = i;
  16. }

  17. Test::Test(const Test& t)
  18. {

  19. }

  20. int Test::getMj() const
  21. {
  22.     return mj;
  23. }
  24. int Test::getMi() const
  25. {
  26.     getMj();
  27.     return mi;
  28. }

  29. int main()
  30. {


  31.         const Test t1(1);

  32.         printf("the mi is %d\n",t1.getMi());

  33.         return 0;
  34. }</font></font></font>
复制代码

3、搞清楚成员函数和成员变量是否都是隶属于具体对象

  • 从面向对象的角度看,对象由属性(成员变量)和方法(成员函数)构成

  • 从程序运行的角度来看,对象由数据和函数构成,而数据可以位于栈、堆、全局数据区;而函数只能位于代码段,而代码段是只读的,在程序运行过程中是不可以被改变的,而对于数据来说,他们处于栈、堆,是可以动态的创建和动态的删除;


4、this指针

不知大家是否注意到在拷贝构造函数里面的这条语句,参数里面是引用,那么t也相当于是一个对象,所以会有t.mi这样的写法,这里要明白:


  1. Test(const Test& t)
  2. {
  3.    mi = t.mi;
  4.                
  5. }
复制代码


所以这里我就引出了this指针,他表示方法中的隐藏参数代指当前对象,为了说明这个,下面我们来看一个代码示例:

  1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">
  2. #include <stdio.h>

  3. class Test
  4. {
  5.     int mi;
  6. public:
  7.     int mj;
  8.     Test(int i);
  9.     Test(const Test& t);
  10.     int getMi();
  11.     void print();
  12. };

  13. Test::Test(int i)
  14. {
  15.     mi = i;
  16. }

  17. Test::Test(const Test& t)
  18. {
  19.     mi = t.mi;
  20. }
  21.    
  22. int Test::getMi()
  23. {
  24.     return mi;
  25. }

  26. void Test::print()
  27. {
  28.     printf("this = %p\n", this);
  29. }

  30. int main()
  31. {
  32.     Test t1(1);
  33.     Test t2(2);
  34.     Test t3(3);
  35.    
  36.     printf("t1.getMi() = %d\n", t1.getMi());
  37.     printf("&t1 = %p\n", &t1);
  38.     t1.print();
  39.    
  40.     printf("t2.getMi() = %d\n", t2.getMi());
  41.     printf("&t2 = %p\n", &t2);
  42.     t2.print();
  43.    
  44.     printf("t3.getMi() = %d\n", t3.getMi());
  45.     printf("&t3 = %p\n", &t3);
  46.     t3.print();
  47.    
  48.     return 0;
  49. }</font></font></font>
复制代码

输出结果:

  1. <font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"><font color="black">
  2. root@txp-virtual-machine:/home/txp# ./a.out
  3. t1.getMi() = 1
  4. &t1 = 0x7ffec3f5e750
  5. this = 0x7ffec3f5e750
  6. t2.getMi() = 2
  7. &t2 = 0x7ffec3f5e758
  8. this = 0x7ffec3f5e758
  9. t3.getMi() = 3
  10. &t3 = 0x7ffec3f5e760
  11. this = 0x7ffec3f5e760</font></font></font>
复制代码

分析:

  • 在类的成员函数当中,有一个隐含的参数,就是我们刚才说的this指针,它的值就是调用这个函数所对应的对象的地址,说白了,this指针指向当前对象。

  • 每一个对象都有一套自己的成员变量,对象和对象之间的成员变量是独立的、不同的;但是每一个对象都共享一个类的成员函数


三、总结:

  • 对象的析构顺序与构造顺序相反

  • const 关键字能够修饰对象,得到只读对象

  • 只读对象只能调用 const 成员函数

  • 所有对象共享类的成员函数

  • 隐藏的 this 指针用于表示当前对象


好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。


收藏 评论0 发布时间:2020-8-7 21:07

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版