demo工程的目录结构:
HaypinsMBP:trainCmake haypin$ tree .
.
├── C2.cpp
├── C2.h
├── CMakeLists.txt
├── Strclass.cpp
├── Strclass.h
├── build
│ └── build.sh
├── main.cpp
└── temp.cpp
1 directory, 8 files
C2.h:
#pragma once
#include<string>
class C2{
private:
std::string m_str;
public:
C2(std::string val);
~C2(void);
std::string Getm_str();
};
C2.cpp:
#include <iostream>
#include "C2.h"
using namespace std;
string globvar2="learn harder!";
C2::C2(std::string val ):m_str(val){
// m_str=val;
};
C2::~C2(){}
std::string C2::Getm_str(){
return m_str;
};
Strclass.h:
#pragma once
#include<string>
// class C2;
#include "C2.h"
using namespace std;
class Cstrclass{
private:
static string sattr;
string m_str;
C2 m_C2;
//如果只是在头文件单独声明class C2则cmake编译报错:error: field has incomplete type
public:
Cstrclass(string val,string val2);
~Cstrclass(void);
static string staticMeth();//静态方法只能操作类静态字段与其他静态方法,相当于Python的类方法
string Meth();//方法也可以操作全局作用域的变量,此时相当于Python的静态方法
void print();
};
Strclass.cpp:
#include <iostream>
// #include "C2.h"
#include "Strclass.h"
string globvar="haha";
string Cstrclass::sattr="heihei";
//类静态字段必须在全局作用域初始化,扮演着全局作用域变量的角色,只不过是封装在类内
Cstrclass::Cstrclass(string val ,string val2):m_str(val) \
,m_C2(val2){
// m_str=val;
};
Cstrclass::~Cstrclass(){};
string Cstrclass::staticMeth(){
return sattr;
};
string Cstrclass::Meth(){
return globvar;
};
void Cstrclass::print(){
cout<<m_str<<"-"<<m_C2.Getm_str()<<"\n";
};
以及"单独"的temp.cpp:
#include<string>
std::string globvar3("brave");
extern const std::string globcstr("glob const string");
main.cpp:
#include <iostream>
#include <string>
#include "Strclass.h"
using namespace std;
struct SubType{
string name;
SubType(string val){
name=val;
};
string Name(){
return name;
};
};
template <typename T>
string Getname(T obj){
return obj.Name(); //模板类T的实例需要提供Name()方法
}
extern string globvar; //定义在Strclass.cpp中,也在CMakeLists.txt的set(SOURCE ...)中
extern string globvar2; //定义在C2.cpp中,也在CMakeLists.txt的set(SOURCE ...)中
extern string globvar3; //定义在temp.cpp中,也在CMakeLists.txt的set(SOURCE ...)中
extern const std::string globcstr; //定义在temp.cpp中,也在CMakeLists.txt的set(SOURCE ...)中
int main(int argc,char *argv[]){
SubType obj=SubType("ahah");
cout<<Getname(obj)<<"\n"; //ahah
Cstrclass objcstr=Cstrclass("heihei","haha");
cout<<objcstr.staticMeth()<<"\n"; //通过实例调用类静态方法
cout<<Cstrclass::staticMeth()<<"\n";//通过类名调用类静态方法
objcstr.print();
cout<<globvar<<"\n";
cout<<globvar2<<"\n";
cout<<globvar3<<"\n";
}
其中extern声明的globvar定义在Strclass.cpp中,Strclass.h被main.cpp直接包含;globvar2定义在C2.cpp中,C2.h被Strclass.h直接包含;globvar3定义在temp.cpp中,temp.cpp不被main.cpp直接或间接包含;三个全局变量定义所在的文件都被CMakeLists.txt的set(SOURCE ....),也就是设置源文件的语法所包含,从而在编译extern声明时能在这些源文件中进行名字查找。
CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(main)
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -g)
set(SOURCE main.cpp Strclass.h Strclass.cpp C2.h C2.cpp temp.cpp)
add_executable(main ${SOURCE})
./build/build.sh:
#!/bin/bash
cd build
cmake ..
make -j8
mv main ../
编译执行:
HaypinsMBP:trainCmake haypin$ sudo bash ./build/build.sh
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/haypin/VScode/cpptest/trainCmake/build
Scanning dependencies of target main
[ 20%] Building CXX object CMakeFiles/main.dir/temp.cpp.o
[ 40%] Linking CXX executable main
[100%] Built target main
HaypinsMBP:trainCmake haypin$ ./main
ahah
heihei
heihei
heihei-haha
haha
learn harder!
brave
HaypinsMBP:trainCmake haypin$
最典型的是temp.cpp中的globvar3,temp.cpp没有被任何其他文件所包含,只出现在CMakeLists.txt的set(SOURCE ...)中,cmake编译时才会在作为源文件的temp.cpp中进行"extern string globvar3;"的名字查找。当注释掉CMakeLists.txt的set(SOURCE ...)中的temp.cpp后再编译,则会报未定义的符号错:
[ 25%] Linking CXX executable main
Undefined symbols for architecture x86_64:
"_globvar3", referenced from:
_main in main.cpp.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]: *** [main] Error 1
make[1]: *** [CMakeFiles/main.dir/all] Error 2
make: *** [all] Error 2
mv: main: No such file or directory
HaypinsMBP:trainCmake haypin$
并且,extern外部声明变量不能在源文件中重复定义,比如当在Strclass.cpp中重复定义globvar3后编译,会报重复的符号错:
[ 20%] Linking CXX executable main
duplicate symbol '_globvar3' in:
CMakeFiles/main.dir/Strclass.cpp.o
CMakeFiles/main.dir/temp.cpp.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [main] Error 1
make[1]: *** [CMakeFiles/main.dir/all] Error 2
make: *** [all] Error 2
mv: main: No such file or directory
HaypinsMBP:trainCmake haypin$
再补充一个知识点:static全局变量具有文件作用域,也就是说static全局变量只在其定义的文件内可见,对其他文件不可见。从而,如果在Strclass.cpp中重复定义globvar3但加以static修饰,则main.cpp中的extern std::string globvar3从源文件进行名字查找时会屏蔽调Strclass.cpp的static std::string globvar3,并能正确查找到temp.cpp中的std::string globvar3;
再补充一个知识点:来自《C++ primer》(第五版)P54:对const常量再文件间共享,也就是只在一个文件中定义const,而再其他多个文件中声明并使用它。此时对该const常量不管是声明还是定义都要添加extern关键字,也就是demo中的globcstr。