1. 关于析构的疑问
(1)单个对象创建时构造函数的调用顺序
对象的构造函数调用不止一次,比如类的成员变量是另一个类的对象
①调用父类的构造函数(后续课程中讲解)
②调用成员变量的构造函数(调用顺序与声明顺序相同)
③调用类自身的构造函数
▲析构函数与对应构造函数的调用顺序相反:即类自身的析构→成员变量→父类析构
(2)多个对象的析构:析构函数顺序与构造函数顺序相反
【实例分析】构造函数与析构函数的调用顺序
1 #include<stdio.h> 构造函数与析构函数的调用顺序 2 3 class Member 4 { 5 const char* ms; //字符指针 6 public: 7 Member(const char* s) 8 { 9 printf("Member(const char* s) : %s\n",s); 10 ms = s; 11 } 12 ~Member() 13 { 14 printf("~Member() : %s\n", ms); 15 } 16 }; 17 18 class Test 19 { 20 Member mA; //定义的成员变量是另一个类Member的对象 21 Member mB; 22 public: 23 Test() : mB("mB"), mA("mA") 24 { 25 printf("Test() \n"); 26 } 27 ~Test() 28 { 29 printf("~Test() \n"); 30 } 31 }; 32 33 Member gA(" gA"); //全局对象 34 35 int main() 36 { 37 Test t; //局部对象 38 //首先构造全局对象的构造函数,Member(const char* s) : gA 39 //然后是main()函数,单个对象t构造 : mA mB 40 //最后是自身函数的调用:Test() 41 //析构顺序:~Test() mB mA gA 42 return 0; 43 }
//输出结果(先构造全局对象gA,进入main构造t对象)
//Member(const char* s): gA
//Member(const char* s): mA
//Member(const char* s): mB
//Test();
//~Test() //注意析构顺序与构造顺序是相反的!
//~Member(): mB
//~Member(): mA
//~Member(): gA
(3)不同存储区对象的析构
①栈对象和全局对象的析构:类似于入栈与出栈的顺序,最后构造的对象被最先析构
②堆对象的析构:发生在使用delete的时候,与delete的使用顺序相关
2. 关于const对象的疑问
const可以修饰变量,那能不能修饰对象??如果可以有什么特性??
类比思想-----类是用户自定义的类型,是struct进化而来的关键字,struct在c语言是变量能被const修饰,那么对应的类某种角度来说也是变量,是不是也能被const修饰,
(1)const关键字能够修饰对象
(2)const修饰的对象是只读对象
(3)只读对象的成员变量不允许被改变。 (对象由一系列成员变量构成,对象只读,那么成员变量不允许改变)
(4)只读对象是编译阶段的概念,在运行时无效。
3. const成员函数--------不能直接改写成员变量的值。
(2)const对象只能调用const成员函数。如拷贝构造函数里只能调用const成员函数
(3)const成员函数只能调用const成员函数。
(4)const成员函数的定义:
Type ClassName::func(Type p) const{}; //类中函数声明与实际函数定义都必须带const关键字
//普通成员函数之后,函数体之前,加const关键字
#include <stdio.h> class Test { private: int mi; public: Test(int i); Test(const Test& t);//t是const对象(的引用),函数内只能调用const成员函数 int getMI() const; //const成员函数 void print() //非const成员函数 { printf("mi = %d\n", mi); } void show() const { //print(); //错误,const成员函数不能调用非const的成员函数 printf("mi = %d\n", mi); } }; Test::Test(int i) { mi = i; } Test::Test(const Test& t) { } int Test::getMI() const { //mi = 2; //const成员函数不能改变成员变量的值! return mi; } int main() { const Test t(1); //const对象 t.getMI(); //正确,const对象只能调用const成员函数 t.show(); //正确,const对象只能调用const成员函数 //t.print(); //错误,const对象不能调用非const成员函数 return 0; }
4. 关于类成员的疑问
成员函数和成员对象都的属于类的对象吗??
(1)从面向对象的角度------------------------对象由属性(成员变量)和方法(成员函数)构成
(2)从程序运行的角度-------------------------对象由数据和函数构成,其中数据位于栈、堆或全局数据区中,而函数只能位于代码段
代码段是只读的,程序运行时不改变,待程序编译成最后的可执行程序时,代码的确定不可修改,
对象可以动态的创建,动态的删除,对于数据也是,栈堆数据动态创建删除,对于代码不可能---------所以不可能成员函数每个都有一套,只有所有的对象共享一套成员函数,因为代码的不能动态添加删除------------那么对于那么多的对象,成员函数怎么分辨是哪个对象调用???????方法中隐藏参数this指针,用于指代当前对象
(3)结论:
①每一个对象拥有自己独立的属性(成员变量)
②所有的对象共享类的方法(成员函数)
③方法能够直接访问对象的属性
④方法中隐藏参数this指针,用于指代当前对象-------当前调用成员函数的对象
成员函数与普通函数区别--------有隐藏的参数-this指针,指向当前调用函数对象的地址
1 #include <stdio.h> 2 3 class Test 4 { 5 private: 6 int mi; 7 8 public: 9 10 int mj; 11 12 Test(int i); 13 14 Test(const Test& t); 15 16 int getMI(); //成员函数------只有一套,所有类调用 17 18 void print(); 19 20 }; 21 22 23 Test::Test(int i) 24 { 25 mi = i; 26 27 printf("Test(int i)\n"); 28 } 29 30 31 32 Test::Test(const Test& t) 33 { 34 //mi = t.getMI();//错误,t是const对象,不能调用非const成员函数 35 36 mi = t.mi; //拷贝构造函数------一种特殊的成员函数------直接访问对应类对象的成员变量 37 //这里t.mi为private属性,但编译仍然能通过。是因为编译器开了个后门, 38 39 //允许类的成员函数直接访问由该类创建的任何对象的局部变量。 40 41 printf("Test(const Test& t)\n"); 42 } 43 44 45 int Test::getMI() 46 { 47 return mi; 48 } 49 50 51 void Test::print() 52 { 53 printf("&mi = %p\n", &mi); 54 55 printf("this = %p\n", this); // this指针,值为调用print()对应对象的地址-------this指向当前对象 56 } 57 58 59 60 int main() 61 { 62 Test t1(1); 63 64 Test t2 = 2; 65 66 Test t3 = Test(3);//手动调用构造函数,由Test(3)产生的临时对象会被编译器优化掉 67 68 //Test(int i); 69 70 71 72 //t1、t2、t3的内存地址各不相同。各自的mi地址也不同。但print函数地址是一样的 73 74 printf("t1.getMI() = %d\n", t1.getMI()); //1 75 76 printf("&t1 = %p\n", &t1); //0xbf85da68 77 78 t1.print(); //0xbf85da68 79 80 printf("&t1.print() = %p\n", &t1.print); 81 82 83 84 85 86 87 printf("t2.getMI() = %d\n", t2.getMI()); //2 88 89 t2.print(); //0xbf85da68 90 91 printf("&t2 = %p\n", &t2);//0xbf85da68 92 93 printf("&t2.print() = %p\n", &t2.print); 94 95 96 97 98 99 printf("t3.getMI() = %d\n", t3.getMI()); //3 100 101 t3.print(); //0xbf85da68 102 103 printf("&t3 = %p\n", &t3); //0xbf85da68 104 105 printf("&t3.print() = %p\n", &t3.print); 106 107 108 return 0; 109 110 }
5. 小结
(1)对象的析构顺序与构造顺序相反
(2)const关键字能够修饰对象,得到只读对象(只读对象只能调用const成员函数)
(3)只读对象只能调用const成员函数(不能修改成员变量的成员函数)
(4)所有对象共享类的成员函数
(5)隐藏的this指针用于表示当前对象