文章

关于VS中新建项的初始后缀名的重要性

当在VisualStudio中<新建项>一个代码文件时,其后缀若是源文件(.cpp),那么即便将后缀改为头文件的后缀(.h/.hpp)并将其拖入头文件筛选器,编译器也依旧会将其当作源文件编译,导致项目在编译阶段就产生莫名其妙的Linking错误,本文中使用的是VS2022

关于VS中新建项的初始后缀名的重要性

一、写在前面

  • 我是从未来回来的,我突然发现我当时遇到的这个问题其实很简单,所以特此将新增的内容写在前面以作为省流版本
  • 你只需要注意在VS中,遇到类似我遇到的那种莫名其妙的问题时,可以排查一下是否遇到了脚本文件的后缀名或图标和其被编译时的属性不符(作为源文件或头文件)的问题,如下图所示

脚本属性与图标之差异.png

  • 后续的博客内容是我当时遇到某个源于此的问题时的排查过程,最终的确发现了配置文件.vcxproj中的问题所在,但想要解决问题并不需要将文件删除后重新写或是直接修改配置文件,而是只需要在VS中右键脚本,选择属性,然后更改一下其中的项类型使其与其后缀相符即可

二、埋下伏笔

  • 我在Visual Studio的物理路径下(提示:按下VS顶部选项栏的项目/显示所有文件按钮即可查看并维护项目的物理路径)准备新建一个头文件

新建项.png

  • 填好名称ResourceManager后,我手快直接便点了添加(A)按钮,文件便创建成功了,我连忙将后缀改回.hpp(由于VS新建项无法选择.hpp后缀,所以我一般都是先新建.h文件然后将其改为.hpp,这是因为在此时我的类声明和定义都存在这一个文件内,以此与仅含声明的.h头文件进行区分)
  • 离开物理路径,我发现这个ResourceManager.hpp乖巧地躺在源文件的筛选器目录下,我并没有在意,将其拖入头文件筛选器下,此时的项目结构如下(可以看到这个小可爱的左侧图标赫然显示的是头文件类型,气死我了)

项目结构.png

  • 然后我开始了编程,最终实现完成了该类,其结构如下(注意函数名,对比后文的报错信息)

游戏资源管理器类内容.png

三、问题发作

  • 然后我运行包含程序入口点main函数的Main.cpp源文件(该文件实际上包含了ResourceManager.hpp,记住这一点)

主函数内容.png

  • 然后就出现了下面显示的链接错误(注意错误函数的名称),即Linker在链接各编译单元.obj时发生了函数的重定义错误,而产生错误的源头即源于ResourceManager类的成员方法

链接错误.png

  • 我反复检查ResourceManager.hpp内的头文件守卫,完全没有任何问题,根本不可能存在重定义的问题,我百思不得其解
1
2
3
4
5
6
#ifndef _RESOURCE_MANAGER_HPP_
#define _RESOURCE_MANAGER_HPP_

...

#endif

四、发现端倪

  • 我们知道普通头文件是无法使用Ctrl + F7进行单文件编译的,如下图的红框内图标所示

gamemanager界面.png

  • 然后我们看ResourceManager.hpp文件

  • 气笑了,问题就出在这里,我将该文件删除,新建项一个.h头文件并改后缀为.hpp后,将内容转移至新文件内,编译Main.cpp,没有报任何的链接错误

五、问题原因

  • 推送到远程仓库后我查看代码差异,果不其然,此问题与.vcxproj后缀的文件有关,关于此文件的结构和作用请自行谷歌

差异对比.png

  • 上文我们也可以看到,筛选器内文件类型的图标是会骗人

差异对比2.png

  • 可见C++的脚本文件的后缀名的意义大抵仅仅是一种习惯性的规范,理论上无论是什么后缀名,我们都可以规定编译器该如何对其进行处理(类似地,VSCode中就可以通过修改launch.jsontasks.json文件对编译规则进行规定)
本文由作者按照 CC BY-NC-SA 4.0 进行授权