c++远征03封装篇(下)

C++远征之封装篇(下)-慕课网

1 概述

  • 对象数组
  • 对象成员
  • 深拷贝 vs. 浅拷贝
  • 对象指针 vs. 对象指针成员
  • this 指针
  • const + 对象 -> 常对象
  • const + 函数 -> 常成员函数
  • const + 对象成员 -> 常对象成员

2 对象数组

2.1 对象数组

说明:堆中实例化的数组需要手动销毁释放内存,在栈中实例化的数组,系统自动回收内存。

定义形式|内存管理
—|—
在栈中定义|系统管理
在堆中定义|手动管理

注意: 和其它类型的数组一样,栈中定义的对象数组和指针之间也有着密切的联系!

1
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
31
32
33
34
35
36
37
38
39
40
#include <iostream>
using namespace std;
class Coordinate
{
public:
int m_iX;
int m_iY;
};

int main(void)
{

// 在栈中定义
Coordinate coord[3];
coord[0].m_iX = 90;
coord[1].m_iX = 10;
coord[2].m_iY = 17;
cout << "(*coord).m_iX" << (*coord).m_iX << endl; // 数组名可以当作指针处理

// 在堆中定义
Coordinate *p = new Coordinate[3];
p[0].m_iY = 7;
p->m_iY = 9; // p 实际指向数组的第一个元素 p[0]
Coordinate *q = p; // 为了防止对 p 的操作导致不指向数组第一个元素,声明一个副本
q++;
q->m_iY = 17; // p[1]

for(int i = 0; i < 3; i++)
{
cout << "coord:(" << coord[i].m_iX << ", " << coord[i].m_iY << ")"<< endl;
}
for (int j = 0; j < 3; j++)
{
cout << "p:(" << p[j].m_iX << ", " << p[j].m_iY << ")" << endl;
}

delete []p;
p = q = NULL;

return 0;
}

2.2 对象成员

实例化过程

  1. 先按照定义的顺序依次实例化对象成员;
  2. 最后在实例化对象成员所在的对象。

扩展: 当所在对象被销毁时,顺序和实例化过程相反。

1
2
3
4
5
6
7
8
 .
├── Coordinate.cpp # 坐标类实现
├── Coordinate.h # 坐标类头文件
├── Line.cpp # 线段类实现
├── Line.h # 线段类头文件
├── main.cpp # 入口
├── makefile
└── test

makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
LIBS=-lm
OBJS=main.o Coordinate.o Line.o
CFLAGS=-Wall
SO=test
main: ${OBJS}
g++ -o ${SO} ${OBJS}
main.o: main.cpp Line.cpp
g++ -c main.cpp ${CFLAGS}
Line.o:Line.cpp Line.h
g++ -c Line.cpp
Coordinate.o:Coordinate.cpp Coordinate.h
g++ -c Coordinate.cpp
clean:
rm -f ${OBJS} ${SO}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include "Line.h"
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
2
3
4
5
6
7
8
9
10
11
12
13
#include "Coordinate.h"
class Line
{
public:
Line(int x1=0, int y1=0, int x2=0, int y2=0);
~Line();
void setA(int x, int y);
void setB(int x, int y);
void printInfo();
private:
Coordinate m_coorA;
Coordinate m_coorB;
};

Line.cpp

1
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
#include <iostream>
#include "Line.h"
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
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
31
32
33
#include <iostream>
using namespace std;

class Array
{
public:
// 构造函数
Array()
{
cout << "Array()" << endl;
m_iCount = 5;
m_pArr = new int[m_iCount];

}
// 拷贝构造函数
Array(const Array& array)
{
cout << "Array(const Array& array)" << endl;
m_iCount = array.m_iCount;
m_pArr = array.m_pArr; // 副本和原对象,该数据成员指向同一地址,有可能出现潜在问题
}
private:
int m_iCount;
int *m_pArr;
};

int main(void)
{

Array arr1;
Array arr2 = arr1;

return 0;
}

深拷贝 Demo

1
2
3
4
5
.
├── Array.cpp
├── Array.h
├── main.cpp
└── makefile

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include "Array.h"
using namespace std;

int main(void)
{

Array arr1(5);
Array arr2(arr1);

cout << "arr1->" << arr1.printArr() << endl;
cout << "arr2->" << arr2.printArr() << endl;
return 0;
}

// Array()
// Array(const Array& array)
// arr1->m_pArr:0x7f9b7a500000
// -246491552
// arr2->m_pArr:0x7f9b7a402830
// -246491552
// ~Array()
// ~Array()

Array.h

1
2
3
4
5
6
7
8
9
10
11
12
13
class Array
{
public:
Array(int count = 0);
Array(const Array& array);
~Array();
void setCount(int count);
int getCount();
void printArr();
private:
int m_iCount;
int *m_pArr;
};

Array.cpp

1
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
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include "Array.h"
using namespace std;

// 构造函数
Array::Array(int count):m_iCount(count)
{
cout << "Array()" << endl;
m_pArr = new int[m_iCount];
}
// 析构函数
Array::~Array()
{
delete []m_pArr;
m_pArr = NULL;
cout << "~Array()" << endl;
}
// 拷贝构造函数
Array::Array(const Array& array)
{
cout << "Array(const Array& array)" << endl;
m_iCount = array.m_iCount;
m_pArr = new int[m_iCount];
for(int i = 0; i < m_iCount; i++)
{
m_pArr[i] = array.m_pArr[i];
}
}
int Array::printArr()
{
// 打印数组在内存中的地址
cout << "m_pArr:" << m_pArr << endl;
}
void Array::setCount(int count)
{
m_iCount = count;
}

int Array::getCount()
{
return m_iCount;
}

4 对象指针

4.1 对象指针

说明:分两大类

  • 实际上就是通过通过 newmalloc 等方式在堆中创建对象实例,返回的指针就是对象指针(需要手动管理内存)
  • 在栈中创建对象,然后通过创建相应的指针指向这个栈中的对象(不需要手动管理内存)

在堆中创建

  • new(推荐) : 会自动调用相应的构造函数
  • malloc 等:不会调用任何构造函数

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

class Coordinate
{
public:
int m_iX;
int m_iY;
};

int main(void)
{

Coordinate coor;
Coordinate *p = &coor;
p->m_iX = 10; // (*p).m_iX = 10;
p->m_iY = 20; // (*p).m_iY = 20;

return 0;
}

对象指针 Demo

说明: 展示对象指针的定义使用方法(以在堆中创建为例)

  • 定义 Coordinate 类,数据成员 m_iX 和 m_iY
  • 声明对象指针,并通过指针操控对象
  • 计算两个点,横、纵坐标的和
1
2
3
4
5
.
├── Coordinate.cpp
├── Coordinate.h
├── main.cpp # 要点在这里
└── makefile

main.c

1
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
#include <iostream>
#include "Coordinate.h"
using namespace std;

int main(void)
{

// 创建对象指针
Coordinate *p1 = new Coordinate;
Coordinate *p2 = new Coordinate();

// 对象数据成员赋值
p1->m_iX = 10;
p2->m_iY = 20;
(*p1).m_iX = 30;
(*p2).m_iY = 40;

// 打印计算结果
cout << p1-> m_iX + (*p2).m_iX << endl;
cout << p1-> m_iY + (*p2).m_iY << endl;

// 回收堆中的内存
delete p1;
p1 = NULL;

delete p2;
p2 = NULL;

return 0;
}

Coordinate.h

1
2
3
4
5
6
7
8
class Coordinate
{
public:
Coordinate(int x=0, int y=0);
~Coordinate();
int m_iX;
int m_iY;
};

Coordinate.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include "Coordinate.h"
using namespace std;

Coordinate::Coordinate(int x, int y):m_iX(x),m_iY(y)
{
cout << "Coordinate(int x, int y):m_iX(x),m_iY(y)" << endl;
}

Coordinate::~Coordinate()
{
cout << "~Coordinate()" << endl;
}

4.2 对象成员指针

说明: 前面有关于对象成员的讨论,这里的对象成员指针,指的就是将对象指针作为对象的成员。

  • 需要在构造函数构造函数列表中创建对象实例,为对象成员指针赋值。
  • 需要在析构函数中释放对象成员指针指向的堆中的对象。

在初始化列表中初始化

1
2
3
Line::Line():m_pCoorA(NULL), m_pCoorB(NULL)
{
}

在构造函数中初始化

1
2
3
4
5
Line::Line()
{
m_pCoorA = new Coordinate(0, 0);
m_pCoorB = new Coordinate(0, 0);
}

在析构函数中释放资源

1
2
3
4
5
Line::~Line()
{
delete m_pCoorA;
delete m_pCoorB;
}

注意: 由于是将指针作为数据成员,所以使用 sizeof 计算数据成员大小时,是指针本身的大小,而不是指向的对象的大小!

对象成员指针Demo

说明: 演示对象成员指针的使用和要注意的事项。

1
2
3
4
5
6
7
.
├── Coordinate.cpp
├── Coordinate.h # 坐标类
├── Line.cpp #要点
├── Line.h # 线段类
├── main.cpp
└── makefile

main.cpp

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include "Line.h"
using namespace std;

int main(void)
{

Line * p = new Line(1, 2, 3, 4);
delete p;
p = NULL;
return 0;
}

Line.h

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "Coordinate.h"
class Line
{
public:
Line(int x1=0, int y1=0, int x2=0, int y2=0);
~Line();
void setA(int x, int y);
void setB(int x, int y);
void printInfo();
private:
Coordinate *m_pCoorA;
Coordinate *m_pCoorB;
};

Line.cpp

1
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
31
32
33
34
35
36
37
38
39
#include <iostream>
#include "Line.h"
using namespace std;
// 在构造函数中初始化对象指针
Line::Line(int x1, int y1, int x2, int y2)
{
m_pCoorA = new Coordinate(x1, y1);
m_pCoorB = new Coordinate(x2, y2);
cout << "Line(int x1, int y1, int x2, int y2)" << endl;
}

// 在析构函数中回收对象指针指向的内存资源
Line::~Line()
{
delete m_pCoorA;
delete m_pCoorB;
m_pCoorA = NULL;
m_pCoorB = NULL;

cout << "~Line()" << endl;
}

void Line::setA(int x, int y)
{
m_pCoorA->setX(x);
m_pCoorA->setY(y);
}

void Line::setB(int x, int y)
{
m_pCoorB->setX(x);
m_pCoorB->setY(y);
}

void Line::printInfo()
{
cout << "(" << m_pCoorA->getX() << ", " << m_pCoorA->getY()<<")" << endl;
cout << "(" << m_pCoorB->getX() << ", " << m_pCoorB->getY()<<")" << endl;
}

Coordinate.h

1
2
3
4
5
6
7
8
9
10
11
12
13
class Coordinate
{
public:
Coordinate(int x=0, int y=0);
~Coordinate();
void setX(int x);
int getX();
void setY(int y);
int getY();
private:
int m_iX;
int m_iY;
};

Coordinate.cpp

1
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
31
32
33
#include <iostream>
#include "Coordinate.h"
using namespace std;

Coordinate::Coordinate(int x, int y):m_iX(x),m_iY(y)
{
cout << "Coordinate(int x, int y):m_iX(x),m_iY(y)" << endl;
}

Coordinate::~Coordinate()
{
cout << "~Coordinate()" << endl;
}

void Coordinate::setX(int x)
{
m_iX = x;
}

int Coordinate::getX()
{
return m_iX;
}

void Coordinate::setY(int y)
{
m_iY = y;
}

int Coordinate::getY()
{
return m_iY;
}

4.3 this 指针

说明: this 指针在对象中的方法中使用,指向对象自身。

  • this指针无需用户定义,是编译器自动产生的。
  • 同一个类的两个对象的this指针指向各自对象位置的内存。
  • this 指针也是指针类型,所以在32位编译器下也占用4个基本的内存单元,即sizeof(this)的结果为4。

用途

  • 显式指出变量是对象成员:当成员函数的参数或临时变量与数据成员同名时,可以使用this指针区分同名的数据成员。
  • 编译器隐式使用 this:类中的成员函数存储在代码区,一个类的所有实例共享成员函数。内部通过隐式 this 指针保证访问正确的数据成员。
  • 实现链式调用:成员方法可以返回 *thisthis来实现链式链式调用(注意,如果返回的是*this,除非返回显式说明返回引用,否则函数的返回值是副本)。

返回对象本身的引用

1
2
3
4
5
Array& Array::printInfo()
{
cout << "len = " << len << endl;
return *this;
}

直接返回指针

1
2
3
4
5
Array* Array::printInfo()
{
cout << "len = " << len << endl;
return this;
}

this 指针 Demo

要点

  • 链式调用
  • 显式明确变量为对象成员(而不是参数)
1
2
3
4
5
.
├── Array.cpp
├── Array.h
├── main.cpp
└── makefile

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include "Array.h"

using namespace std;

int main(void)
{

Array arr1(1);
// 链式调用
arr1.printInfo().setLen(2).printInfo();
return 0;
}

Array.h

1
2
3
4
5
6
7
8
9
10
11
class Array
{
public:
Array(int len);
~Array();
int getLen();
Array& setLen(int len);
Array& printInfo();
private:
int len;
};

Array.cpp

1
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
#include <iostream>
#include "Array.h"
using namespace std;

Array::Array(int len)
{
this->len = len;
}
Array::~Array()
{

}
int Array::getLen()
{
return len;
}
Array& Array::setLen(int len)
{
this->len = len;
return *this;
}

Array& Array::printInfo()
{
cout << "len = " << len << endl;
return *this;
}

5 const 再现江湖

5.1 const 对象成员和const 成员函数

说明: 当用 const 修饰 对象数据成员成员函数时,它们就变成了

  • 常对象成员
  • 常成员函数

常对象成员

简单示例

1
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
31
32
33
34
35
36
37
#include <iostream>
using namespace std;

class Coordinate
{
public:
Coordinate(int x, int y):m_iX(x), m_iY(y)
{
}
private:
const int m_iX;
const int m_iY;
};

class Line
{
public:
Line(int x1, int y1, int x2, int y2);
private:
// 常对象成员
const Coordinate m_coorA;
const Coordinate m_coorB;
};

// 常对象成员和普通 const 数据成员一样,只能在初始化列表中赋值
Line::Line(int x1, int y1, int x2, int y2):m_coorA(x1, y1), m_coorB(x2, y2)
{
}

int main(void)
{

Line *p = new Line(2, 1, 6, 4);

delete p;
p = NULL;
return 0;
}

常成员函数

说明:特性如下

  • 常成员函数中可以使用普通的数据成员,但是不能改变对象成员的值
  • 普通成员函数可以被常对象或普通对象调用,单通过常对象才能够调用常成员函数
  • 可以和同名同参的普通成员函数共存(互为重载,但不建议这样使用)

限制:常成员函数中的 this 指针是被 const 修饰的,因此无法通过该指针对原对象的数据成员进行修改。

1
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
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
using namespace std;

class Coordinate
{
public:
Coordinate(int x, int y);
// 常成员函数(只能通过常对象调用)
void changeX() const;
// 和上面的同名同参的常成员函数互为重载
void changeX();
private:
int m_iX;
int m_iY;
};

Coordinate::Coordinate(int x, int y)
{
m_iX = x;
m_iY = y;
}

void Coordinate::changeX() const
{
// m_iX = 10;(会报错)
cout << "changeX()" << endl;
}
void Coordinate::changeX()
{
m_iX = 20;
}

int main(void)
{

// 常对象
const Coordinate coor(3, 5);
// 通过常对象才能调用常成员函数
coor.changeX();

return 0;
}

5.2 const 对象成员和const 成员函数 Demo

1
2
3
4
5
6
7
.
├── Coordinate.cpp
├── Coordinate.h # getX 和 getY 为常对象和普通对象分别进行了定义
├── Line.cpp
├── Line.h
├── main.cpp
└── makefile

main.cpp

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include "Line.h"
using namespace std;

int main(void)
{

Line l(1, 2, 3, 4);
l.printInfo();
return 0;
}

Line.h

1
2
3
4
5
6
7
8
9
10
11
12
#include "Coordinate.h"
class Line
{
public:
Line(int x1=0, int y1=0, int x2=0, int y2=0);
~Line();
void printInfo();
void printInfo() const;
private:
const Coordinate m_coorA;
const Coordinate m_coorB;
};

Line.cpp

1
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
#include <iostream>
#include "Line.h"
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::printInfo()
{
cout << "(" << m_coorA.getX() << ", " << m_coorA.getY()<<")" << endl;
cout << "(" << m_coorB.getX() << ", " << m_coorB.getY()<<")" << endl;
}
void Line::printInfo() const
{
cout << "Line::printInfo() const" << endl;
cout << "(" << m_coorA.getX() << ", " << m_coorA.getY()<<")" << endl;
cout << "(" << m_coorB.getX() << ", " << m_coorB.getY()<<")" << endl;
}

Coordinate.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Coordinate
{
public:
Coordinate(int x=0, int y=0);
~Coordinate();
void setX(int x);
int getX() const; // for 常对象
int getX();
void setY(int y);
int getY() const; // for 常对象
int getY();
private:
int m_iX;
int m_iY;
};

Coordinate.cpp

1
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
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include "Coordinate.h"
using namespace std;

// 在初始化列表中对常对象成员进行初始化
Coordinate::Coordinate(int x, int y):m_iX(x),m_iY(y)
{
cout << "Coordinate(int x, int y):m_iX(x),m_iY(y)" << endl;
}

Coordinate::~Coordinate()
{
cout << "~Coordinate()" << endl;
}

void Coordinate::setX(int x)
{
m_iX = x;
}

int Coordinate::getX() const
{
return m_iX;
}
int Coordinate::getX()
{
return m_iX;
}
void Coordinate::setY(int y)
{
m_iY = y;
}
int Coordinate::getY() const
{
return m_iY;
}
int Coordinate::getY()
{
return m_iY;
}

5.4 对象常指针和对象常引用

1
2
3
4
5
.
├── Coordinate.cpp
├── Coordinate.h
├── main.cpp
└── makefile

main.cpp

1
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
31
32
33
34
35
36
37
#include <iostream>
#include "Coordinate.h"
using namespace std;

int main(void)
{

// 在栈中创建对象实例
Coordinate coor1(3, 5);
//1---------------
// 对象引用
Coordinate &coor2 = coor1;
coor2.printInfo(); // (3, 5)

// 对象指针
Coordinate *pCoor1 = &coor1;
pCoor1->printInfo(); // (3, 5)

//2---------------
// 对象常引用(只能调用常成员函数)
const Coordinate &coor3 = coor1;
coor3.printInfo(); // (3, 5)
// coor3.getX(); (报错,getX 的 this 需要有读写权限)

// 常对象指针(智能调用常成员函数)
const Coordinate *pCoor2 = &coor1;
pCoor2->printInfo(); // (3, 5)
// pCoor2.getX(); (报错,getX 的 this 需要有读写权限)

//3---------------
Coordinate coor4(3, 9);
// 对象常指针(对指向的对象仍然有读写权限,但本身地址不可修改)
Coordinate * const pCoor3 = &coor1;
pCoor3->getY();
// pCoor3 = &coor4;(报错,指针本身只读)
pCoor3->printInfo();
return 0;
}

Coordinate.h

1
2
3
4
5
6
7
8
9
10
11
12
class Coordinate
{
public:
Coordinate(int x=0, int y=0);
~Coordinate();
int getX();
int getY();
void printInfo() const;
private:
int m_iX;
int m_iY;
};

Coordinate.cpp

1
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
#include <iostream>
#include "Coordinate.h"
using namespace std;

Coordinate::Coordinate(int x, int y):m_iX(x),m_iY(y)
{
cout << "Coordinate(int x, int y):m_iX(x),m_iY(y)" << endl;
}

Coordinate::~Coordinate()
{
cout << "~Coordinate()" << endl;
}

int Coordinate::getX()
{
return m_iX;
}

int Coordinate::getY()
{
return m_iY;
}
void Coordinate::printInfo() const
{
cout << "(" << m_iX << "," << m_iY << ")" << endl;
}

5.3 单元巩固

1
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
31
32
33
34
35
36
37
38
#include <iostream>
using namespace std;
class Coordinate
{

public:
Coordinate(int x, int y)
{
// 设置X,Y的坐标
m_iX = x;
m_iY = y;
}
// 实现常成员函数
void printInfo() const
{

cout << "(" << m_iX << "," << m_iY <<")";
}
public:
int m_iX;
int m_iY;
};


int main(void)
{

const Coordinate coor(3, 5);

// 创建常指针p
const Coordinate *p = &coor;
// 创建常引用c
const Coordinate &c = coor;

coor.printInfo();
p->printInfo();
c.printInfo();

return 0;
}

6 学以致用

6.1 开篇案例

6.1.1 案例描述

走出迷宫。迷宫可以有单个出口,也可以有多个出口。

6.1.2 算法

无论怎样,都可以通过左右规则右手规则扶着墙走出来。

6.1.3 程序结构

程序设计


迷宫类(MazeMap)

数据成员

  • 墙壁字符
  • 通路字符
  • 迷宫数组(二维)

成员函数

  • 构造函数
  • 数据封装函数
  • 迷宫回执函数
  • 迷宫边界检查函数

人(MazePerson)
数据成员

  • 人的字符
  • 人的朝向
  • 人当前位置
  • 人前一个位置
  • 人的速度

成员函数

  • 构造函数
  • 数据封装函数
  • 向不同函数前进的函数
  • 转弯函数
  • 开始函数

动画控制

1
2
3
4
5
6
7
8
void MazePerson::gotoxy(int x, int y)
{
COORD cd;
cd.X = x;
cd.Y = y;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(handle, cd);
}

注意事项

  • 枚举类型:方向
  • 常量定义:宏定义或 const

程序实现

C-PLUS-PLUS_STUDY/JK_C-PLUS-PLUS/l03_class_encapsulation_two/0601 at master · laputa-er/C-PLUS-PLUS_STUDY · GitHub

说明:为了模拟人(字符)在终端中的移动,使用到,windows.h 头文件,这个头文件是 windows 系统独有的,因此该程序只能在 windows 环境运行。开发运行环境如下

  • win10 64
  • VSCode
  • c/c++ extension for VSCode
  • MSYS2 + MinGW 64

注意: 代码文件是 UTF-8 编码,在 win10 下编译后乱码,因此用 g++ 编译时指定了编码的转换。

程序目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── MazeMap.cpp
├── MazeMap.h # 地图类
├── MazePerson.cpp
├── MazePerson.h # 迷宫行者类
├── MyMazeMap.cpp
├── MyMazeMap.h # 地图类的封装
├── MyMazePerson.cpp
├── MyMazePerson.h # 迷宫行者类的封装
├── Public.h # 全局常量
├── makefile # 使用 make 来定义
├── maze.cpp # 程序入口
└── test.exe # 编译打包好的命令行程序