在C++中,std::vector和std::basic_string之间有什么本质区别(如果有的话)?
在C++中,std::vector和std::basic_string之间有什么本质区别(如果有的话)?
basic_string 不调用其元素的构造和析构函数,但 vector 调用。
交换 basic_string 会使迭代器无效(启用小字符串优化),而交换 vector 不会。
C++03 中,basic_string 内存可能不是连续分配的,vector 总是连续的。这种差异在 C++0x [string.require] 中被移除:
一个 basic_string 对象中的类 char 对象必须是连续存储的。
basic_string 具有字符串操作接口,而 vector 没有。
在 C++11 之前,basic_string 可能使用写时复制策略,而 vector 不能。
相关名言:
[basic.string]:
类模板 basic_string 符合序列容器(23.2.3)、可逆容器(23.2)和支持分配器的容器的要求(表99),但 basic_string 不使用 allocator_traits::construct 和 allocator_traits::destroy 构造或销毁其元素,并且 basic_string 的 swap() 使迭代器无效。basic_string 支持的迭代器是随机访问迭代器(24.2.7)。
basic_string
提供的默认元素分配仅有一个命令,即 resize
,它会“将新字符初始化为 CharT()
”。对于那些不信的人,这里有一个例子:http://ideone.com/UjQzrM - Jonathan Meebasic_string
不符合规范,因此会导致未定义的行为;在这种情况下不调用复制构造函数。basic_string
仅适用于具有 char_traits<T>
的类型,并且例如 char_traits<T>::copy
不需要调用构造函数。(所有必须提供的 char_traits 特化都可以将 char_traits<T>::copy
实现为对 memcpy
的调用,因为对于 char/wchar_t/char16_t/char32_t 这是有效的) - Billy ONealbasic_string
给编译器和标准库的实现提供了比vector更多的自由:
"小字符串优化"在字符串上是有效的,它允许实现在字符串对象中存储实际字符串而不是指向字符串的指针,当字符串很短时。大致如下:
class string
{
size_t length;
union
{
char * usedWhenStringIsLong;
char usedWhenStringIsShort[sizeof(char*)];
};
};
在C++03中,基础数组不一定是连续的。按照 "绳索" 等方式实现 basic_string
是符合当前标准的。 (虽然没有人这样做,因为那样会使成员函数 std::basic_string::c_str()
和 std::basic_string::data()
实现过于昂贵。)
不过,C++11 现在禁止了这种行为。
在 C++03 中,basic_string
允许编译器/库供应商对数据使用写时复制 (可节省复制操作),而对于 std::vector
则不允许。实际上,这曾经更为普遍,但现在由于它对多线程的影响较大,所以越来越少见。不管怎样,你的代码不能依赖于是否使用了 COW 来实现 std::basic_string
。
C++11 再次禁止了这种行为。
basic_string
还附带了一些辅助方法,但大多数都很简单,当然也可以轻松地在 vector
上实现。
关键区别在于std::vector
应该在连续的内存中保留其数据,而std::basic_string
则不必如此。因此:
std::vector<char> v( 'a', 3 );
char* x = &v[0]; // valid
std::basic_string<char> s( "aaa" );
char* x2 = &s[0]; // doesn't point to continuous buffer
//For example, the behavior of
std::cout << *(x2+1);
//is undefined.
const char* x3 = s.c_str(); // valid
x2
指向连续的缓冲区,因为 C++ 标准没有给出任何保证。 - Kirill V. Lyadvinskyx2
解释为指向单个字符的指针(而不是指向以空字符结尾的C字符串的指针),那么代码就是完全有效的。例如,有人可以执行std::cout << *x2
,一切都会很好,但是std::cout << *(x2 + 1)
将是无效的。@Noah:哈哈--好观点。 - Billy ONealstring
只优化了包含字符原语的情况,而 vector
可以包含原语或对象。vector
和 string
的主要区别在于,vector
可以正确地包含对象,而 string
仅适用于原语。因此,vector
提供了以下对于只处理原语的 string
来说是无用的方法:
即使扩展 string
,也无法正确处理对象,因为它缺少析构函数。这不应被视为缺点,因为它允许在优化方面比 vector
更显著,因为 string
可以:
char_traits
,是string
的模板参数之一,用于定义如何对包含的基元进行操作(其中仅实现了char
、wchar_t
、char16_t
和char32_t
:http://en.cppreference.com/w/cpp/string/char_traits)。char_traits::copy
, char_traits::move
和 char_traits::assign
,这明显意味着直接赋值将被使用,而不是构造或销毁,这对于原始类型来说是更可取的。所有这些专门化对 string
的额外缺点是:
char
、wchar_t
、char16_t
或char32_t
原始类型。显然,大小不超过32位的原始类型可以使用相应大小的char_type
:https://dev59.com/dVsW5IYBdhLWcg3wCTaB#35555016,但对于像long long
这样的原始类型,需要编写一个新的char_traits
专业化,而且专业化char_traits::eof
和char_traits::not_eof
的想法,而不仅仅是使用vector<long long>
,似乎不是最好的时间利用。vector
迭代器无效的操作都会使迭代器无效,但string
迭代器还会被string::swap
和string::operator=
无效。vector
和string
接口的其他差异:
string::data
: 为什么 std::string.data() 不提供可变的 char*?string
提供了与 vector
不可用的单词处理功能:string::c_str
, string::length
, string::append
, string::operator+=
, string::compare
, string::replace
, string::substr
, string::copy
, string::find
, string::rfind
, string::find_first_of
, string::find_first_not_of
, string::flind_last_of
, string::find_last_not_of
, string::operator+
, string::operator>>
, string::operator<<
, string::stoi
, string::stol
, string::stoll
, string::stoul
, string::stoull
, string::stof
, string::stod
, string::stold
, stirng::to_string
, string::to_wstring
vector
接受另一个 vector
的参数,string
都接受一个 string
或一个 char*
string
进行连续分配。std::string
和std::vector
之间的一个区别是,程序可以从以空字符结尾的字符串构造一个字符串,而对于向量则不行。
std::string a = "hello"; // okay
std::vector<char> b = "goodbye"; // compiler error
这通常使得字符串更易于处理。
向量是一种模拟数组的数据结构。在其深层内部,它实际上是一个(动态)数组。
basic_string类表示字符序列。它包含所有序列的常规操作,并且还包含标准字符串操作,例如搜索和连接。
您可以使用向量来保留任何数据类型,例如std::vector<int>
或<float>
甚至是std::vector< std::vector<T> >
,但是basic_string
只能用于表示“文本”。
basic_string
实际上“工作”。 - Edward Strangebasic_string
。只要定义了一个char_traits
类,任何类型都可以与std::basic_string
一起使用,即使你编写了一个char_traits<double>
(如@Noah的评论中所述)。 - Billy ONealvector
为基础实现的basic_string
。事实上,一个非常常见的实现,即MSVC++附带的实现,非常不同,因为它们使用小字符串优化(任何足够小以适合指针的东西都只是被粘贴到指向缓冲区的指针中,而不是分配一个)。 - Edward Strangebasic_string提供了许多特定于字符串的比较选项。你说得没错,底层内存管理接口非常相似,但是string包含许多其他成员,如c_str(),对于向量来说毫无意义。