程序运行时报错,查看core文件,发现basic_string.h报错
经过排查发现
编译文件CMakeLists中设置了_GLIBCXX_USE_CXX11_ABI = 0
_GLIBCXX_USE_CXX11_ABI
是C++中的编译宏, 用来控制string及list使用的版本。 该宏仅在GCC5.1及后续版本中有效。
string及list的新版本符号是std::__cxx11::basic_string 及 std::__cxx11::list。
示例:
#include<string>
void test(std::string &s)
{
s = "Hello World";
return;
}
编译使用-D_GLIBCXX_USE_CXX11_ABI=1
test>$ g++ -c -D_GLIBCXX_USE_CXX11_ABI=1 test.c
test>$ nm test.o
0000000000000000 T _Z4testRNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEaSEPKc
test(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
test>$
编译使用-D_GLIBCXX_USE_CXX11_ABI=0
test>$ g++ -c -D_GLIBCXX_USE_CXX11_ABI=0 test.cc
test>$ nm test.o
0000000000000000 T _Z4testRSs
U _ZNSsaSEPKc
test(std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
test>$
可以看到,-D_GLIBCXX_USE_CXX11_ABI=1
编译后的符号是 std::__cxx11::basic_string
,而-D_GLIBCXX_USE_CXX11_ABI=0
编译后的符号是 std::basic_string
。
由string
版本不同引发的链接问题
从上节中看到,不同版本的string
具有不同的符号。若程序使用了不同版本的库,则会有找不到符号的问题。举例如下:
#include<iostream>
#include<string>
using namespace std;
void a(string s)
{
cout << s << endl;
return ;
}
test>$ g++ -c -D_GLIBCXX_USE_CXX11_ABI=1 a.cc
test>$ nm a.o | grep " T "
0000000000000000 T _Z1aNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
test>$ c++filt _Z1aNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
a(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
test>$
主程序:
#include<iostream>
#include<string>
using namespace std;
void a(string s);
int main()
{
a("12345");
return 0;
}
test>$ g++ -c -D_GLIBCXX_USE_CXX11_ABI=0 main.cc
test>$ nm main.o | grep _Z1aSs
U _Z1aSs
test>$ c++filt _Z1aSs
a(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)
test>$
可以看到,main.o依赖的是std::basic_string, 而a.o中是std::__cxx11::basic_string,将main.o与a.o链接起来会找不到符号,如下:
test>$ g++ main.o a.o
main.o:在函数‘main’中:
main.cc:(.text+0x32):对‘a(std::string)’未定义的引用
collect2: 错误:ld 返回 1
test>$
项目中是因为编译的动态库是设置的为-D_GLIBCXX_USE_CXX11_ABI=0 ,但是整个运行主程序是-D_GLIBCXX_USE_CXX11_ABI=1,所以出现开头的崩溃
为什么实现两个版本的string?
在c++11之前的版本,允许string Copy On Write(即string COW)功能,新的C++11版本禁用了此项功能。在新的 C++11中新增了__cxx11这个命名空间以示区别。
简单来说, COW 功能是在string对象赋值时, 两个string对象共享内存, 只有在其中一个对象被修改时才会申请新的内存。
关于string COW功能,看如下的代码:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s("str");
string s1 = s;
char *p = const_cast<char *>(s1.data());
p[2] = '\0';
cout << s << endl;
cout << s1 << endl;
return 0;
}
// 方式一
test>$ g++ -D_GLIBCXX_USE_CXX11_ABI=1 test.cc
test>$ ./a.out
str
st
// 方式二
test>$ g++ -D_GLIBCXX_USE_CXX11_ABI=0 test.cc
test>$ ./a.out
st
st
可以看到, -D_GLIBCXX_USE_CXX11_ABI=0
编译时修改s1.data
,s
也被改变了, 说明s
和s1
共享了内存。 -D_GLIBCXX_USE_CXX11_ABI=1
时 s
没改变,说明s
和s1
没有共享内存。