注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。
测试环境:Ubuntu 10.10
GCC版本:9.2.0
一、关于析构的疑问
当程序中存在多个对象的时候,如何确定这些对象的析构顺序?
1)单个对象创建时构造函数的调用顺序——1、2、3
1、调用父类的构造过程(后续课程中讲解)
2、调用成员变量的构造函数(调用顺序与声明顺序相同)
3、调用类自身的构造函数
先父母——在客人——后自己
析构函数与对应构造函数的调用顺序相反。
2)多个对象析构时
- 析构顺序与构造顺序相反
构造顺序:A-B-C
析构顺序:C-B-A
实例分析
构造与析构顺序
24-1.cpp
#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; //成员变量mA
Member mB; //成员变量mB
public:
Test() : mB("mB"), mA("mA") //成员变量顺序与构造顺序相同,不要被迷惑
{
printf("Test()\n");
}
~Test()
{
printf("~Test()\n");
}
};
Member gA("gA"); //最先调用了全局对象的构造函数
int main()
{
Test t; // gA 第一步父类跳过
// mA
// mB
// Test() 第三步调用t自身构造函数
// ~Test()
// mB
// mA
// gA
return 0;
}
操作:
1) g++ 24-1.cpp -o 24-1.out编译正确,打印结果:
Member(const char* s): gA //全局对象Member gA("gA");
Member(const char* s): mA //局部变量t开始实例化:客人,后自己
Member(const char* s): mB
Test()
~Test()
~Member(): mB
~Member(): mA
~Member(): gA //全局对象析构函数
二、关于析构的答案
对于栈对象和全局对象,类似于入栈与出栈的顺序,最后构造的对象被最先析构!!
堆对象的析构发生在使用delete的时候,与delete的使用顺序相关!!
三、关于const对象的疑问
const关键字能否修饰类的对象?如果可以,有什么特性?
1)const关键字能够修饰对象
2)const修饰的对象为只读对象
3)只读对象的成员变量不允许被改变(变成只读)
4)只读对象是编译阶段的概念,运行时无效
5)C++中的const成员函数
- const对象只能调用const的成员函数
- const成员函数中只能调用const成员函数
- const成员函数中不能直接改写成员变量的值
6)const成员函数的定义:
Type ClassName::function(Type p)const
类中的函数声明与实际函数定义中都必须带const关键字。
编程实验
类的const函数初探
24-2.cpp
#include <stdio.h>
class Test
{
int mi;
public:
int mj;
Test(int i);
Test(const Test& t); //拷贝构造函数
int getMi() const;
};
Test::Test(int i)
{
mi = i;
}
Test::Test(const Test& t) //拷贝构造函数
{
}
int Test::getMi()const
{
return mi;
}
int main()
{
Test t(1);
return 0;
}
操作:
1) 修改代码,定义一个const Test t(1);对象,测试:
int main()
{
const Test t(1); //增加:修改成只读对象
return 0;
}
g++ 24-2.cpp -o 24-2.out编译正常,运行正常。
2) 继续修改代码,增加变量int mj;测试:
class Test
{
int mi;
public:
int mj; //增加:变量
Test(int i);
Test(const Test& t); //拷贝构造函数
int getMi();
};
int main()
{
const Test t(1);
t.mj = 1000; //增加:变量
return 0;
}
编译报错:
24-2.cpp: In function ‘int main()’:
24-2.cpp:32:7: error: assignment of member ‘Test::mj’ in read-only object
t.mj = 1000;
给只读成员变量赋值
分析:const对象对以后,对象的公有成员变量,也不能再次被修改数据。
3) 继续修改代码,打印t.mi内容:
int main()
{
const Test t(1);
printf("t.getMi() = %d\n", t.getMi()); //1
return 0;
}
编译报错:
24-2.cpp: In function ‘int main()’:
24-2.cpp:32:37: error: passing ‘const Test’ as ‘this’ argument of ‘int Test::getMi()’ discards qualifiers [-fpermissive]
printf("t.getMi() = %d\n", t.getMi());
错误:编译到const Test有错误,争议为int Test::getMi()丢失限定词(const)。
分析:
只读对象只能调用const成员函数。
解决办法:定义和声明时,给函数增加const关键字。
class Test
{
int mi;
public:
int mj;
Test(int i);
Test(const Test& t); //拷贝构造函数
int getMi()const;
};
int Test::getMi()const
{
return mi;
}
编译正常,打印结果:
t.getMi() = 1
4) 继续修改,在int Test::getMi() const中修改变量mi数值:
int Test::getMi()const
{
mi = 2;
return mi;
}
编译程序,报错:
24-2.cpp: In member function ‘int Test::getMi() const’:
24-2.cpp:25:5: error: assignment of member ‘Test::mi’ in read-only object
mi = 2;
错误:在只读对象中给成员Test::mi赋值
分析:
只读对象中给成员Test::mi赋值。
5) 继续修改,在拷贝构造函数调用int Test::getMi()去掉了const
class Test
{
int mi;
public:
int mj;
Test(int i);
Test(const Test& t); //拷贝构造函数
int getMi(); //去掉const
};
Test::Test(const Test& t)
{
mi = t.getMi(); //增加
}
int Test::getMi() //去掉const
{
return mi;
}
编译报错:
24-2.cpp: In copy constructor ‘Test::Test(const Test&)’:
24-2.cpp:20:15: error: passing ‘const Test’ as ‘this’ argument of ‘int Test::getMi()’ discards qualifiers [-fpermissive]
mi = t.getMi();
24-2.cpp: 再拷贝构造函数'Test::Test(const Test&)'里:
24-2.cpp:20:25: 错误:编译到'const Test'时,争议为'int Test::getMi()'丢失限定符。
分析:
const对象只能调用const类型成员函数。
四、关于类成员的疑问
成员函数和成员变量都是隶属于具体对象的吗?
1)从面向对象的角度
- 对象由属性(成员变量)和方法(成员函数)构成
2)从程序运行的角度
- 对象由数据和函数构成
* 数据可以位于栈,堆和全局数据区
* 函数只能位于代码段(代码段不能被改,编译阶段就固定)
3)结论
- 每一个对象都拥有自己独立的属性(成员变量) 数据私有,安全
- 所有的对象共享类的方法(成员函数)
- 方法能够直接访问对象的属性
- 方法中的隐藏参数this用于指代当前对象
this指针用于指向当前对象地址,*this为当前对象。
编程实验
成员函数的秘密
24-3.cpp
#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;
}
int Test::getMi()
{
return mi;
}
void Test::print()
{
printf("this = %p\n", this); //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("\n");
printf("t2.getMi() = %d\n", t2.getMi());
printf("&t2 = %p\n", &t2);
t2.print();
printf("\n");
printf("t3.getMi() = %d\n", t3.getMi());
printf("&t3 = %p\n", &t3);
t3.print();
return 0;
}
操作:
1) g++ 24-3.cpp -o 24-3.out编译正确,打印结果:
t1.getMi() = 1
&t1 = 0xbf85da68
this = 0xbf85da68
t2.getMi() = 2
&t2 = 0xbf85da60
this = 0xbf85da60
t3.getMi() = 3
&t3 = 0xbf85da58
this = 0xbf85da58
分析:
this指针指向了当前对象地址。
小结
1)对象的析构顺序与构造顺序相反
2)const关键字能够修饰对象,得到只读对象
3)只读对象能够调用const成员函数
4)所有对象共享类的成员函数
5)隐藏的this指针用于表示当前对象