从VC++ 6.0到VS——记一个简单窗口程序的迁移

重新拾起大二时写过,或者说照着教材修改过的一份计算并绘制相图的代码。

讲道理,从功能上看,用C/C++来写似乎不是一个好的选择,没什么计算量,用Python应该会更简单一些(我仿佛又看到了一个坑…)。

印象中教程上的原始代码是用一门上古的C语言(变体?)写的,名字已经记不太清。当时是在机房装了那个语言的IDE,看着蓝屏一般的背景色和又大又复古的字体,幼小的我被吓坏了…

限于教学条件,那个时候自然是基于VC++ 6.0把代码重新改了一遍,然后就交作业了。分数还不错。

OK,回忆就到这里。昨天心血来潮,想在Visual Studio下重新跑一遍。于是…我就掉进了坑里…

代码功能与现状

代码实现的功能其实简陋,执行一下,跳出来一个NiO-MgO固溶体相图的窗口,就完了。所以虽然是窗口程序,用户完全没法做什么。

代码方面,除了在现在看来中了毒一般的代码风格和陈旧的头文件,还隐藏着C++标准改变埋下的坑。

改代码风格不用多说,坑的在后面…

适应VS

关于头文件

graphics.h

代码使用了一个古老而且非标准的头文件,graphics.h。既然如此,随便搜了一个头文件就include了。然而…我下的这个头文件本身就有bug…

1
int left=0, int right=0, int right=INT_MAX, int bottom=INT_MAX,

把前面那个right改成top,倒也不是什么大问题(反正全世界的图像处理都是这么搞的,从左和上开始定义)。

然后就是我下的这个graphics.h,和现有的代码八字不合…

比如,原始代码是这样写的

1
outtextxy(225, 220, "TWO PHASE DLAGRAM\n");

按照我下的头文件,第三个参数应当是char *类型,而这里填的"TWO PHASE DLAGRAM\n"cosnt char *,用强制类型转换解决

1
outtextxy(225, 220, (char*)"TWO PHASE DLAGRAM\n");

其余的问题后续再说,总之把类似上面头文件与代码不匹配的问题解决之后,我发现一个严重的事情…我下的这个头文件…仅有函数声明…没有函数定义…(此处应插入一个“你跟这开玩笑呢”的图片)。所以,理论上应该还有个lib库之类的东西…

(所以报了LINK2019错误)

算了…不折腾了…搜到了EasyX库,想起来当时在VC++ 6.0上用的也是这个。

配置好头文件和库文件,但略显蛋疼的是,一些绘图函数的参数类型又不一样了。重新改…

还是拿上面那个举例,EasyX库下的graphics.h需要char类型

1
outtextxy(225, 220, (char)"TWO PHASE DLAGRAM\n");

(这样在指针间反复横跳…总感觉哪里不对劲…)

又比如

1
2
char my[5], mx[5];
outtextxy(X1 + 2 * i * xstep - 10, Y2 + 10, mx);

outtextxy()有两种重载形式

1
2
void outtextxy(int x, int y, LPCTSTR str);	// 在指定位置输出字符串
void outtextxy(int x, int y, TCHAR c); // 在指定位置输出字符

所以把原代码中的char类型强制转换为LPCTSTR类型。

1
outtextxy(X1 + 2 * i * xstep - 10, Y2 + 10, (LPCTSTR));

LPCTSTR类型先放一下,先说一下函数重载。

函数重载

度娘百科上说的还是挺清楚的。

C++允许在同一范围中声明几个功能类似的同名函数, 形参需要不同。

调用时会选择合适的函数进行调用。类似的还有运算符的重载,比如<<运算符,左移+输出。

函数重载这个概念在C++课上肯定是学过的,只是被课本和习题蒙蔽了,不知道是干什么的。在读Waifu2x的代码时,才发掘这是怎么一回事。

目的的话,为了方便吧,能想到的就是这个了。

继续graphics.h

有些函数变量的含义、个数都变了,照猫画虎地去改。

参照

1
void setlinestyle(int style, int thickness = 1, const DWORD *puserstyle = NULL, DWORD userstylecount = 0);	// 设置当前画线样式

1
setlinestyle(0, 3, 1);  //坐标轴几何参数

改成

1
setlinestyle(0, 3);  //坐标轴几何参数

以上这些绘图函数都是用来美化修饰的,其实参数含义什么的囫囵吞枣就可以,我也不是UI,但initgrah()这个函数还是要关注一下的。

1
2
// 绘图模式相关函数
HWND initgraph(int width, int height, int flag = NULL); // 初始化图形环境

上面是头文件中的声明,但我的原始代码画风有点不一样

1
2
int gdriver = DETECT, graphmode;
initgraph(&gdriver, &graphmode, "E:\\TurboC3cn\\BGI");

恕我孤陋寡闻,这样声明和初始化两个变量,真的是…

当初我改代码时,似乎也没有动这个地方…所以那个路径我当时是怎么编译通过的…orz…

按照度娘百科给的函数原型,应该更符合原始代码(因为度娘的词条似乎就是针对(原始的)graphics.h写的,而相比我用的EasyX库,至少对于针对VS2019版本的头文件,是不一样的)。

1
void far initgraph(int far *gdriver, int far *gmode, char *path);

嘛…不管了,反正就依照我现在用的头文件,把参数改一下,现在的参数含义还更好理解。

关于_T("黑体")的问题,似乎是解决了,这个没细看。

stdafx.h

这个stdafx.h不用管…注释掉就行…

C++标准的升级

我用的VS2019是支持c++14和c++17(虽然我还没搞明白默认的是哪个),有些陈旧的函数会报C4996错误,照着改就行。

getch() -> _getch()
gcvt() -> _gcvt() -> _gcvt_s()

结语

好像也就这么多了。虽然画出来的图还不正常…但至少不报错了…

上午改代码的时候,真有一种代码被下了毒的感觉…

收获有三点吧。

  • 理解了头文件、库文件的依赖关系。目前来看,对于稍大型的代码,是在头文件中写函数声明,然后把函数实现放在库文件中。在VS中的配置也更加熟练了(头文件位于\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\xxxx\include之类的地方)。
  • 明白了函数重载
  • 关于charstring等类型的关系和转换

但代码中仍有一些地方值得我去看,比如extern