16 经典问题解析二

1 析构函数调用顺序

  • 【问题】当程序中存在多个对象时,如何确定这些对象的析构顺序?

    • 对于栈对象和全局对象,类似于入栈和出栈的顺序,最后构造的对象被最先析构
    • 堆对象的析构发生在使用 delete 的时候,delete 的使用顺序相关
  • 单个对象创建时构造函数的调用顺序

    • 1.调用父类的构造函数
    • 2.调用成员变量的构造函数(调用顺序与声明顺序相同)
    • 3.调用类自身的构造函数
    • 析构函数与对应构造函数的调用顺序相反
  • 多个对象析构时,析构顺序与构造顺序相反

  • 示例1:构造与析构顺序

    • Demo

      #include <stdio.h>
      
      class Member
      {
          const char* ms;
      public:
          Member(const char* s) {
              printf("Member(const char* s): %s\n", s);    
              ms = s;
          }
          ~Member() {
              printf("~Member(): %s\n", ms);
          }
      };
      
      class Test
      {
          Member mA;
          Member mB;
      public:
          Test() : mB("mB"), mA("mA") {
              printf("Test()\n");
          }
          ~Test() {
              printf("~Test()\n");
          }
      };
      
      Member gA("gA");
      
      int main()
      {
          Test t;
          
          return 0;
      }
      
    • 编译运行

      Member(const char* s): gA
      Member(const char* s): mA
      Member(const char* s): mB
      Test()
      ~Test()
      ~Member(): mB
      ~Member(): mA
      ~Member(): gA
      

2 const 对象

  • 【问题】const 关键字能否修饰类的对象,如果可以,有什么特性?

    • const 关键字能够修饰类的对象
    • const 修饰的对象为只读对象
    • 只读对象的成员变量不允许被改变
    • 只读变量是编译阶段的概念,运行时无效
  • const 成员函数的定义

    Type ClassName::function(Tyep p) const

    类中的函数声明与实际函数定义中都必须带 const 关键字

  • C++ 中的 const 成员函数

    • const 对象只能调用 const 的成员函数
    • const 成员函数中只能调用 const 成员函数
    • const 成员函数中不能直接改写成员变量的值
  • 示例2:类的 const 函数

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

      #include <stdio.h>
      
      class Test
      {
          int mi;
      public:
          int mj;
          Test(int i);
          Test(const Test& t);
          int getMi();
      };
      
      Test::Test(int i)
      {
          mi = i;
      }
      
      Test::Test(const Test& t)
      {
          
      }
          
      int Test::getMi()
      {
          return mi;
      }
      
      int main()
      {
          // 定义 const 对象
          const Test t(1);
          t.mj = 1000;
          
          return 0;
      }
      
    • 编译

      test.cpp: In function ‘int main()’:
      test.cpp:31:10: error: assignment of member ‘Test::mj’ in read-only object
           t.mj = 1000;
                ^
      
    • Demo2:const 对象只能调用 const 的成员函数

      #include <stdio.h>
      
      class Test
      {
          int mi;
      public:
          int mj;
          Test(int i);
          Test(const Test& t);
          int getMi();
      };
      
      Test::Test(int i)
      {
          mi = i;
      }
      
      Test::Test(const Test& t)
      {
          
      }
          
      int Test::getMi()
      {
          return mi;
      }
      
      int main()
      {
          const Test t(1);
          printf("t.getMi() = %d\n",t.getMi());  // const对象不能调用普通成员函数
          
          return 0;
      }
      
    • 编译

      test.cpp: In function ‘int main()’:
      test.cpp:31:39: error: passing ‘const Test’ as ‘this’ argument discards qualifiers [-fpermissive]
           printf("t.getMi() = %d\n",t.getMi());
                                             ^
      test.cpp:23:5: note:   in call to ‘int Test::getMi()’
       int Test::getMi()
           ^
      

3 类成员

  • 【问题】成员函数和成员变量都是隶属于具体对象的么?

  • 从对象对象的角度

    • 对象由属性(成员变量)和方法(成员函数)构成
  • 从程序运行的角度

    • 对象由数据和函数构成
      • 数据可以位于栈,堆和全局数据区
      • 函数只能位于代码段
  • 结论

    • 每一个对象拥有自己独立的属性(成员变量)
    • 所有的对象共享类的方法(成员函数)
    • 类的方法能够直接访问对象的属性
    • 类的方法中的隐藏参数 this 指针(常量指针,始终指向调用当前函数的对象)用于指代当前对象
  • 示例3

    • Demo

      #include <stdio.h>
      
      class Test
      {
          int mi;
      public:
          int mj;
          Test(int i);
          Test(const Test& t);
          int getMi();
          void print();
      };
      
      Test::Test(int i)
      {
          mi = i;
      }
      
      Test::Test(const Test& t)
      {
          mi = t.mi;  //为什么拷贝构造函数可以访问对象t的private成员变量mi?  =>封装是编译期的概念,是针对类型而非对象的,在类的成员函数中可以访问同类型实例对象的私有成员变量
      }
          
      int Test::getMi()
      {
          return mi;
      }
      
      void Test::print()
      {
          printf("this = %p\n", this);
      }
      
      int main()
      {
          Test t1(1);
          Test t2(2);
          Test t3(3);
          
          printf("t1.getMi() = %d\n", t1.getMi());  // t1.getMi() = 1
          printf("&t1 = %p\n", &t1);                // &t1 = 0xbf975fd4
          t1.print();                               // this = 0xbf975fd4
          
          printf("t2.getMi() = %d\n", t2.getMi());  // t2.getMi() = 2
          printf("&t2 = %p\n", &t2);                // &t2 = 0xbf975fdc
          t2.print();                               // this = 0xbf975fdc
          
          printf("t3.getMi() = %d\n", t3.getMi());  // t3.getMi() = 3
          printf("&t3 = %p\n", &t3);                // &t3 = 0xbf975fe4
          t3.print();                               // this = 0xbf975fe4
          
          return 0;
      }
      
posted @ 2020-09-23 19:45  nxgy  阅读(119)  评论(0编辑  收藏  举报