为什么C ++没有用C ++元素/样式重新实现C标准函数?

有关具体示例,请考虑atoi(const std::string &) 。 这非常令人沮丧,因为我们作为程序员需要使用它。

  1. 更一般的问题是为什么C ++标准库没有用C ++字符串,C ++向量或其他C ++标准元素重新实现标准C库而不是保留旧的C标准库并迫使我们使用旧的char *接口?

    它耗时并且在这两个接口之间转换数据类型的代码并不容易优雅。

  2. 这是出于兼容的原因,考虑到比现在有更多的遗留C代码,保留这些C标准接口会使从C代码到C ++的转换更容易吗?

  3. 另外,我听说过许多其他可用于C ++的库对STL进行了大量的增强和扩展。那么库是否支持这些function呢?

PS:考虑到第一个具体问题的更多答案,我编辑了很多来澄清问题,以概述我更加好奇的问题。

另一个更普遍的问题是为什么STL不重新实现所有标准C库

因为旧的C库可以解决问题。 如果C ++标准库可以比旧版本更好地执行它,则它只重新实现现有function。 而对于C库的某些部分,编写新C ++的好处 – 实现并不足以certificate额外的标准化工作是正确的。

至于atoi等,在std::stringstream类的C ++标准库中有这些新版本。

要从类型T转换为类型U:

 T in; U out; std::stringstream sstr(in); sstr >> out; 

与IOStream库的其余部分一样,它并不完美,它非常冗长,速度非常慢,等等,但它确实有效,而且通常它足够好 。 它可以处理所有大小的整数,浮点值,C和C ++字符串以及定义运算符<<的任何其他对象。

编辑:此外,我听说许多其他可用于C ++的库对STL进行了大量的增强和扩展。那么库是否支持这些function?

Boost有一个boost::lexical_cast ,它包装了std::stringstream 。 使用该function,您可以将上述内容编写为:

 U out = boost::lexical_cast(in); 

即使在C语言中,使用atoi对于转换用户输入也不是一件好事。 它根本不提供错误检查。 提供它的C ++版本并不是那么有用 – 考虑到它不会抛出任何东西,你可以将.c_str()传递给它并使用它。

相反,你应该在C代码中使用strtol ,它确实进行了错误检查。 在C ++ 03中,你可以使用stringstreams来做同样的事情,但是它们的使用容易出错:你究竟需要检查什么? .bad() .fail().eof() ? 你怎么吃剩余的空白? 格式化标志怎么样? 这样的问题不应该打扰普通用户,只想转换他的字符串。 boost::lexical_cast确实做得很好,但顺便提一句,C ++ 0x添加了实用程序函数,以便通过C ++包装器来实现快速安全的转换,如果转换失败则可以抛出:

 int stoi(const string& str, size_t *idx = 0, int base = 10); long stol(const string& str, size_t *idx = 0, int base = 10); unsigned long stoul(const string& str, size_t *idx = 0, int base = 10); long long stoll(const string& str, size_t *idx = 0, int base = 10); unsigned long long stoull(const string& str, size_t *idx = 0, int base = 10); 

效果 :前两个函数调用strtol(str.c_str(), ptr, base) ,最后三个函数调用strtoul(str.c_str(), ptr, base)strtoll(str.c_str(), ptr, base)strtoull(str.c_str(), ptr, base) 。 每个函数返回转换后的结果(如果有)。 参数ptr指定一个指向函数内部对象的指针,该对象用于确定在*idx存储的内容。 如果函数没有抛出exception并且idx != 0 ,则函数在*idx存储str的第一个未转换元素的索引。

返回 :转换后的结果。

抛出invalid_argument如果strtolstrtoulstrtollstrtoull报告不能执行转换。 如果转换后的值超出返回类型的可表示值范围,则抛出out_of_range

没有好办法知道atoi是否失败了。 它总是返回一个整数。 该整数是有效转换吗? 或者是0或-1或其他表示错误的? 是的,它可能会抛出exception,但这会改变原始合同,并且您必须更新所有代码以捕获exception(这是OP正在抱怨的内容)。

如果翻译过于耗时,请编写自己的atoi:

 int atoi(const std::string& str) { std::istringstream stream(str); int ret = 0; stream >> ret; return ret; } 

我看到提供了使用std::stringstreamstd::istringstream解决方案。 对于单线程应用程序来说这可能是完全正常的,但是如果应用程序有很multithreading并且经常调用以这种方式实现的atoi(const std::string& str) ,这将导致性能下降。

请阅读此讨论,例如: http : //gcc.gnu.org/ml/gcc-bugs/2009-05/msg00798.html 。 并查看std :: istringstream的构造函数的回溯:

 #0 0x200000007eb77810:0 in pthread_mutex_unlock+0x10 () from /usr/lib/hpux32/libc.so.1 #1 0x200000007ef22590 in std::locale::locale (this=0x7fffeee8) at gthr-default.h:704 #2 0x200000007ef29500 in std::ios_base::ios_base (this=) at /tmp/gcc-4.3.1.tar.gz/gcc-4.3.1/libstdc++-v3/src/ios.cc:83 #3 0x200000007ee8cd70 in std::basic_istringstream,std::allocator >::basic_istringstream (this=0x7fffee4c, __str=@0x7fffee44, __mode=_S_in) at basic_ios.h:456 #4 0x4000f70:0 in main () at main.cpp:7 

因此,每次输入atoi()并创建std::stringstream类型的本地变量时,您将锁定全局互斥锁,并且在multithreading应用程序中,可能会导致等待此互斥锁。

因此,在multithreading应用程序中最好不要使用std::stringstream 。 例如,只需调用atoi(const char*)

 inline int atoi(const std::string& str) { return atoi(str.c_str()); } 

举个例子,你有两个选择:

 std::string mystring("4"); int myint = atoi(mystring.c_str()); 

或类似的东西:

 std::string mystring("4"); std::istringstream buffer(mystring); int myint = 0; buffer >> myint; 

第二个选项为您提供比第一个更好的错误管理。

您可以编写一个更通用的字符串来进行数字转换:

 template  T strToNum(const std::string &inputString, std::ios_base &(*f)(std::ios_base&) = std::dec) { T t; std::istringstream stringStream(inputString); if ((stringStream >> f >> t).fail()) { throw runtime_error("Invalid conversion"); } return t; } // Example usage unsigned long ulongValue = strToNum(strValue); int intValue = strToNum(strValue); int intValueFromHex = strToNum(strHexValue,std::hex); unsigned long ulOctValue = strToNum(strOctVal, std::oct); 

对于转换,我发现最简单的方法是使用boost的lexical_cast (除非它可能过于严格地检查字符串转换为其他类型的有效性)。

它肯定不是很快(它只是在引擎盖下使用std::stringstream ,但显然更方便),但在转换值时通常不需要性能(例如,创建错误输出消息等)。 (如果你做了很多这些转换并且需要极高的性能,那么你可能做错了什么,而且根本不应该进行任何转换。)

因为旧的C库仍然可以使用标准C ++类型,只需要很少的适应性。 您可以使用const char *轻松地将const char *更改为std::string ,然后使用std::string::c_str()更改。 在你的例子中,使用std::string s ,只需调用atoi(s.c_str())就可以了。 只要您可以轻松来回切换,就无需添加新function。

我没有提出在数组上工作的C函数而不是容器类,除了像qsort()bsearch()这样的东西,STL有更好的方法来做这些事情。 如果你有具体的例子,我可以考虑一下。

C ++确实需要支持旧的C库以实现兼容性,但趋势是提供有保证的新技术,并在没有太多改进时为旧函数提供接口。 例如,Boost lexical_cast是对atoi()strtol()等函数的改进,就像标准C ++字符串是对C语言的改进一样。 (有时候这是主观的。虽然C ++流比CI / O函数有相当大的优势,但有时候我宁愿退回到C的做事方式.C ++标准库的某些部分非常好,有些部分,好吧,不是。)

有各种各样的方法来解析字符串中的数字,如果你真的想要atoi可以很容易地通过atoi(std.c_str())std::string一起使用,但atoi有一个糟糕的接口,因为没有确定确定解析过程中是否发生错误的方法。

这是从std::string获取int的一种稍微更现代的C ++方法:

 std::istringstream tmpstream(str); if (tmpstream >> intvar) { // ... success! ... } 

脸颊上的答案是:因为STL是一个半心半意的尝试来展示C ++模板的强大function。 在他们到达问题空间的每个角落之前,时间已经过去了。

有两个原因:创建API需要时间和精力。 创建一个好的API需要花费大量的时间和巨大的努力。 创建一个出色的API需要花费大量的时间和不可思议的努力。 在创建STL时,OO对于C ++人员来说仍然是一个新手。 他们没有如何制作流畅简单的API的想法。 今天,我们认为迭代器是如此1990年,但在当时,人们认为“血腥地狱,为什么我需要它?因为(int i = 0; i <...)已经足够好了三十年!”

所以STL并没有成为优秀,流畅的API。 这不是所有的C ++错误,因为你可以用C ++制作好的API。 但这是第一次尝试这样做,它表明了。 在API成熟之前,它已经变成了一个标准,并且所有丑陋的缺点都被置之不理。 除此之外,还有所有这些遗留代码和所有已经可以完成所有工作的库,所以压力并不是真的存在。

为了解决你的痛苦,放弃STL并看看接class人:尝试提升 ,也许Qt 。 是的,Qt是一个UI库,但它也有一个非常好的标准库。