用视频讲会好点我之前也录过相关视频可以先凑合看
之前也说了C++一个类是由源文件和头文件组成的,所以分别对头文件和源文件进行设计
头文件写的一般都是类的声明(包括类成员和方法的声明),函数原型,#define(宏定义)等,不涉及实现。
或者说头文件就是去描述这个类的实列长什么样有什么特征,他会干什么,比如我们去设计一个猫类,那首先想去想猫有什么特征或者说属性,比如有四肢,有尾巴,有花纹等等,然后去想他有什么动作或者说行为,比如进食,移动,睡觉,那类的头文件就是去对这些做一个描述,做一个声明。
比如上面的猫类我们头文件可以这么设计,对于类的概念可以类比我们之前学过的java
class Cat{
public:
//访问权限为公开
Cat(void);//无参构造,因为为了保证对象在创建的时候
//就被构造函数初始化所以构造函数应该和类名一致
virtual ~Cat(void);//析构函数
/*c++不同于java有垃圾回收,为了在在跳出程序
(比如关闭文件、释放内存等)前释放资源,
所以人们设计出析构函数,它会在每次删除所创建的对象时执行*/
void Move(int x0,int y0);//猫会移动比如移动到(x0,y0)
void Eat();//猫会吃
void Sleep(int times);//猫会睡觉
private:
//访问权限为私有一般类的属性都做为私有
Hand lefthand;
/*猫的左手 Hand是我写的另一个类
这里不做介绍你就当成有个左手就行主要是基础
数据类型找不到一个好表示左手的*/
Hand righthand;//同上猫的右手
//还有什么脚啊尾巴啊就不说了有这么概念就行主要是说一下C++的类该咋写
};
那这样你可能对类有一个初步的认识,我们来想一下该区如何设计一个CLine直线类
首先我们去想一下该怎么画直线画直线需要啥,我们画直线肯定需要两个点,因为两点确定一条直线,所以CLine类就应该两个成员变量表示起点和终点,同样的我们要画肯定需要把笔移动到起点所以有一个MoveTo函数表示移动画笔到起点,同样有个函数LineTo表示用笔去画一条直线,那可能有人会说不应该还要有画笔吗,之前我提到过CDC类设备环境就相当于一个工具箱里面啥都有什么画布,画笔,笔刷等等,我们所有的绘画都依靠他来进行,所以不比去在类里面声明一个成员CDC,只需在画线的时候将他作为参数带入即可,不理解的看代码就好了。
首先创建头文件Line.h
#pragma once
#include "pch.h"
#include "afxwin.h"
#include "atltypes.h"
#include "dlgs.h"
//声明要用到的头文件其中"pch.h"必须在最上面
class CLine {
public:
CLine(void);//无参构造函数
virtual ~CLine(void);//析构函数
void MoveTo(CDC* pDC,CPoint p0);
//将画笔移动到起点,参数需要起点和CDC设备环境
void LineTo(CDC* pDC, CPoint p1);
//画一条直线到终点,参数需要终点和CDC
private:
CPoint P0;//起点,CPoint是MFC封装好的类之前提到过
CPoint P1;//终点
};
既然头文件已经实现好了,那接下来就需要把头文件声明的操作函数都在源文件去具体实现
首先创建源文件Line.cpp
#include "pch.h"
#include "Line.h"
#include "math.h"
CLine::CLine()//无参构造函数
{
}
CLine::~CLine()//析构函数
{
}
void CLine::MoveTo(CDC* pDC,CPoint p0) {//移动起点到p0
P0 = p0;//讲指定点p0复制给成员起点P0
}
void CLine::LineTo(CDC* pDC, CPoint p1) {//Bresenham算法实现
P1 = p1;//设置终点
int x=P0.x, y=P0.y,f;//x,y为要画的点,f是用来判断候选点的关系式
int directx=-1,directy=-1;
/*x,y方向增量就咋说比如终点x坐标在起点x坐标左边的话,
那x就每次加一反之减一,这里先初始化为-1后面再判断一下*/
int dx = abs(P1.x - P0.x);//算出起点x坐标和终点x坐标差值
int dy = abs(P1.y - P0.y);//算出y坐标差值
if (dx >= dy) {//谁大谁就是主增量方向在视频有详细解释
//这里x大x就是主增量方向
if (P1.x > P0.x)directx = 1;//判断终点x坐标和起点坐标关系
//终点在起点右边的话就每次加一反之减一
if (P1.y > P0.y)directy = 1;//同上
f = 2 * dy - dx;//设置判断候选点关系式初值
if (directx > 0) {//分类讨论如果x是每次加一的话原本是可以根据
//directx来一个巧妙地设计但是我发现当值为0是是个Bug优化太麻烦了
//就这么分类讨论吧
for (x = P0.x; x <= P1.x; x++) {//从起点遍历到终点算出对应y坐标
//然后绘制对应像素点
pDC->SetPixelV(x, y, RGB(255, 0, 255));
if (f < 0) {
f += 2 * (dy - dx);
}
else {
y += directy;
f += 2 * dy;
}
}
}
else {//如果x是每次减一的话
for (x = P0.x; x >= P1.x; x--) {
pDC->SetPixelV(x, y, RGB(255, 0, 255));
if (f < 0) {
f += 2 * (dy - dx);
}
else {
y += directy;
f += 2 * dy;
}
}
}
}
else {
//同上就是这里是y是主要增量方向就x和y颠倒一下位置就行
if (P1.x > P0.x)directx = 1;
if (P1.y > P0.y)directy = 1;
f = 2 * dx - dy;
if (directy > 0) {
for (y = P0.y; y <= P1.y; y ++) {
pDC->SetPixelV(x, y, RGB(255, 0, 255));
if (f < 0) {
f += 2 * (dx - dy);
}
else {
x += directx;
f += 2 * dx;
}
}
}
else {
for (y = P0.y; y >= P1.y ; y--) {
pDC->SetPixelV(x, y, RGB(255, 0, 255));
if (f < 0) {
f += 2 * (dx - dy);
}
else {
x += directx;
f += 2 * dx;
}
}
}
}
}
到这里CLine类就已经设计完毕,接下来实际运用一下
进入你的TestView.cpp文件,这里的Test取决于你的项目名称,然后找到OnDraw函数在这里实现绘制代码
void CTest2View::OnDraw(CDC* pDC)
{
CTest2Doc* pDoc = GetDocument();//之前介绍过了
ASSERT_VALID(pDoc);
if (!pDoc)
return;
//套我们之前的自定义二次元坐标系模板
CRect rect;//创建矩形对象
GetClientRect(rect);//初始化矩形为客户区
pDC->SetMapMode(MM_ANISOTROPIC);//设置映射模式为各向异性的
pDC->SetWindowExt(rect.Width(), rect.Height());//设置窗口的宽度和高度
pDC->SetViewportExt(rect.Width(), -rect.Height());//设置视区的宽度和高度,同时设置坐标系正方向
pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);//设置原点坐标于中心
rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);
// TODO: 在此处为本机数据添加绘制代码
CLine* line1=new CLine;
/*
创建一个CLine对象,这里我好好说一下有助于你们对于面向对象编程的理解
首先把他拆成两部分,Cline* line1和 new Cline,左边是创建了一个Cline
类型的指针,右边是创建了一个CLine对象在堆中(应该是在队中java就是没查
过c++是不是),然后将指针指向这个对象,通过指针去调用对象的操作和访问其
属性。
那为啥要这么做呢,因为对象通常都很大,就像这个直线CLine他有很多成员变量
和操作函数,在堆栈中存放太占空间,所以把它放在堆中,堆的空间大,举个例子
你有一台电视机,电视机很大,你看电视不可能把电视抱着,走到哪抱到哪,假如
放置到一个地方,那你想去挑音量换台的时候总不可能每次都去到电视机跟前去按
他的按钮,这样很麻烦,所以我们选择的是,将他放在一个大空间放着,然后用遥
控器去操作他,遥控器很小可以随时拿着,放在身上要去操作电视机也很便捷,那
这个遥控器就是这个指针在java里面就是引用,电视机就是那个对象
*/
CPoint p0(-200, 100);//设置起点
CPoint p1(0, -200);//设置终点
line1->MoveTo(pDC,p0);//调用Cline类的MoveTo函数去移动到起点
line1->LineTo(pDC, p1);//调用画直线函数去画
delete line1;//释放内存
}