1、C++编译链接的理解
首先,C++链接的逻辑是:对构建目标target,其int main()入口函数所在的文件通过#include "myfunc_class.h"包含函数或类的声明,这些头文件内也可以写有实现从而都不需要.cpp文件,但这样做就违背了“在头文件中只包含声明使头文件内容紧凑清晰”的初衷,所以一般会在对应的"myfunc_class.cpp"文件中写有实现。然后这些“依赖文件”又可以有其他依赖文件,从而形成如下的拓扑依赖关系:
main.cpp<——myfunc_class.h(myfunc_class.cpp)<——myfunc_class3.h(myfunc_class3.cpp)
/\
|
myfunc_class2.h,myfunc_class2.cpp<——myfunc_class4.h(myfunc_class4.cpp)
g++编译器首先将#include "myfunc_class.h"包含的头文件内联地插入到包含处,但#include "myfunc_class.h"还有另一层意思是当前main.cpp可能需要"myfunc_class.cpp"文件中的声明(如果有myfunc_class.cpp的话)。最终每个.cpp文件都分别编译成.o中间文件,此时的拓扑依赖关系:
main.o<——myfunc_class.o<——myfunc_class3.o
/\
|
myfunc_class2.o<——mfunc_class4.o
接下来就是链接了,拓扑关系是明确了,但编译时还是需要自己编写CMakeLists.txt的
add_executable(mybin main.cpp myfunc_class.cpp myfunc_class2.cpp myfunc_class3.cpp myfunc_class4.cpp)
明确参与链接的.cpp文件(书写顺序无先后)。此时如果main.cpp或某中间myfunc_class.cpp虽然#include "myfunc_class3.h"但并没有使用"myfunc_class3.cpp"中实现的任何函数或类方法,那add_executable()中可以不用写入myfunc_class3.cpp,写了也没关系。
如果在add_executable()中缺少了某个在调用链中使用了其函数|类方法定义的myfunc_class4.cpp,那cmake --build .编译时会链接报错未定义的符号:
HaypinsMBP:build haypin$ cmake --build .
Scanning dependencies of target MathFunctions
[ 25%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.o
[ 50%] Linking CXX static library libMathFunctions.a
[ 50%] Built target MathFunctions
Scanning dependencies of target Tutorial
[ 75%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.o
[100%] Linking CXX executable Tutorial
Undefined symbols for architecture x86_64:
"OtherFunc()", referenced from:
_main in tutorial.cxx.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [Tutorial] Error 1
make[1]: *** [CMakeFiles/Tutorial.dir/all] Error 2
make: *** [all] Error 2
HaypinsMBP:build haypin$
2、static修饰全局函数
static修饰全局函数:https://blog.youkuaiyun.com/yesyes120/article/details/79519753
上栗子:
otherFunc.h:
static void OtherFunc();
otherFunc.cpp:
#include<iostream>
#include "otherFunc.h"
// static void OtherFunc(){
void OtherFunc(){
std::cout<<"calling OtherFunc() in otherFunc.cpp"<<std::endl;
}
tutorial.cxx:
#include "otherFunc.h"
int main(int argc, char* argv[]){
OtherFunc();
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
add_executable(Tutorial otherFunc.cpp tutorial.cxx)
此时编译会先警告:
/Users/haypin/cmake_tutorial/Step5/otherFunc.h:1:13: warning: function 'OtherFunc' has internal linkage but is not defined
[-Wundefined-internal]
static void OtherFunc();
然后在链接时报未定义的符号,因为tutorial.cxx有#include "otherFunc.h",并且otherFunc.h中的OtherFunc()带static修饰,编译处理文本包含后,则tutorial.cxx中链接OtherFunc()时会在tutorial.cxx文件作用域内部查找实现,从而因为找不到定义报错未定义的符号。注意此时并不会链接otherFunc.cpp中的extern版本的OtherFunc()定义,因为otherFunc.cpp与tutorial.cxx是不同的文件作用域。
[100%] Linking CXX executable Tutorial
Undefined symbols for architecture x86_64:
"OtherFunc()", referenced from:
_main in tutorial.cxx.o
ld: symbol(s) not found for architecture x86_64
将otherFunc.h中OtherFunc()去掉static后可以成功编译链接,此时otherFunc.cpp中的OtherFunc()默认是extern的,可以被tutorial.xcc链接到。
2、static限定文件作用域的实现犯法
如果:
otherFunc.h:
void OtherFunc();
otherFunc.cpp:
#include<iostream>
#include "otherFunc.h"
static void OtherFunc(){
// extern void OtherFunc(){
std::cout<<"calling OtherFunc() in otherFunc.cpp"<<std::endl;
}
则报编译错:
[ 60%] Building CXX object CMakeFiles/Tutorial.dir/otherFunc.cpp.o
/Users/haypin/cmake_tutorial/Step5/otherFunc.cpp:3:13: error: static declaration of 'OtherFunc' follows non-static declaration
static void OtherFunc(){
^
/Users/haypin/cmake_tutorial/Step5/otherFunc.h:1:7: note: previous declaration is here
void OtherFunc();
^
1 error generated.
也就是不允许在extern(non-static)的默认声明后再实现static的同名函数,编译不允许。由此杜绝了tutorial.cxx在#include "otherFunc.h"后链接otherFunc.cpp中定义的static函数,static限定文件作用域在是在编译时禁止"static declaration of 'OtherFunc' follows non-static declaration"实现的。
所以,static声明函数或类方法的所谓“限定文件作用域”本质上就是使被编译到otherFunc.o中间文件中的被修饰函数或方法不能被其他main.o中间文件链接到,此时,#include了被修饰函数或方法所在otherFunc.h头文件的那些.cpp会在编译链接otherFunc()函数时报错未定义的函数,因为链接不到其定义。