STL - 字符串是否是向量?

10
我在一些问答中遇到了这样一个问题: “字符串是向量吗?如果是,以什么方式?如果不是,则为什么?” 它们都可以随机访问内容。但是字符串有一些向量没有的方法。同时它也可能具有引用计数。因此,显然字符串并不完全是一个向量(typedef string vector)。 是否已知有实现使string: public vector <char>? 如果没有,那么不实现的原因是什么?

String有一些方法是Vector没有的吗?反过来也是一样的。 - Amirhossein Mahdinejad
@MehdiTaxir - 例如 c_str()、data() 或 find_first_of() 等。 - YAKOVM
2
注意:C++11 明确禁止引用计数字符串。 - David Rodríguez - dribeas
@DavidRodríguez-dribeas - 为什么? - YAKOVM
@Yakov - 这其中有几个重要原因(您可以在openstd.org上阅读会议记录)。 其中一个重要原因是他们希望std::string在所有实现中的行为保持一致。 基于写入时复制的实现需要考虑一些问题,而那些不尝试对字符串进行引用计数的实现则没有这些问题。 - Zac Howland
1
@Yakov:除了一致的行为之外,在多线程环境中由于取消共享缓冲区的过程,还存在其他问题。 - David Rodríguez - dribeas
4个回答

14

从纯哲学的角度来看:是的,字符串是一种向量类型。它是一个连续的内存块,用于存储字符(向量是一个连续的内存块,用于存储任意类型的对象)。因此,从这个角度来看,字符串是一种特殊的向量。

在设计和实现 std::stringstd::vector 时,它们共享一些相同的接口元素(例如连续的内存块、operator[]),但是 std::string 并不派生自 std::vector(副注:您不应公开从标准容器派生,因为它们没有被设计为基类,例如它们没有虚析构函数),而且它们彼此之间也不能直接转换。也就是说,以下内容将不能编译:

std::string s = "abc";
std::vector<char> v = s; // ERROR!

然而,由于它们都支持迭代器,因此您可以将字符串转换为向量:

std::string s = "abc";
std::vector<char> v(s.begin(), s.end()); // note that the vector will NOT include the '\0' character

std::string自C++11起将不再具有引用计数,因为许多实现所使用的写时复制功能已被C++11标准禁止。

从内存角度来看,std::string实例与std::vector<char>非常相似(例如它们都有指向其内存位置的指针、大小和容量),但两个类的功能是不同的。


你不应该公开从标准容器派生,因为它们没有虚析构函数。嗯,我认为这在很大程度上是正确的,但有点掩盖了一些细节。我会在中间插入一些文本:“你不应该公开从标准容器派生,因为它们不是设计成基类使用的。其中一个线索是它们没有虚析构函数。”这不是唯一的原因,也不完全是一个原因。 - BoBTFish
仅就词汇表达提出一点小意见,向量并不存储类型,而是存储任意类型的对象。(更或者说:这些对象必须是可复制的,例如。) - James Kanze
@JamesKanze 好的。我想表达的主要观点是,你可以将字符串看作一种特殊类型的向量,但你不能将向量看作字符串。 - Zac Howland

11

std::stringstd::vector(以及其他标准容器)有部分接口是相同的,但它们是两个不同的东西,拥有不同的目的。

它们可能有非常不同的实现方式,因为 std::string 允许使用小字符串优化或写时复制(自2011年后已不再合法)。尽管它们可能具有非常相似的实现方式。

它们都支持随机访问迭代器,因此可以使用标准算法以类似的方式使用。我认为 std::string 不能被归类为序列容器。

很多 std::string 的成员函数不能直接通过从 std::vector 继承来实现,因为它隐藏了存储 NUL 终止符的事实。因此,当 std::string::size 返回 3 时,std::vector::size 将返回 4,同样适用于 end 和其他一些函数。


3
主要原因是不能(或至少不应该)使用公共继承来实现,因为从字符串string到向量vector的隐式转换是不允许的。例如,如果我编写这样的代码:
int f(std::vector<char> const &s);

// ...

std::string s;
f(s);

编译应该失败(除非存在另一个接受字符串的f重载)。
如果你真的想这样做,你可以使用从std::vector私有继承的方式来实现std::string。虽然它可能不是最有效率的,但至少我无法想到它会明显违反什么要求。效率损失来自于std::vector需要更加通用,因为它必须支持可以抛出异常的类型的实例化,而std::string只设计用于可以免于异常的类型的实例化。

另一个无法使用公共继承实现的原因是缺乏虚析构函数(对于任何std容器)。 - Zac Howland
@ZacHowland 嗯,我的想法是继承只是一种实现细节,用户不会知道它的存在,因此他们永远不会将 std::vector<char>* 存储到 std::string 中,更不用说 delete 它了。 - BoBTFish

3
不,std::stringstd::basic_string<char>)不是使用std::vector实现的序列容器类型。你可以将其视为包含char的序列容器之一,因为它与其他容器共享许多功能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接