文章

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

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

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

一、埋下伏笔

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

新建项.png

  • 填好名称ResourceManager后,我手快直接便点了添加(A)按钮,文件便创建成功了,我连忙将后缀改回.hpp(由于VS新建项无法选择.hpp后缀,所以我一般都是先新建.h文件然后将其改为.hpp,并删除其默认添加的#pragma once并改为#ifndef形式头文件守卫,我喜欢这样)
  • 离开物理路径,我发现这个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 进行授权