似乎每个人使用C++三方库都会遇到苦难,但本质其实很简单,只需要理解链接过程到底发生了什么。对任何概念和术语不完全清楚,都可以求助大语言模型。
众所周知,编译和链接是两个过程,同一个标识符可以多次声明,但只能有一个实现。编译只需要声明,链接时才会检查实现。
函数重载不是同一个标识符吗?答案是每一个重载函数确实有一个唯一的标识符,函数重载会在编译时重整化,每一个重载函数生成一个唯一的内部名称
std::cout << "hello" << std::endl;
这是一条语句将其改写为一个函数
void hello(){std::cout << "hello" << std::endl;}
hello();
声明并定义后在代码中像这样即可调用hello函数了
要在多个文件中使用hello函数该怎么做,记住声明可以多次,但只能有一个定义
自然,是在一个源文件中定义,在其他源文件中只声明,或者使用头文件
在头文件 hello.h 中声明hello函数,严格来说是一个无参数无返回值的hello函数
void hello()
通常地,hello.cpp中编写实现,但记住,这只是一个惯例,定义可以在任何源文件,但只能定义一次
void hello(){std::cout << "hello" << std::endl;}
#include "hello.h"
相当于将
void hello();
粘贴到文件中,hello函数声明后就可以使用函数了
要在多个项目中使用hello函数怎么做?可以将头文件和源文件复制到每一个项目中
或者将hello函数生成为库,这样每个项目只需要头文件获取声明和一个编译完成的库文件获取定义了
为什么?因为使用库只需要声明,在源代码中包含库的源文件只会造成混乱,如果不做修改,没有必要关心这些声明的具体定义,使用这些库只需要头文件,在源文件中引入头文件,声明标识符之后就可以使用了。
同样地,使用三方库需要引入头文件获取声明,实现在哪里?lib或DLL,取决于静态链接还是动态链接
当然可以将所有需要的头文件复制到项目目录下并包含于项目中,也可以使用完整路径或相对路径引入头文件
有什么方法能让这些头文件像标准库头文件一样引入呢?这就是附加包含目录的作用
你可以告诉IDE,附加包含目录的目录下,有项目需要的头文件,使用include 指令时请检查这些目录。
链接器怎么知道lib或DLL文件在哪里?
答案是不知道,当然通常会有一个默认路径,其中包含了系统和标准库相关的库,这也被称之为环境变量。
你需要告诉链接器你的库文件在哪里,准确地说是哪个路径下,这就是附加库目录的作用,
既然已经知道了目录,那为什么又需要附加依赖项呢?
答案是因为附加库目录下可能有多个库文件,链接器需要知道要链接哪些库文件。现实是不能像其他语言按需自动识别,自动链接对应库文件。
在 VS2022 中使用静态库时,需要同时设置附加库目录和附加依赖项,这两个设置有着不同的作用和目的:
附加库目录
• 作用与原理:附加库目录用于告诉编译器和链接器去哪里查找静态库文件(.lib文件)。它类似于设置一个搜索路径,当链接器在寻找所需的静态库时,会在该目录下进行搜索。但它并不完全等同于设置环境变量让链接器搜索指定lib文件。虽然设置环境变量也可以让链接器找到库文件,但这种方式可能会影响整个系统的环境,而且不够灵活和项目特定化。而在 VS2022 中通过设置附加库目录,可以针对每个项目进行独立的配置,更方便管理和维护。
• 示例:如果你的静态库文件位于项目目录下的libs文件夹中,你就需要将该libs文件夹的路径添加到附加库目录中,这样链接器才能找到对应的.lib文件。
附加依赖项
• 作用与原理:附加依赖项则是明确指定项目所依赖的静态库文件名。它的作用是告诉链接器在链接过程中需要将哪些具体的静态库文件链接到可执行文件中。可以理解为指定链接参数的一部分,它具体指定了要链接的库文件的名称,而不仅仅是库文件所在的目录。
• 示例:如果你的项目依赖于mylib.lib和anotherlib.lib这两个静态库,你就需要在附加依赖项中分别添加这两个库的文件名。这样链接器在链接时就会将这两个库文件中的代码和数据与你的项目代码进行合并,生成最终的可执行文件。
std::cout << "hello" << std::endl;
这是一条语句,cout endl 在头文件iostream中声明
#include <iostream> 本质上就是复制粘贴
因此源文件获得了cout endl 的声明