c++项目入门
c++项目入门
本文基础:具备python项目开发经验,入坑c项目
基础概念:项目构建的四个流程:预处理 -> 编译 -> 组装 -> 链接
在C语言的编译过程中,四个步骤所作的工作
- 预处理(Preprocessing)
- 在这个阶段,预处理器处理源代码文件中以井号(#)开头的指令。
- 这包括宏定义的扩展(例如
#define
)、条件编译指令(例如#ifdef
、#ifndef
、#endif
)和文件包含指令(例如#include
)。 #include
指令导致相关的头文件内容被插入到源文件中,替换掉#include
指令本身。- 预处理器还会移除注释,并可能添加一些编译器需要的特殊行标记。
- 编译(Compilation)
- 编译器接收预处理后的代码,并将其转换成汇编语言。
- 在这个阶段,编译器会进行语法分析、语义分析、优化和生成目标代码。
- 它检查代码中的错误,并在找到错误时报告(如语法错误或类型不匹配)。
- 如果代码无误,它将生成汇编语言代码。
- 组装(Assembly)
- 汇编器取汇编代码作为输入,并将其转换成机器语言的目标代码。
生成.o文件
- 目标代码是一系列的机器语言指令,这些指令可以被处理器直接执行。
- 输出通常是对象文件,这些文件包含了机器语言代码和程序中定义的符号(如变量和函数)的地址信息。
- 汇编器取汇编代码作为输入,并将其转换成机器语言的目标代码。
- 链接(Linking)
- 链接器的工作是将一个或多个对象文件合并成一个单一的可执行文件。
- 在链接过程中,链接器解析多个对象文件之间的相互引用,比如一个文件中调用的另一个文件中定义的函数。
- 如果有库函数被使用,链接器还会将这些库代码合并到最终的可执行文件中。
- 链接器处理外部和静态变量的存储分配,并解决符号的最终地址。
- 最终产生的可执行文件包含了程序所需的所有代码和数据,准备被操作系统加载到内存中执行。
这个过程可以通过一系列的命令行工具手动完成,也可以通过集成开发环境(IDE)或者构建系统(如Make)自动化进行。在实际开发中,通常使用构建系统来管理这些步骤,因为它能够处理项目中的复杂依赖关系,并且可以自动化执行重复的构建任务。
生成和编译的区别:编译是将源代码翻译为机器代码,而生成是将编译好的机器代码构建成可执行文件。
cmake和makefile的关系:
hpp和.h文件的区别:一个为c,一个为cpp设计
网上说hpp声明和定义可以不分开,应该是忽悠人的。
头文件和源文件分离,是项目构建的常用做法,会方便后面。
方便什么?
项目运行一段时间后,发现一个函数的逻辑有问题,作为程序员的你想要更改某一个函数的逻辑。
你发现这个函数的实现在file1.cpp中,于是你重新编辑file1.cpp,然后重新编译。
但是file2.cpp,file3.cpp都include了file1.cpp,你file1.cpp变化了,后面所有文件都要重新编译,链接。
于是程序员们想出了一个方法,把函数的具体实现和外在调用接口分离,分别写在cpp和hpp里面。你更改cpp中某个function的代码逻辑,只要外在接口形式不变(hpp不变),就不用重新编译file2.cpp,file3.cpp等文件,只需要重新编译file1.cpp一个文件了!
编译完,再和之前已经存在的.o文件打包在一起链接,就可以生成新的项目。
cpp的包管理器vcpkg
有没有类似python pip的包依赖管理器呢?
答案是有的,比如vcpkg。
使用vcpkg管理C++项目依赖
安装和配置 vcpkg
克隆并安装 vcpkg
:
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
集成 vcpkg
到Visual Studio(仅需执行一次):
./vcpkg integrate install
安装库,如sfml
./vcpkg install sfml
如果使用cmake项目,需要在 CMakeLists.txt
中附加必要的语句:
# 设置vcpkg工具链文件
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake")
# 查找SFML库
find_package(SFML CONFIG REQUIRED COMPONENTS system window graphics network audio)
-
设置工具链文件:
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake")
这句设置了CMake工具链文件的路径,使CMake能够正确找到和使用通过
vcpkg
安装的库。 -
查找SFML库:
find_package(SFML CONFIG REQUIRED COMPONENTS system window graphics network audio)
这句查找并配置SFML库,确保项目使用所需的SFML组件。
3. 在项目代码中使用SFML
-
包含SFML头文件:
#include <SFML/Graphics.hpp> int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "SFML works!"); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(); window.display(); } return 0; }
- 使用
#include <SFML/Graphics.hpp>
而非#include "SFML/Graphics.hpp"
,因为前者用于查找标准库或外部库中的头文件,而后者用于查找项目本地的头文件。
- 使用
总结
- 工具链文件设置:通过设置
CMAKE_TOOLCHAIN_FILE
,CMake能够正确找到并使用通过vcpkg
安装的库。 - 库查找和配置:使用
find_package
查找并配置所需的库组件,确保项目使用正确的库版本和组件。 - 项目级别的依赖管理:通过克隆和配置本地
vcpkg
实例,可以实现每个项目独立的依赖管理。 - 头文件包含:使用标准的
#include <...>
语法包含外部库的头文件,确保编译器正确查找和使用这些文件。
通过这些步骤和配置,可以有效地管理C++项目的依赖,使得项目更加模块化和易于维护。
Q&A
- sfml的贴图不显示问题
- resources内的文件加载问题
路径是看编译之后的可执行文件的,所以,要把resources文件copy到对应输出文件夹
// CMakeLists.txt
# Copy resources to the build directory
add_custom_command(
TARGET BreakoutGame POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/resources $<TARGET_FILE_DIR:BreakoutGame>/resources
)
- 如何根据需求选择合适的库?有什么原则吗?
选择合适的库和包对于开发项目非常重要,资深程序员在做出选择时通常会考虑以下几个方面的因素。以开发一个平面游戏为例,我们来详细探讨一下: 1. 功能需求 首先要明确游戏的功能需求,例如:
渲染2D图形 处理用户输入 音效和音乐 物理引擎(如需要碰撞检测) 网络功能(如多人游戏) 2. 兼容性 确保库与目标平台兼容。如果游戏要在Windows、macOS和Linux上运行,就需要选择跨平台的库。
-
社区和支持 选择有活跃社区和良好文档的库很重要。这样在遇到问题时,可以更快地找到解决方案。活跃的社区意味着库会有较快的更新和bug修复。 中国人偏向于国产库
-
性能 游戏开发对性能要求较高,需要选择性能优越且资源开销较低的库。例如渲染库需要高效处理大量图形。
-
易用性 库的API设计是否友好,是否易于集成到现有项目中,也是选择时的重要考虑因素。 比如我是新手,就用API简单的库
-
许可证 确保库的许可证与项目兼容,避免法律纠纷。例如,GPL许可证要求项目也开源,而MIT和BSD许可证则较为宽松。
-
vcpkg项目级别包管理的一点思路
- 直接把vcpkg目录复制到项目下
-
改变了vcpkg路径之后,要做哪两件事?
- 改变cmakelist
- 重新运行./bootstrap-vcpkg.bat,如果没有,将会提示找不到dll,可能是./bootstrap-vcpkg.bat的作用之一就是设置正确的运行时环境?不太懂。
遇到font无法加载的问题
问题在于使用了release的库文件来构建debug
vcpkg会帮你管理依赖,自己手动装库很繁琐,有层层依赖
Cmake相关
构建时需要把所需要的资源文件夹和动态库文件一起拷贝到生成目标文件夹,才能被识别到
以.exe的位置为准。
杂项:
- sfml是一个2d游戏库,类似的有cocos2dx,这是一个很出名的游戏引擎
- easyx也是一个图形库,比起上面两个,场景有什么不同?
参考: