1 概述
- 对象数组
- 对象成员
- 深拷贝 vs. 浅拷贝
- 对象指针 vs. 对象指针成员
- this 指针
- const + 对象 -> 常对象
- const + 函数 -> 常成员函数
- const + 对象成员 -> 常对象成员
2 对象数组
2.1 对象数组
说明:堆中实例化的数组需要手动销毁释放内存,在栈中实例化的数组,系统自动回收内存。
定义形式|内存管理
—|—
在栈中定义|系统管理
在堆中定义|手动管理
注意: 和其它类型的数组一样,栈中定义的对象数组和指针之间也有着密切的联系!
1 |
|
2.2 对象成员
实例化过程
- 先按照定义的顺序依次实例化对象成员;
- 最后在实例化对象成员所在的对象。
扩展: 当所在对象被销毁时,顺序和实例化过程相反。
1 | . |
makefile
1 | LIBS=-lm |
main.cpp1
2
3
4
5
6
7
8
9
10
11
12
using namespace std;
int main(void)
{
// Line *p = new Line(2, 3, 5, 8);
Line * p = new Line();
delete p;
p = NULL;
return 0;
}
Line.h
1 |
|
Line.cpp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using namespace std;
Line::Line(int x1, int y1, int x2, int y2):m_coorA(x1, y1),m_coorB(x2, y2)
{
cout << "Line(int x1, int y1, int x2, int y2)" << endl;
}
Line::~Line()
{
cout << "~Line()" << endl;
}
void Line::setA(int x, int y)
{
m_coorA.setX(x);
m_coorA.setY(y);
}
void Line::setB(int x, int y)
{
m_coorB.setX(x);
m_coorB.setY(y);
}
void Line::printInfo()
{
cout << "(" << m_coorA.getX() << ", " << m_coorA.getY()<<")" << endl;
cout << "(" << m_coorB.getX() << ", " << m_coorB.getY()<<")" << endl;
}
3 深拷贝与浅拷贝
3.1 深拷贝浅拷贝
说明:简单了解下深拷贝和浅拷贝
- 当数据成员中含有指针时,
浅拷贝
会使两个对象的成员指针指向相同的内存地址。
- 深拷贝不是简单的值拷贝,而是将指针成员指向的内存数据也进行拷贝。
技巧: 不是所有的对象都需哟啊深拷贝,应根据情况合理使用。如果对象的数据成员有引用类型(指针),则建议采用深拷贝!否则,会存在如下隐患
- 原对象和副本中任何一个,引用类型数据成员指向的内存数据发生变化,则共享该变化;
- 对同一内存空间的重复回收,会导致程序的崩溃。
浅拷贝 Demo
1 |
|
深拷贝 Demo
1 | . |
main.cpp
1 |
|
Array.h
1 | class Array |
Array.cpp
1 |
|
4 对象指针
4.1 对象指针
说明:分两大类
- 实际上就是通过通过
new
或malloc
等方式在堆中创建对象实例,返回的指针就是对象指针
(需要手动管理内存) - 在栈中创建对象,然后通过创建相应的指针指向这个栈中的对象(不需要手动管理内存)
在堆中创建
new
(推荐) : 会自动调用相应的构造函数
malloc
等:不会调用任何构造函数
简单示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using namespace std;
class Coordinate
{
public:
int m_iX;
int m_iY;
};
int main(void)
{
Coordinate *p = new Coordinate; // Coordinate *p = new Coordinate();
p->m_iX = 10; // (*p).m_iX = 10;
p->m_iY = 20; // (*p).m_iY = 20;
delete p;
p = NULL;
return 0;
}
在栈中创建
简单示例
1 |
|
对象指针 Demo
说明: 展示对象指针的定义
和使用
方法(以在堆中创建为例)
- 定义 Coordinate 类,数据成员 m_iX 和 m_iY
- 声明对象指针,并通过指针操控对象
- 计算两个点,横、纵坐标的和
1 | . |
main.c
1 |
|
Coordinate.h
1 | class Coordinate |
Coordinate.cpp
1 |
|
4.2 对象成员指针
说明: 前面有关于对象成员的讨论,这里的对象成员指针
,指的就是将对象指针
作为对象的成员。
- 需要在
构造函数
或构造函数列表
中创建对象实例,为对象成员指针
赋值。 - 需要在析构函数中释放对象成员指针指向的堆中的对象。
在初始化列表中初始化
1 | Line::Line():m_pCoorA(NULL), m_pCoorB(NULL) |
在构造函数中初始化
1 | Line::Line() |
在析构函数中释放资源
1 | Line::~Line() |
注意: 由于是将指针作为数据成员,所以使用 sizeof 计算数据成员大小时,是指针本身的大小,而不是指向的对象的大小!
对象成员指针Demo
说明: 演示对象成员指针的使用和要注意的事项。
1 | . |
main.cpp
1 |
|
Line.h
1 |
|
Line.cpp
1 |
|
Coordinate.h
1 | class Coordinate |
Coordinate.cpp
1 |
|
4.3 this 指针
说明: this
指针在对象中的方法中使用,指向对象自身。
- this指针无需用户定义,是编译器自动产生的。
- 同一个类的两个对象的this指针指向各自对象位置的内存。
- this 指针也是指针类型,所以在32位编译器下也占用4个基本的内存单元,即sizeof(this)的结果为4。
用途
- 显式指出变量是对象成员:当成员函数的参数或临时变量与数据成员同名时,可以使用this指针区分同名的数据成员。
- 编译器隐式使用 this:类中的成员函数存储在代码区,一个类的所有实例共享成员函数。内部通过隐式 this 指针保证访问正确的数据成员。
- 实现链式调用:成员方法可以返回
*this
或this
来实现链式链式调用(注意,如果返回的是*this
,除非返回显式说明返回引用,否则函数的返回值是副本)。
返回对象本身的引用
1 | Array& Array::printInfo() |
直接返回指针
1 | Array* Array::printInfo() |
this 指针 Demo
要点
- 链式调用
- 显式明确变量为对象成员(而不是参数)
1 | . |
main.cpp
1 |
|
Array.h
1 | class Array |
Array.cpp
1 |
|
5 const 再现江湖
5.1 const 对象成员和const 成员函数
说明: 当用 const 修饰 对象数据成员
或 成员函数
时,它们就变成了
- 常对象成员
- 常成员函数
常对象成员
简单示例
1 |
|
常成员函数
说明:特性如下
- 常成员函数中可以使用普通的数据成员,但是不能改变对象成员的值
- 普通成员函数可以被常对象或普通对象调用,单通过常对象才能够调用常成员函数
- 可以和同名同参的普通成员函数共存(互为重载,但不建议这样使用)
限制:常成员函数中的 this 指针是被 const 修饰的,因此无法通过该指针对原对象的数据成员进行修改。
1 |
|
5.2 const 对象成员和const 成员函数 Demo
1 | . |
main.cpp
1 |
|
Line.h
1 |
|
Line.cpp
1 |
|
Coordinate.h
1 | class Coordinate |
Coordinate.cpp
1 |
|
5.4 对象常指针和对象常引用
1 | . |
main.cpp
1 |
|
Coordinate.h
1 | class Coordinate |
Coordinate.cpp
1 |
|
5.3 单元巩固
1 |
|
6 学以致用
6.1 开篇案例
6.1.1 案例描述
走出迷宫。迷宫可以有单个出口,也可以有多个出口。
6.1.2 算法
无论怎样,都可以通过左右规则
或右手规则
扶着墙走出来。
6.1.3 程序结构
程序设计
迷宫类(MazeMap)
数据成员
- 墙壁字符
- 通路字符
- 迷宫数组(二维)
成员函数
- 构造函数
- 数据封装函数
- 迷宫回执函数
- 迷宫边界检查函数
人(MazePerson)
数据成员
- 人的字符
- 人的朝向
- 人当前位置
- 人前一个位置
- 人的速度
成员函数
- 构造函数
- 数据封装函数
- 向不同函数前进的函数
- 转弯函数
- 开始函数
动画控制
1 | void MazePerson::gotoxy(int x, int y) |
注意事项
- 枚举类型:方向
- 常量定义:宏定义或 const
程序实现
说明:为了模拟人(字符)在终端中的移动,使用到,windows.h
头文件,这个头文件是 windows 系统独有的,因此该程序只能在 windows 环境运行。开发运行环境如下
- win10 64
- VSCode
- c/c++ extension for VSCode
- MSYS2 + MinGW 64
注意: 代码文件是 UTF-8 编码,在 win10 下编译后乱码,因此用 g++ 编译时指定了编码的转换。
程序目录结构
1 | . |