在编程中,会经常用到数学计算,所以C++将常用的数学计算,例如求正余弦等,封装成函数(正是我们在3.2 数学计算中学习到的),我们只需要写入简单的语句就可以执行所需要的功能,这正是函数的意义。在这一章的学习,我们会建立起模块化的思维,小的功能模块我们会封装成函数,而一些大的功能模块,则封装成类。
刚开始进行函数的编写,会感觉有些难度。其实,只要稍稍多做一些函数编写的训练的时候,就可以很容易找到其中的规律。这一章会有大量的练习实践,勤动手是学好这一章内容的关键。
7.1 函数
写一个求中点函数,输入两个点坐标——Point1,Point2,求出Point1与Point2的中点坐标。参考表7-1中的程序。如何来完成这个编程要求,先看一下函数的编写规范。
#include<iostream>
#include<cmath>
using namespace std;
struct Point{
double x;
double y;
};
Point GetMidpoint(Point P1, Point P2)
{
Point Midpoint={0,0};
Midpoint.x = (P1.x + P2.x )/2;
Midpoint.y = (P1.y + P2.y )/2;
return Midpoint;
}
int main()
{
Point Point1,Point2;
cout<<"请输入两个点的坐标"<<endl;
cin>>Point1.x>>Point1.y;
cin>>Point2.x>>Point2.y;
Point Point_MidP1P2;
Point_MidP1P2=GetMidpoint(Point1,Point2);
cout<<"中点坐标为"<<Point_MidP1P2.x<<','<<Point_MidP1P2.y<<endl;
system("pause");
return 0;
}
表7-1 求中点函数程序
函数的代码应该放在主函数前面(如果想放在后面需要声明,在7.2三点求圆心会讲到如果将函数的实现放在主函数后面,应该如何进行声明),具体如何实现该函数请看7.1.1小节。
第27行代码对函数GetMidPoint进行了调用,并将求得的结果赋值给了Point_MidP1P2结构体变量,函数的使用可以回顾3.2数学计算这一节。区别只在于3.2数学计算中的函数是C++提供给我们的,而函数GetMidPoint是我们自己编写的。
测试程序,输入两个坐标为(3,5),(5,10)的坐标点,程序自动计算中点坐标值并显示,如图7-1所示。
图7-1 求中点函数测试结果示意图
7.1.1 函数的编写规范
首先,先看一下函数的编写规范,函数定义的格式为
函数返回值的类型名 函数名(参数1类型 参数1名称, 参数2类型 参数2名称, ...)
{
函数体
}
要求中点坐标,首先回顾上一章将的内容——结构体,先定义名为Point的结构体类型,并定义名为Point1,Point2,Point3的结构体变量,结构体类型为Point :
struct Point{
double x;
double y;
};
然后开始进行求中点的函数的编写,根据易懂的命名方式,将该功能的函数名起名为GetMidpoint,最终求得的结果为一个点,所以原函数返回值的类型名为Point,共有两个参数,都为Point类型。
Point GetMidPoint(Point P1, Point P2)
第一个Point为函数返回值的类型名,即最终我们想要求得结果的类型,这里我们要求中点坐标,所以最终的结果就是一个Point类型;空格后紧接着是函数名GetMidPoint;小括号内是求中点函数的参数,共有两个参数,并且都为Point类型,而P1和P2则是参数1名称,以及参数2名称。
接下里开始在大括号内进行函数体的编写。
Point Midpoint={0,0};
Midpoint.x = (P1.x + P2.x )/2;
Midpoint.y = (P1.y + P2.y )/2;
return Midpoint;
先定义了Point类型的名为Midpoint的结构体变量,并赋初值为0,0;然后求得点Midpoint的x坐标值以及y坐标值;函数最终的结果需要使用关键字return进行返回。
(当然,如果定义函数返回值的类型名为void的话,即函数什么都不返回,最后的return语句不需要编写;如果某个函数只执行某项功能,需要返回任何值得时候,就可以这样编写)
7.1.2 形式参数与实在参数
形式参数是在定义函数时放在函数名称之后的括号中的参数。在表7-1的程序中11行代码中的P1,P2为形式参数。在函数的实现部分,该函数还没有被调用,系统不对形式参数分配内存;函数被调用时(程序的第27行为调用函数的代码),系统对形式参数进行内存分配,在函数结束后,将这部分内存释放掉。形式参数属于局部变量,其作用域限定在函数体内。
实在参数是一个具有确定值的参数。在表7-1的程序中27行代码中的Point1,Point2为实在参数。调用函数时,将实在参数的值放入形式参数的内存单元当中。
需要注意的是,实在参数的个数以及类型必须和形式参数一致,并且前后排序也要一一对应。
7.1.3 编程练习:求两点连线
编写一个函数,输入两点坐标,能够自动计算出这两点连线的斜率和截距。程序如表7-2所示。
#include<iostream>
#include<cmath>
using namespace std;
struct Point{
double x;
double y;
};
struct Line{
double k;
double b;
};
Line GetLineOfTwoPoints(Point P1,Point P2)
{
Line StraightLine={0,0};
if(P1.x==P2.x)
{
P1.x=P2.x-0.0000001;
}
StraightLine.k = (P2.y - P1.y)/ (P2.x -P1.x);
StraightLine.b = P1.y - P1.x * StraightLine.k ;
return StraightLine;
}
int main()
{
Point Point1,Point2;
cout<<"请输入两个点的坐标"<<endl;
cin>>Point1.x>>Point1.y;
cin>>Point2.x>>Point2.y;
Line Line1_P1P2;
Line1_P1P2=GetLineOfTwoPoints(Point1,Point2);
cout<<"两点连线的斜截式方程为y="<<Line1_P1P2.k<<"x+"<<Line1_P1P2.b<<'.' <<endl;
system("pause");
return 0;
}
表7-2 求两点连线函数程序
在初高中的时候我们通常使用斜截式来表示直线,也就是说区分不同的直线只需要一个斜率k和一个截距b即可;第10至13行定义一个名为Line的结构体类型,成员为double型的斜率k,以及double型的截距b。
15行至25行代码实现了两点求连线的函数,函数返回值的类型名为Line,函数名为GetLineOfTwoPoints,参数有两个,都是Point型,两个形式参数为P1,P2;函数体内部先定义一个Line型的结构体变量StraightLine,用来求得直线的斜率和截距。注意第18行至21行有一个if语句,该语句是为了在求斜率的时候,防止分母为0;最后将得到的直线结果返回。
在34行进行了函数的调用,实在参数为Point1和Point2,所得连线结果赋值给结构体变量Line1_P1P2。
测试程序,输入两点坐标分别为(3,6)、(2,9),最终结果如图7-2所示。
图7-2 求两点连线函数测试结果示意图
7.1.4 编程练习:求中垂线
编写一个函数,输入两点坐标,能够自动计算出这两点连线的中垂线的斜率和截距。根据两点求中垂线,回忆初高中的数学知识,需要先求两点的中点,然后求得两点连线的斜率,垂线的斜率与连线的斜率相乘为-1,有了过中垂线的一个点以及斜率就可以求出中垂线了。程序如表7-3所示。
#include<iostream>
#include<cmath>
using namespace std;
struct Point{
double x;
double y;
};
struct Line{
double k;
double b;
};
Point GetMidpoint(Point P1, Point P2)
{
Point Midpoint={0,0};
Midpoint.x = (P1.x + P2.x )/2;
Midpoint.y = (P1.y + P2.y )/2;
return Midpoint;
}
Line GetLineOfTwoPoints(Point P1,Point P2)
{
Line StraightLine={0,0};
if(P1.x==P2.x)
{
P1.x=P2.x-0.0000001;
}
if(P1.y==P2.y)
{
P1.y=P2.y-0.0000001;
}
StraightLine.k = (P2.y - P1.y)/ (P2.x -P1.x);
StraightLine.b = P1.y - P1.x * StraightLine.k ;
return StraightLine;
}
Line GetMidperpendicular(Point P,double k)
{
Line StraightLine={0,0};
StraightLine.k = -1/k;
StraightLine.b = P.y - StraightLine.k * P.x ;
return StraightLine;
}
int main()
{
Point Point1,Point2;
cout<<"请输入两个点的坐标"<<endl;
cin>>Point1.x>>Point1.y;
cin>>Point2.x>>Point2.y;
Point Point_MidP1P2;
Point_MidP1P2=GetMidpoint(Point1,Point2);
Line Line1_P1P2;
Line1_P1P2=GetLineOfTwoPoints(Point1,Point2);
cout<<"两点连线的斜截式方程为y="<<Line1_P1P2.k<<"x+" <<Line1_P1P2.b<<'.'<<endl;
Line Line3_P1P2_Vertical;
Line3_P1P2_Vertical = GetMidperpendicular(Point_MidP1P2,Line1_P1P2.k);
cout<<"两点的中垂线的斜截式方程为y="<<Line3_P1P2_Vertical.k<<"x+" <<Line3_P1P2_Vertical.b<<'.'<<endl;
system("pause");
return 0;
}
表7-3 求中垂线函数程序
由于要求中垂线,所以需要先求中点坐标以及两点连线的斜率,所以将直接将前面的写好的求两点中点函数以及求两点连线函数拿来使用。
需要注意的是求两点连线的第31至34行防止两点的y坐标相同;如果两点y坐标相同,求得的斜率就为0,在求中垂线的时候,连线的斜率作为分母,这样就会出错。
40至46行实现了求中垂线函数,函数返回值的类型名为Line,函数名为 GetMidperpendicular,参数有两个,一个是Point型,一个是double型,两个形式参数的名称为P,k;函数体内部先定义一个Line型的结构体变量StraightLine,用来求得直线的斜率和截距。注意两个函数的结构体变量StraightLine都是局部参数,两者是不同的。
中垂线函数实现后,开始在主函数中进行中垂线求取。首先,求得两点的中点Point_MidP1P2;然后求得两点连线Line1_P1P2;最后,将中点Point_MidP1P2以及两点连线Line1_P1P2的斜率Line1_P1P2.k作为实在参数,调用求中垂线函数GetMidperpendicular,将结果赋值给Line1_P1P2。
测试程序,输入两个点坐标为(1,1)以及(0,2),最终结果如图7-3所示。
图7-3 求中垂线函数测试结果示意图
7.2 编程练习:三点求圆心
两点确定一条直线,那么几个点确定一个圆呢?回忆下高中的数学知识,三个点(不在同一条直线上)确定一个圆。这一节回顾初中的数学知识,并结合学习的程序语言,对如何将给定的不在同一条直线的三点进行圆心的求解形成一个初步的思路。
7.2.1 三点求圆心思路
如图7-4所示,当给定三个坐标点(x1,y1),(x2,y2),(x3,y3),我们分别称这三个坐标点为Point1,Point2,Point3;
我们连接坐标点Point1,Point2以及Point2,Point3,这样我们有了两条线段,我们分别称这两条线段为Line1_P1P2,Line2_P2P3;
然后我们分别对这两条线段Line1_P1P2,Line2_P2P3求中垂线,获得的中垂线我们称为Line3_P1P2_Vertical,Line4_P2P3_Vertical;
最后,我们能够得到这两条中垂线的交点——Point6_CenterOfCircle。
需要注意的是,在求三点求圆心前,需要先确定输入的三个点都不相同,并且不在一条直线上。判断不在一条直线上,可以将Line1_P1P2,Line2_P2P3的斜率是否相同,如果相同则意味着三点在一条直线上。
图7-4 三点求圆心坐标示意图