C++STD::vector和STD::Basic字符串之间的根本区别是什么?
- 检查文档,它们有不同的接口。如果您指定了要解决的实际问题,那么答案也可能更具体。
- @基因:它们有不同的接口,但都实现了成为STL序列容器所必需的一切。
- @吉恩:我没有解决任何特定的问题,我只是好奇为什么我应该为各种目的选择其中一个或另一个:我不把一些额外的类似字符串的方法的存在作为基础。我也不认为绩效是最基本的。然而,迭代器的有效性,几个回复提到的肯定是。我有一个模糊的怀疑,一个字符串的数据类型必须有一个"类似于零"的值放在data()方法的末尾(从一个特性thingy中获得)。
- 仅供参考:字符串最初不是STL容器。在皮特·贝克尔的建议下,委员会的其余成员决定成立一个委员会。这使得它像矢量一样,消除了许多优化的可能性。回想起来,我认为皮特·贝克尔是对的。
- @YTTrill是不是只想讨论这个问题?这些答案中的一些似乎都很完整,有没有理由没有一个被接受?
- 我在寻找根本的差异。例如,元素析构函数调用的不存在并不是很好,因为vector也可以用适当的专门化和特征信息来实现这一点,事实上它可能是无关的,因为在现代C++编译器中调用一个平凡的析构函数应该优化(然后,循环优化)。
- 交换迭代器的失效在技术上是基本的,但看起来并不重要(您交换什么东西的频率是多少?).概念上,如果容器具有随机访问迭代器,并且必须连续存储,则迭代器必须是(同构于)指针,并且所有此类容器随后都是数组。
- 事实上,给定C++ 11约束,我预期主要的区别是,当输出到适当的流时,字符串不被转换,即元素代表它们自己并且连续输出。而向量可以打印"vector(char(63)、char(64)、char(65))"而不是"abc"。
Basic字符串不调用其元素的构造函数和析构函数。向量确实如此。
交换基本字符串会使迭代器失效(启用小字符串优化),交换向量则不会。
在C++ 03中,基本字符串内存可能不会被连续分配。向量总是连续的。此差异在C++0x[Strun.Adv]中删除:
The char-like objects in a basic_string object shall be stored contiguously
基本_字符串具有字符串操作接口。向量没有。
Basic字符串可以使用复制写策略(在C++ 11中)。向量不能。
非信徒相关引用:
[基本字符串]:
The class template basic_string conforms to the requirements for a Sequence Container (23.2.3), for a
Reversible Container (23.2), and for an Allocator-aware container (Table 99), except that basic_string
does not construct or destroy its elements using allocator_traits::construct and allocator_-
traits::destroy and that swap() for basic_string invalidates iterators. The iterators supported
by basic_string are random access iterators (24.2.7).
- @比利:在投反对票之前先看一下标准,我加了一句话。
- 据我所知,这句话不是来自"标准",而是来自C++0X草案。这很好,值得一提,但是你需要用"C++0x……"来限定它。
- @嗯,看起来你是对的。但是只有配方是不同的,C++ 98仍然说它只分配和释放元素。这一变化只是一个澄清。
- 那么在这个问题上有共识吗?单个ctor/dtor/mov/assign操作可能比bitblits更昂贵,所以有充分的理由希望字符串元素是pods,但这是必需的吗?难道POD和非POD病例不能根据一个特征用特殊分类来划分吗?或者至少常见的情况,如char?这对向量也不能吗?
- @顺便说一下,basic_string提供的元素默认分配只有一个命令,那就是resize,它"将新字符初始化为CharT()"。非信徒的例子:ideone.com/ujqzrm
- @约翰森·米:你使用basic_string是不符合规定的,所以你的行为是不明确的;在这种情况下,它恰好不调用拷贝目录。basic_string只对带有char_traits的类型有效,例如,char_traits::copy不需要调用构造函数。(所有需要提供的特性专业化都可以一致地实现char_traits::copy作为对memcpy的调用,因为它对char/wchar_t/char16_t/char32_t有效)
basic_string提供了编译器和标准库实现,在矢量上有一些自由:
"小字符串优化"对字符串有效,它允许实现在字符串较短时将实际字符串(而不是指向字符串的指针)存储在字符串对象中。沿着这条线的东西:
1 2 3 4 5 6 7 8 9
| 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中,EDCOX1(1)允许编译器/库供应商使用对数据的写入(可以保存在副本上),这对于EDCOX1 2是不允许的。在实践中,这一点曾经非常普遍,但由于它对多线程的影响,现在已经不那么普遍了。然而,无论哪种方式,您的代码都不能依赖于是否使用COW实现std::basic_string。C++ 11现在再次禁止这种行为。
在EDOCX1 1中也有一些辅助方法,但是大多数都是简单的,当然可以很容易地在EDOCX1,12的上面实现。
- 我不喜欢把(1)作为挑选或使用STD::String的原因,这是新标准中已经严格压缩的标准措辞的一个意想不到的副作用。(2)使用std::string是一个很好的理由,因为它使得从方法返回字符串非常有效(实际上没有成本),不幸的是,由于并行性的要求(尽管从阅读建议这样做的文章中可以看出,绳子最终会占据牛的壁炉架(我们必须等待并查看是可行的。
- @马丁:这是真的。Othmove语义消除了很多Cow实现加速的情况:)
关键的区别在于,当std::basic_string无法保存数据时,std::vector应将其数据保存在连续内存中。因此:
1 2 3 4 5 6 7 8 9
| 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++标准没有提供任何保证。
- 实际上它们都不是有效的。两个对象都没有任何大小,所以第一个元素实际上是一个超过了末尾的元素,您不能取消对该元素的引用。只有c_str()定义得很好。
- @基里尔:我的观点是,您的示例没有演示连续缓冲区方面。如果有人将x2解释为指向单个字符的指针(而不是指向以空结尾的C字符串的指针),那么代码是完全有效的。例如,有人可以做std::cout << *x2,一切都会好起来,但std::cout << *(x2 + 1)是无效的。@诺亚:哈哈——说得对。
- @诺亚,更新了这个例子。重点不在于初始化这些容器,所以我跳过了它。
- @基里尔:加上这个例子,就投了赞成票。
- 这个关键的区别在C++ 0x中被删除了,看看我的答案,所以它不是那么关键的区别。
- @比利,Thanx,用你的编辑,这个例子非常清楚地说明了这个问题。
- 这完全是次要的(以及意想不到的后果)。循环孔在C++0x中已被关闭,并且没有STL(在更新标准过程中由委员会测试)会因为相邻数据的假设而中断。
- @基里尔:您知道字符串不连续的任何实现吗?当他们讨论了对C++ 0x的EDCOX1第13条的更改时,委员会并没有这样做。这在标准中是很重要的,因为它鼓励了向量与字符串的稍微不同的用法(例如,如果您担心可移植性,您可能会选择不使用字符串作为读取缓冲区),但这不是实际实现之间的差异。
- @史提夫:相关:StutoPox.com /问题/ 2256160 / & Helip;
tldr:
strings被优化为只包含字符原语,
vectors可以包含原语或对象。
vector和string之间最显著的区别是vector能够正确地包含对象,string只在原语上工作。因此,vector提供的这些方法对于string使用原语是无用的:
向量::ESPACE
矢量::放回原处
矢量::~矢量
即使扩展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://stackoverflow.com/a/3555016/2642059,但是对于诸如long long这样的原语,需要编写char_traits的新专门化,以及专门化char_traits::eof和char_traits::not_eof的想法,而不只是使用vector。似乎不是最好的时间利用方式。
由于短字符串优化,迭代器被所有可能使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、EDOCX1 1〔50〕、EDOCX11〔51〕、EDOCX11〔51〕、EDOCX11〔41〕41〔41〔41〕、EDOCX11〔42〕、EDOCX11〔43〕、EDOCX11〔44〕44、string::replace、EDOCX11〔46〕46、EDOCX11〔46〕、EDOCX11〔46〕47、1〔52〕、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*的论点。
注意,这个答案是针对C++ 11编写的,所以EDCOX1×0的S需要被连续地分配。
std::string和std::vector之间的一个区别是,程序可以从以空结尾的字符串构造字符串,而使用向量则不能。
1 2
| std::string a ="hello"; // okay
std::vector<char> b ="goodbye"; // compiler error |
这通常使字符串更容易使用。
基本的_字符串提供了许多特定于字符串的比较选项。您是对的,因为底层内存管理接口非常相似,但是字符串包含许多额外的成员,比如c_str(),对于向量来说这是没有意义的。
- 我不认为有任何类似的事情,比如使用小字符串优化的向量。还没出去找,但我很确定它不在外面。它不会有用。事实是,这两件事完全不同。他们有不同的目的,因此往往是非常不同的实施,尽管天真的方法可能在两个方面都是相似的。
- 诺亚:所以我为什么说"接口"。
- 哪些比较是字符串特定的?给定元素数据类型在两种情况下都是可变的,像字典比较之类的东西对向量作为字符串的意义就相当大。当然,不区分大小写的比较是特定于字符串的,但字符串不会是多态的。
矢量是模拟数组的数据结构。它的内部实际上是一个(动态)数组。
基本的字符串类表示一系列字符。它包含序列的所有常规操作,另外,它还包含标准字符串操作,如搜索和连接。
可以使用vector保留所需的任何数据类型std::vector or or even std::vector< std::vector >,但basic_string只能用于表示"文本"。
- 我相信我能找到一个办法让一个满是双打或同样邪恶的东西的江户11〔0〕真正"工作"。
- 好吧,大多数实现都是在类似于vector的方面实现basic_string。定义了char_traits类的任何类型都将与std::basic_string一起使用,即使您组成了char_traits(如@noah's comment中所写)。
- @比利-不是真的。有些相同的技术可以很好地应用于这两个方面,但我从未见过用vector来实现basic_string。事实上,一个非常常见的实现,即使用MSVC++分发的实现,是非常不同的,因为它们使用小字符串优化(任何足够小的、适合于指针的东西都只停留在指向缓冲区的指针中,而不是分配一个)。
- @诺亚:如果你再看我的评论,它会说"有点像矢量",而不是"矢量"——这两个词很重要。(我的意思是它通常是某种形式的动态数组)
- 还不想回答我自己的问题……但是,新的字符串类型(如utf-8字符串)呢?这些将是较少的数组,不是吗?它们如何适应基本的_字符串?
- …我不知道在内存中以UTF-8(或任何其他可变宽度)编码存储字符的字符串类型的任何计划。就性能而言,这是个坏主意;你会失去对角色的随机访问。