C++远征之封装篇(下)
c++封装概述
下半篇依然围绕类 & 对象进行展开
将原本学过的简单元素融合成复杂的新知识点。
- 对象 + 数据成员 = 对象成员(对象作为数据成员)
- 对象 + 数组 = 对象数组(一个数组中的每个元素都是对象)
- 深拷贝 & 浅拷贝 (对象之间彼此赋值,彼此拷贝)
- 对象指针(操作对象) & 对象指针成员
- this指针
- const + 对象 -> 常对象
- const + 函数 -> 常成员函数
- const + 对象成员 -> 常对象成员
设计了一个精巧的案例,走迷宫。
c++ 对象数组
如何实例化一个对象?实例化对象对于程序是很重要的,只有实例化了对象才能访问成员函数和数据成员。
某些情况下我们往往需要实例化一组对象。比如:我们想实例化出一个50人的班的学生。
![](https://img.haomeiwen.com/i1779926/2fcf3c03840ebcb4.png)
栈区实例化对象数组,会分配相应的内存,系统会自动管理。每一个内存中保存着x,y
堆区分配相应内存,p 与 p[0] 是等价的。
C++对象数组实践代码
![](https://img.haomeiwen.com/i1779926/45eab56a0210d803.png)
2-2-InstanceArray
Coordinate.h
class Coordinate
{
public:
Coordinate();
~Coordinate();
public:
int m_iX;
int m_iY;
};
coordinate.cpp
#include <iostream>
#include "Coordinate.h"
using namespace std;
Coordinate::Coordinate()
{
cout << "Coordinate" << endl;
}
Coordinate::~Coordinate()
{
cout << "~Coordinate" << endl;
}
main.cpp
#include <stdlib.h>
#include <iostream>
#include <string>
#include "Coordinate.h"
using namespace std;
int main(void)
{
Coordinate coor[3];
coor[0].m_iX = 3;
coor[0].m_iY = 5;
Coordinate *p = new Coordinate[3];
p->m_iX = 7;
p[0].m_iY = 9;
p++;
p->m_iX = 11;
p[0].m_iY = 13;//此时因为上面p++。p已经指向第二个地址了
p[1].m_iX = 15;//此时p指向第三个元素
p++;
p->m_iY = 17;//此时p指向第三个元素
for (int i = 0; i < 3; i++)
{
cout << coor[i].m_iX << " coor x&y " << coor[i].m_iY << endl;
}
for (int j = 0; j < 3; j++)
{
//cout << "p_x" << p[j].m_iX << endl;
//cout << "p_y" << p[j].m_iY << endl;
cout << "p_x" << p->m_iX << endl;
cout << "p_y" << p->m_iY << endl;
p--;
}
p++;//因为上面p=-1时才退出了循环。因此释放时已经不是原来那段内存了。
delete []p;
p = NULL;
system("pause");
return 0;
}
![](https://img.haomeiwen.com/i1779926/dd04fc75eb61435c.png)
2-6-ObjectMember
Coordinate.h :
class Coordinate
{
public:
Coordinate();
~Coordinate();
public:
int getX();
void setX(int x);
int getY();
void setY(int y);
private:
int m_iX;
int m_iY;
};
Coordinate.cpp :
#include <iostream>
#include "Coordinate.h"
using namespace std;
Coordinate::Coordinate()
{
cout << "Coordinate" << endl;
}
Coordinate::~Coordinate()
{
cout << "~Coordinate" << endl;
}
int Coordinate::getX() {
return m_iX;
}
void Coordinate::setX(int x) {
m_iX = x;
}
int Coordinate::getY(){
return m_iY;
}
void Coordinate::setY(int y) {
m_iY = y;
}
Line.h :
#include "Coordinate.h"
class Line {
public:
Line();
~Line();
void setCoorA(int x, int y);
void setCoorB(int x, int y);
void printInfo();
private:
Coordinate m_coorA;
Coordinate m_coorB;
};
Line.cpp :
#include <iostream>
#include "Line.h"
using namespace std;
Line::Line() {
cout << "Line()" << endl;
}
Line::~Line() {
cout << "~Line()" << endl;
}
void Line::setCoorA(int x, int y) {
m_coorA.setX(x);
m_coorA.setX(y);
}
void Line::setCoorB(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;
}
main.cpp
#include <stdlib.h>
#include <iostream>
#include <string>
#include "Line.h"
using namespace std;
int main(void)
{
Line *p = new Line();
delete p;
p = NULL;
system("pause");
return 0;
}
![](https://img.haomeiwen.com/i1779926/c8f45ea62713b564.png)
说明一下某些场合深拷贝的实用性。
3-2-ShallowCopy
浅拷贝代码:
Array.h
class Array
{
public:
Array();
Array(const Array& arr);
~ Array();
int getCount();
void setCount(int val);
private:
int m_iCount;
};
Array.cpp:
#include <iostream>
#include "Array.h"
using namespace std;
Array::Array()
{
cout << "Array()" << endl;
}
Array::Array(const Array& arr) {
m_iCount = arr.m_iCount;
cout << "Array(&)" << endl;
}
Array::~Array() {
cout << "~Array()" << endl;
}
void Array::setCount(int c) {
m_iCount = c;
}
int Array::getCount() {
return m_iCount;
}
main.cpp:
#include <iostream>
#include <stdlib.h>
#include "Array.h"
using namespace std;
int main(void)
{
Array arr1;
arr1.setCount(5);
Array arr2 = arr1;
cout << "arr2,count:" <<arr2.getCount() << endl;
system("pause");
return 0;
}
![](https://img.haomeiwen.com/i1779926/2835a417e8d13cfc.png)
p指向m_iX,访问方式为
p->m_iX
;
*p
变成一个对象,采用.
访问其中元素。
具体示例代码:
int main(void)
{
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;
}
- new 会自动调用对象的构造函数;
- C语言中的 malloc 则不会调用相关对象的构造函数,只是分配内存。
C++对象指针实践:
![](https://img.haomeiwen.com/i1779926/27b8e8cebb5227d9.png)
4-2-ObjectPointer
Coordinate.h
class Coordinate
{
public:
Coordinate();
~Coordinate();
int m_iX;
int m_iY;
};
Coordinate.cpp
#include <iostream>
#include "Coordinate.h"
using namespace std;
Coordinate::Coordinate()
{
cout << "Coordinate()" << endl;
}
Coordinate::~Coordinate()
{
cout << "~Coordinate()" << endl;
}
main.cpp:
#include <iostream>
#include "Coordinate.h"
#include <stdlib.h>
using namespace std;
int main()
{
// 使用对象指针指向内存,两种方法
// 堆中实例化
Coordinate *p1 = NULL;
p1 = new Coordinate; //因为有默认构造函数,括号可写可不写
Coordinate *p2 = new Coordinate(); //方法2
p1->m_iX = 10;
p1->m_iY = 20; // 指针方式
(*p2).m_iX = 30; //*p2使p2变成了一个对象
(*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;
system("pause");
return 0;
}
![](https://img.haomeiwen.com/i1779926/5676bd541d3286e9.png)
创建时line对象中只有两个占4字节的指针。
而实例化出的两个对象在堆中;销毁时,先销毁堆中的,再释放line对象。
对象成员指针实践
![](https://img.haomeiwen.com/i1779926/bd5f3d087fc95188.png)
4-5-ObjectMemberPointer
Coordinate.h
class Coordinate
{
public:
Coordinate(int x, int y);
~Coordinate();
public:
int getX();
int getY();
private:
int m_iX;
int m_iY;
};
Coordinate.cpp
#include <iostream>
#include "Coordinate.h"
using namespace std;
Coordinate::Coordinate(int x,int y)
{
m_iX = x;
m_iY = y;
cout << "Coordinate()"<<m_iX<<","<<m_iY << endl;
}
Coordinate::~Coordinate()
{
cout << "~Coordinate()" << m_iX << "," << m_iY << endl;
}
int Coordinate::getX() {
return m_iX;
}
int Coordinate::getY(){
return m_iY;
}
Line.h:
#include "Coordinate.h"
class Line {
public:
Line(int x1,int y1,int x2,int y2);
~Line();
void setCoorA(int x, int y);
void setCoorB(int x, int y);
void printInfo();
private:
Coordinate *m_pCoorA;
Coordinate *m_pCoorB; //这是一个坐标类的对象指针。它只是一个指针。
};
Line.cpp:
#include "Line.h"
#include <iostream>
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()" << endl;
}
Line::~Line() {
delete m_pCoorA;
m_pCoorA = NULL;
delete m_pCoorB;
m_pCoorB = NULL;
cout << "~Line()" << endl;
}
void Line::printInfo() {
cout << "("<<(*m_pCoorA).getX()<<","<< (*m_pCoorA).getY()<< ")" << endl;
cout << "(" << m_pCoorB->getX() << "," << m_pCoorB->getY() << ")" << endl;
}
main.cpp:
#include <stdlib.h>
#include <iostream>
#include <string>
#include "Line.h"
using namespace std;
int main(void)
{
Line *p = new Line(1,2,3,4);
p->printInfo();
delete p;
p = NULL;
cout << sizeof(p) << endl;
cout << sizeof(Line) << endl;
system("pause");
return 0;
}
运行结果:
![](https://img.haomeiwen.com/i1779926/f6e0eae0b96f6dfd.png)
this表达什么地址,取决于当前所在作用域。这样就可以标记出自身的成员,与参数可以分清哪个是哪个了。
class Array
{
public:
Array(int len){this->len = len;}//对
int getLen(){return len;}
void setLen(int len){this->len = len;}//对
private:
int len;
}
对象结构:
![](https://img.haomeiwen.com/i1779926/e7540720fe4ef63c.png)
- 存在多个对象,成员函数只有代码区内的一份。
又没有传递参数,成员函数如何确定该调用哪个对象的数据成员?
- 成员函数如何访问到对应对象的数据成员?
class Array
{
public:
Array(T *this,int len){this->len = len;}//对
int getLen(T *this){return this->len;}
void setLen(T *this,int len){this->len = len;}//对
private:
int len;
}
![](https://img.haomeiwen.com/i1779926/910478622d6f6b02.png)
- 通过this来实现分辨不同的arr。
- 编译器自动的为每一个成员函数的参数列表都自动加上了this指针。
this指针在参数列表中的位置(代码实践)
要求:
![](https://img.haomeiwen.com/i1779926/9be48fbddcd62b10.png)
正常版本1:
4-7-ThisPointerPosition
Array.h
class Array
{
public:
Array(int len);
~ Array();
void printAddr();
void printArr();
int getLen();
void setLen(int val);
void printInfo();
private:
int m_iLen;
};
Array.cpp:
#include <iostream>
#include "Array.h"
using namespace std;
Array::Array(int len)
{
m_iLen = len;
cout << "Array()" << endl;
}
Array::~Array() {
cout << "~Array()" << endl;
}
void Array::setLen(int len) {
m_iLen = len;
}
int Array::getLen() {
return m_iLen;
}
void Array::printInfo() {
}
main.cpp:
#include <iostream>
#include <stdlib.h>
#include "Array.h"
using namespace std;
int main(void)
{
Array arr1(10);
system("pause");
return 0;
}
![](https://img.haomeiwen.com/i1779926/7684ca2720ef0116.png)
实际成员函数有一个隐藏的参数,this指针。
![](https://img.haomeiwen.com/i1779926/f5dbc8b8ec896d87.png)
this指针变成了一个常指针,通过常指针改变数据,显然是不被允许的。
互为重载:
void changeX() const;
void changeX(); //互为重载
Q:当直接调用:coordinate.changeX()
到底调用的是哪一个呢?
A:调用的是普通的不带const的。
Q: 那么想调用那个带const的如何写?
A:代码如下
int main(void)
{
// 实例化对象时用const修饰这个对象
const Coordinate coordinate(3,5);//常对象
coordinate.changeX(); // 调用的是常成员函数
return 0;
}
常对象调用的是常成员函数。
普通对象调用的是普通成员函数。
常对象成员与常成员函数代码实践
![](https://img.haomeiwen.com/i1779926/a32480c32e94235c.png)
5-2-ConstantMemberFunction
Coordinate.h :
class Coordinate
{
public:
Coordinate(int x, int y);
~Coordinate();
public:
int getX() const;//此处声明该成员函数为常成员函数
void setX(int x); // 相当于setX(Coordinate *this, int x)
int getY() const;//同上
void setY(int y);
private:
int m_iX;
int m_iY;
};
Coordinate.cpp
#include <iostream>
#include "Coordinate.h"
using namespace std;
Coordinate::Coordinate(int x, int y)
{
m_iX = x;
m_iY = y;
cout << "Coordinate()" << m_iX << "," << m_iY << endl;
}
Coordinate::~Coordinate()
{
cout << "~Coordinate()" << m_iX << "," << m_iY << endl;
}
int Coordinate::getX() const{
return m_iX;
}
void Coordinate::setX(int x) {
m_iX = x;
}
int Coordinate::getY() const{
return m_iY;
}
void Coordinate::setY(int y) {
m_iY = y;
}
Line.h:
#include "Coordinate.h"
class Line {
public:
Line(int x1, int y1, int x2, int y2);
~Line();
void setCoorA(int x, int y);
void setCoorB(int x, int y);
void printInfo();
void printInfo() const;//互为重载
private:
const Coordinate m_coorA; // Coordinate const m_coorA;
Coordinate m_coorB;
};
Line.cpp:
#include "Line.h"
#include <iostream>
using namespace std;
Line::Line(int x1, int y1, int x2, int y2) :m_coorA(x1, y1), m_coorB(x2, y2) {
cout << "Line()" << endl;
}
Line::~Line() {
cout << "~Line()" << endl;
}
void Line::setCoorA(int x, int y) {
//m_coorA.setX(x); // 出现问题: 此时相当于在setX中传递了一个this指针
//m_coorA.setX(y);
}
void Line::setCoorB(int x, int y) {
m_coorB.setX(x);
m_coorB.setY(y);
}
void Line::printInfo() {
cout << "printInfo()" << endl;
cout << "(" << m_coorA.getX() << "," << m_coorA.getY() << ")" << endl;
cout << "(" << m_coorB.getX() << "," << m_coorB.getY() << ")" << endl;
}
void Line::printInfo() const{
cout << "printInfo() const" << endl;
cout << "(" << m_coorA.getX() << "," << m_coorA.getY() << ")" << endl;
cout << "(" << m_coorB.getX() << "," << m_coorB.getY() << ")" << endl;
}
main.cpp
#include <stdlib.h>
#include <iostream>
#include <string>
#include "Line.h"
using namespace std;
int main(void)
{
Line line(1, 2, 3, 4);
line.printInfo();//调用的是普通的
const Line line2(1, 2, 3, 4);
line2.printInfo();//调用的是常成员函数
system("pause");
return 0;
}
![](https://img.haomeiwen.com/i1779926/ec9b50716ec1182f.png)
架构描述
涉及两个类: 迷宫类(MazeMap)& 走迷宫的人(Person)
二维数组:
1代表墙,0代表路,自己决定。
![](https://img.haomeiwen.com/i1779926/19df5fd7acfd835b.png)
迷宫类(MazeMap)
数据成员:
- 墙壁字符
- 通路字符
- 迷宫数组
成员函数:
- 构造函数
- 数据封装函数
- 迷宫回执函数
- 迷宫边界检查函数
人类(MazePerson)
数据成员:
- 人的字符
- 人的朝向
- 人当前位置(设置在入口)
- 人前一个位置 (人走动,前位置抹掉,后一个位置绘制)
- 人的速度
成员函数:
- 构造函数
- 数据封装函数
- 向不同方向前进的函数(上下左右)
- 转弯函数
- 开始函数
控制台动画控制:
/*
* 函数名称:gotoxy
* 函数功能:确定控制台中字符的输出位置
* 函数列表:
* x:横坐标
* y:纵坐标
*/
void MazePerson::gotoxy(int x, int y)
{
COORD cd;
cd.X = x;
cd.Y = y;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(handle,cd); // 把光标定位到相应位置上
};
首先需要定义一个迷宫,迷宫是一个二维数组,WaLL是墙,Road是路。
绘制迷宫cout就可以了。有了迷宫的二维数组,先实例化一个迷宫对象,通过setMazeMap函数将
二维数组设置进去,SetMazeWall告诉计算机墙用什么表示。设置好之后绘制迷宫。
走迷宫的人,设置人的位置位于入口。设置人的速度,设置人的字符形状。
人开始运动。
注意事项:
- 枚举类型:方向(上下左右)
- 常量定义:宏定义 & const
成就感源于克服困难
迷宫代码实现
未完待续, 之后补充。
网友评论