一、实验目的

  1. 掌握面向对象编程基本思想
  2. 掌握 VC++.net 中创建类
  3. 掌握建立和使用对象
  4. 掌握运算符号重载
  5. 理解类的继承和多态性

二、实验内容

要求:设计一个水准网类,注意水准的基本属性。功能上要求:(1)能够通过
界面上的文本框按照以下格式输入数据;(2)能够计算未知点近似高程。
在这里插入图片描述
算例:
在这里插入图片描述
在这里插入图片描述

三、设计与实现

3.1 实验基本思路

1、设计一个水准网的类,类的(私有)成员包括已知点数,已知点,未知点数,未知点,高程差数和高程差,类能够实现的功能包括(重载)构造函数,析构函数,拷贝构造函数,为成员赋值,返回成员,计算未知点高程的近似值。
2、界面设计:设置两个编辑框,一个负责输入数据,另外一个负责输出数据(未知点的高程)。设置两个按钮,一个为“计算”,负责提取输入框中的数据,并输出结果,另一个按钮为“取消”,负责将两个编辑框清空。
3、本实验要利用类做到代码与界面分离,即不能直接利用界面输入的数据进行计算,要利用类进行计算。输入输出数据。

3.2 类的设计

3.2.1 设计思路

1、类的私有成员的设计思路:已知点和未知点都采用点位结构体数组来表示,用new函数动态存储,点位结构体的成员包含点号、高程值和高程是否已计算出的标志(bool型变量,为后来未知点高程计算函数的编写提供便利)。高程差采用高程差结构体数组来表示,同样采用new函数来动态存储,高程差结构体的成员包括起点、终点(均为点位结构体的指针变量)、高程差和路线长度。

#pragma once

//点位结构体
struct Point
{
	CString name;//点号
	double dHigh;//高程值
	bool state;//高程值是否已计算出的状态
};

//高程差结构体
struct DiffH
{
	Point* Start;//起点
	Point* End;//终点
	double dDiffH;//高程差
	double dLen;//路线长度
};

class LevNet
{
private:
	int GivPoiCount;//已知点数
	Point* GivPoi;//动态存储已知点数组
	int ObsPoiCount;//未知点数
	Point* ObsPoi;//动态存储未知点数组
	int DiffHCount;//高程差数
	DiffH* ObsDiffH;//动态存储高程差数组
public:
	LevNet(void);//构造函数
	LevNet(int GivPoiCount_, Point* GivPoi_, int ObsPoiCount_, Point* ObsPoi_, int DiffHCount_, DiffH* ObsDiffH_);//重载构造函数
	~LevNet(void);//析构函数
	LevNet(LevNet &A);//拷贝构造函数
	void SetGivPoi(int GivPoiCount_, Point* GivPoi_);//为已知点赋值
	void SetObsPoi(int ObsPoiCount_, Point* ObsPoi_);//为未知点赋值
	void SetDiffH(int DiffHCount_, DiffH* ObsDiffH_);//为高程差赋值
	Point* GetGivPoi(void);//返回已知点
	Point GetGivPoi_i(int i);//返回第i个已知点
	Point* GetObsPoi(void);//返回未知点
	Point GetObsPoi_i(int i);//返回第i个未知点
	DiffH* GetDiffH(void);//返回高程差
	DiffH GetDiffH_i(int i);//返回第i个高程差
	bool CalH(Point* ObsPoi);//遍历高程差,并计算所求未知点的高程,能计算出返回真,计算不出返回假
	void CalOBsPOi(void);//求未知点高程
	void InOutData(CString& strData, CString& strResult);//从界面输入并输出数据
};


3.2.3 cpp文件(代码展示)

下面展示类的cpp文件的代码(部分)

#include "pch.h"
#include "LevNet.h"
#include "Cppplus.h"

//构造函数
LevNet::LevNet(void){}

//重载构造函数
LevNet::LevNet(int GivPoiCount_, Point* GivPoi_, int ObsPoiCount_, Point* ObsPoi_, int DiffHCount_, DiffH* ObsDiffH_)
{
	//初始化已知点
	GivPoiCount = GivPoiCount_;
	GivPoi = new Point[GivPoiCount];
	for (int i = 0; i < GivPoiCount; i++)
	{
		GivPoi[i] = GivPoi_[i];
	}
	//初始化未知点
	ObsPoiCount = ObsPoiCount_;
	ObsPoi = new Point[ObsPoiCount];
	for (int i = 0; i < ObsPoiCount; i++)
	{
		ObsPoi[i] = ObsPoi_[i];
	}
	//初始化高程差
	DiffHCount = DiffHCount_;
	ObsDiffH = new DiffH[DiffHCount];
	for (int i = 0; i < DiffHCount; i++)
	{
		ObsDiffH[i] = ObsDiffH_[i];
	}
}

//析构函数
LevNet::~LevNet(void)
{
	if (GivPoi)
	{
		delete[] GivPoi;
		GivPoi = NULL;
	}

	if (ObsPoi)
	{
		delete[] ObsPoi;
		ObsPoi = NULL;
	}

	if (ObsDiffH)
	{
		delete[] ObsDiffH;
		ObsDiffH = NULL;
	}
}

//拷贝构造函数
LevNet::LevNet(LevNet& A)
{
	//拷贝已知点
	GivPoiCount = A.GivPoiCount;
	GivPoi = new Point[GivPoiCount];
	for (int i = 0; i < GivPoiCount; i++)
	{
		GivPoi[i] = A.GivPoi[i];
	}
	//拷贝未知点
	ObsPoiCount = A.ObsPoiCount;
	ObsPoi = new Point[ObsPoiCount];
	for (int i = 0; i < ObsPoiCount; i++)
	{
		ObsPoi[i] = A.ObsPoi[i];
	}
	//拷贝高程差
	DiffHCount = A.DiffHCount;
	ObsDiffH = new DiffH[DiffHCount];
	for (int i = 0; i < DiffHCount; i++)
	{
		ObsDiffH[i] = A.ObsDiffH[i];
	}
}

//为已知点赋值
void LevNet::SetGivPoi(int GivPoiCount_, Point* GivPoi_)
{
	GivPoiCount = GivPoiCount_;
	GivPoi = new Point[GivPoiCount];
	for (int i = 0; i < GivPoiCount; i++)
	{
		GivPoi[i] = GivPoi_[i];
	}
}

//为未知点赋值
void LevNet::SetObsPoi(int ObsPoiCount_, Point* ObsPoi_)
{
	ObsPoiCount = ObsPoiCount_;
	ObsPoi = new Point[ObsPoiCount];
	for (int i = 0; i < ObsPoiCount; i++)
	{
		ObsPoi[i] = ObsPoi_[i];
	}
}

//为高程差赋值
void LevNet::SetDiffH(int DiffHCount_, DiffH* ObsDiffH_)
{
	DiffHCount = DiffHCount_;
	ObsDiffH = new DiffH[DiffHCount];
	for (int i = 0; i < DiffHCount; i++)
	{
		ObsDiffH[i] = ObsDiffH_[i];
	}
}

//返回已知点
Point* LevNet::GetGivPoi(void)
{
	return GivPoi;
}

//返回第i个已知点
Point LevNet::GetGivPoi_i(int i)
{
	return GivPoi[i - 1];
}

//返回未知点
Point* LevNet::GetObsPoi(void)
{
	return ObsPoi;
}

//返回第i个未知点
Point LevNet::GetObsPoi_i(int i)
{
	return ObsPoi[i - 1];
}

//返回高程差
DiffH* LevNet::GetDiffH(void)
{
	return ObsDiffH;
}

//返回第i个高程差
DiffH LevNet::GetDiffH_i(int i)
{
	return ObsDiffH[i - 1];
}

//遍历高程差,并计算所求未知点的高程,能计算出返回真,计算不出返回假
bool LevNet::CalH(Point* ObsPoi)
{
	int i;
	for (i = 0; i < DiffHCount; i++)
	{
		if (strCompare(ObsDiffH[i].Start->name, ObsPoi->name) && ObsDiffH[i].End->state == TRUE)//如果所求点是高程差的起点并且终点的高程已求出
		{
			ObsPoi->dHigh = ObsDiffH[i].End->dHigh - ObsDiffH[i].dDiffH;
			ObsPoi->state = TRUE;
			return TRUE;
		}
		else if (ObsDiffH[i].Start->state == TRUE && strCompare(ObsDiffH[i].End->name, ObsPoi->name))//如果所求点是高程差的终点并且起点的高程已求出
		{
			ObsPoi->dHigh = ObsDiffH[i].Start->dHigh + ObsDiffH[i].dDiffH;
			ObsPoi->state = TRUE;
			return TRUE;
		}
	}
	if (i == DiffHCount)//如果所求点的高程无法求出
	{
		return FALSE;
	}
}

//求未知点高程
void LevNet::CalOBsPOi(void)
{
	int go;//所有未知点的高程是否全部求出的标志
	do
	{
		go = 1;
		for (int i = 0; i < ObsPoiCount; i++)
		{
			if (ObsPoi[i].state == FALSE)//如果所求点的高程没有求出
			{
				if (!CalH(&ObsPoi[i]))
				{
					go = 0;
				}//如果所求点的高程值没有求出的话,标记未知点的高程没有全部求出
			}
		}
	} while (go == 0);
}

//从界面输入并输出数据
void LevNet::InOutData(CString& strData, CString& strResult)
{
	strData.Trim();//去掉数据中的开头和结尾的换行符
	int iLine;
	//分行并存入字符串数组
	CStringArray aStrLine;
	iLine = SplitStringArray(strData, 13, aStrLine);//13 ‘\r’
	int n;
	CStringArray aStrTmp;

	//获得已知点
	n = SplitStringArray(aStrLine[0], ',', aStrTmp);
	int GivPoiCount_ = _ttoi(aStrTmp[0]);
	Point* GivPoi_ = new Point[GivPoiCount_];
	for (int i = 1; i <= GivPoiCount_; i++)
	{
		n = SplitStringArray(aStrLine[i], ',', aStrTmp);
		GivPoi_[i - 1].name = aStrTmp[0];
		GivPoi_[i - 1].dHigh = _tstof(aStrTmp[1]);
		GivPoi_[i - 1].state = TRUE;
	}

	//获得未知点
	n = SplitStringArray(aStrLine[long(GivPoiCount_ + 1)], ',', aStrTmp);
	int ObsPoiCount_ = _ttoi(aStrTmp[0]);
	Point* ObsPoi_ = new Point[ObsPoiCount_];
	n = SplitStringArray(aStrLine[long(GivPoiCount_ + 2)], ',', aStrTmp);
	for (int i = 0; i < ObsPoiCount_; i++)
	{
		ObsPoi_[i].name = aStrTmp[i];
		ObsPoi_[i].state = FALSE;
	}

	//获得高程差
	n = SplitStringArray(aStrLine[long(GivPoiCount_ + 3)], ',', aStrTmp);
	int DiffHCount_ = _ttoi(aStrTmp[0]);
	DiffH* ObsDiffH_ = new DiffH[DiffHCount_];
	for (int i = GivPoiCount_ + 4; i < DiffHCount_ + GivPoiCount_ + 4; i++)
	{
		n = SplitStringArray(aStrLine[i], ',', aStrTmp);
		//遍历已知点,寻找和高程差中起点或终点相同的点
		for (int t = 0; t < GivPoiCount_; t++)
		{
			if (strCompare(GivPoi_[t].name, aStrTmp[0]))
			{
				ObsDiffH_[i - GivPoiCount_ - 4].Start = &GivPoi_[t];
			}
			if (strCompare(GivPoi_[t].name, aStrTmp[1]))
			{
				ObsDiffH_[i - GivPoiCount_ - 4].End = &GivPoi_[t];
			}
		}
		//遍历未知点,寻找和高程差中起点或终点相同的点
		for (int k = 0; k < ObsPoiCount_; k++)
		{
			if (strCompare(ObsPoi_[k].name, aStrTmp[0]))
			{
				ObsDiffH_[i - GivPoiCount_ - 4].Start = &ObsPoi_[k];
			}
			if (strCompare(ObsPoi_[k].name, aStrTmp[1]))
			{
				ObsDiffH_[i - GivPoiCount_ - 4].End = &ObsPoi_[k];
			}
		}
		//获得高程差和路线长度
		ObsDiffH_[i - GivPoiCount_ - 4].dDiffH = _tstof(aStrTmp[2]);
		ObsDiffH_[i - GivPoiCount_ - 4].dLen = _tstof(aStrTmp[3]);
	}

	//调用赋值函数把得到的已知点、未知点和高程差存入LevNet类中
	SetGivPoi(GivPoiCount_, GivPoi_);
	SetObsPoi(ObsPoiCount_, ObsPoi_);
	SetDiffH(DiffHCount_, ObsDiffH_);

	//调用未知点高程求解函数
	CalOBsPOi();

	//调用未知点返回函数
	Point* p = GetObsPoi();
	for (int i = 0; i < ObsPoiCount_; i++)
	{
		ObsPoi_[i] = p[i];
	}

	//格式化输出
	strResult.Format(_T("%s\r\n"),
		_T("未知点高程:"));
	CString strOutput;
	for (int i = 0; i < ObsPoiCount_; i++)
	{

		strOutput.Format(_T("%s%.3f\r\n"),
			_T(":"), ObsPoi_[i].dHigh);
		strResult = strResult + ObsPoi_[i].name + strOutput;
	}

	//释放空间
	if (GivPoi_)
	{
		delete[] GivPoi_;
		GivPoi_ = NULL;
	}
	if (ObsPoi_)
	{
		delete[] ObsPoi_;
		ObsPoi_ = NULL;
	}
	if (ObsDiffH_)
	{
		delete[] ObsDiffH_;
		ObsDiffH_ = NULL;
	}
}


3.3 界面设计

1、界面设计思路:两个编辑框均添加CString变量,输入框的变量名为strData,输出框的变量名为strResult。
2、界面设计展示:
界面设计展示

3.4 “计算”按钮对应的映射函数

1、设计思路:首先利用建立的水准网类类型声明一个变量LNEet。然后利用分割函数把输入的数据分行存入一个CStringArray数组中。接着再次利用分割函数获得已知点的数据、未知点的数据和高程差的数据。然后把获得的数据存入类LNet中。接着调用类的未知点高程计算函数计算出未知点的高程。然后利用未知点返回函数得到已计算出高程的未知点。接着格式化输出。最后释放空间。
2、下面展示“计算”按钮对应的映射函数的代码(部分)

//“计算”按钮的映射函数
void CCHtest6Dlg::OnBnClickedCal()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(true);
	LevNet LNet;
	LNet.InOutData(strData, strResult);
	UpdateData(false);
}

3.5 其他代码

下面展示本实验中所用到的其他函数的代码(部分):

// 分割字符串,得到字符串数组(CStringArray )
int SplitStringArray(CString str, char split, CStringArray& aStr)
{
	int startIdx = 0;
	int idx = str.Find(split, startIdx);
	aStr.RemoveAll();//先清空
	while (-1 != idx)
	{
		CString sTmp = str.Mid(startIdx, idx - startIdx);
		aStr.Add(sTmp);
		startIdx = idx + 1;
		idx = str.Find(split, startIdx);
	}
	CString sTmp = str.Right(str.GetLength() - startIdx);
	if (!sTmp.IsEmpty())
		aStr.Add(sTmp);
	return aStr.GetSize();
}

//比较两个字符串是否相等,相等返回真,不相等返回假
bool strCompare(CString a, CString b)
{
	a.Remove('\r');
	b.Remove('\r');
	a.Remove('\n');
	b.Remove('\n');
	a.Remove(' ');
	b.Remove(' ');
	if (!a.Compare(b))
	{
		return TRUE;
	}
	else
		return FALSE;
}

3.6 结果展示

结果展示

四、心得总结

本实验对类的设计以及指针的运用的要求比较高,程序稍复杂,对逻辑思维的要求也较高。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐