今天我们来探讨下当程序中存在多个对象时,如何确定这些对象的析构顺序?那么单个对象创建时构造函数的调用顺序是:a> 调用父类的构造过程(我们会在后面进行讲解);b> 调用成员变量的构造函数(调用顺序与生命顺序相同);c> 调用类自身的构造函数。析构函数与对应构造函数的调用顺序相反。当多个对象析构时,析构顺序与构造顺序相反。
下来我们以代码为例进行说明
#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 mB; Member mA; public: Test() : mA("mA"), mB("mB") { printf("Test()\n"); } ~Test() { printf("~Test()\n"); } }; Member gA("gA") int main() { Test t; return 0; }
我们先来分析下,首先我们定义两个类 Member 和 Test,在类 Test 中定义了两个类成员 Member。当程序流往下执行时,首先会创建全局类 Member gA,下来是类 Member mB,mA;注意这块它们的顺序和声明的顺序是相同的。最后才是创建类 Test 本身,后面析构的顺序和构造顺序是相反的,我们来看看编译结果
我们看到结果和我们分析的是一致的。那么对于栈对象和全局对象,便类似于入栈与出栈的顺序,最后构造的对象是最先被析构的。堆对象的析构发生在使用 delete 的时候,与 delete 的使用顺序相关!!
我们之前学习了 const 关键字,它可以修饰变量及函数。那么它是否可以修饰类的对象呢?如果可以的话,有何特性呢?const 关键字是可以修饰对象的,它修饰的对象为只读对象。只读对象的成员变量不允许被改变,只读对象是编译阶段的概念,运行时无效。我们下来来讲下 C++ 中的 const 成员函数:a> const 对象只能调用 const 成员函数;b> const 成员函数中只能调用 const 成员函数;c> const 成员函数中不能直接改写成员变量的值。const 成员函数的定义:Type ClassName::function(Type p) const。类中的函数声明与实际函数定义中都必须带 const 关键字。
下来我们还是以示例代码进行分析说明
#include <stdio.h> class Test { int mi; public: Test(int i); int getMI(); }; Test::Test(int i) { mi = i; } int Test::getMI() { return mi; } int main() { const Test t(1); printf("t.getMI() = %d\n", t.getMI()); return 0; }
我们将 main 函数中的 Test 对象定义为 const 的,然后输出 mi 的值,我们直接编译下,看看能否编译通过呢
我们看到直接报错了,它说这是个 const 类型的对象。我们前面讲过,const 类型的对象只能调用 const 成员函数,所以我们在 getMI 函数的声明和实现中都加上 const,再来编译看看
我们看到编译通过,并且完美运行。那么我们说过 const 成员函数是在其中不能改变值的,我们试试在 getMI 函数中赋值 mi 为 2,看看编译是否通过呢
它说这个成员在只读的函数中,不能被改变。那么成员函数和成员变量都隶属于具体对象的吗?从面向对象的角度,对象由属性(成员变量)和方法(成员函数)构成。从程序运行的角度,对象由数据和函数构成,数据可以位于栈,堆和全局数据区,但函数只能位于代码段。结论便是:a> 每一个对象拥有自己独立的属性(成员变量);b> 所有的对象共享类的方法(成员函数);c> 方法能够直接访问对象的属性;d> 方法中的隐藏参数 this 用于指代当前对象。
我们以代码为例进行说明
#include <stdio.h> class Test { int mi; public: int mj; Test(int i); Test(const Test& t); int getMI() const; void print(); }; Test::Test(int i) { mi = i; } Test::Test(const Test& t) { mi = t.mi; } int Test::getMI() const { 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()); printf("&t1 = %p\n", &t1); t1.print(); printf("t2.getMi() = %d\n", t2.getMI()); printf("&t2 = %p\n", &t2); t2.print(); printf("t3.getMi() = %d\n", t3.getMI()); printf("&t3 = %p\n", &t3); t3.print(); return 0; }
我们定义了三个对象,分别打印出它们的 mi 的值,它们本身的地址以及对应的 this 指针的地址,看看编译结果
那么它们为什么能知道自己定义的 mi 的值对应的是多少呢?就是因为每个对象有个隐藏的 this 指针,而且通过打印可以看出这个 this 指针和他们本身的地址是相同的。可能有的小伙伴对拷贝构造函数中的 mi 的赋值有疑问,可以这样直接进行赋值嘛?这个 t.mi 不是私有成员嘛?通过运行,编译器告诉了我们答案,是可以这样写的。因为成员函数是位于代码段的,因此成员函数是唯一的。也就是说,成员函数只有一套,它能够直接访问对应类的成员变量。所以这块是不冲突的。通过今天对特定问题的学习,总结如下:1、对象的析构顺序与构造顺序相反;2、const 关键字能够修饰对象,得到的是只读对象,只读对象只能调用 const 成员函数;3、所有对象共享类的成员函数;5、隐藏的 this 指针用于表示当前对象。
欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083。