为什么C ++没有用C ++元素/样式重新实现C标准函数?
有关具体示例,请考虑atoi(const std::string &)
。 这非常令人沮丧,因为我们作为程序员需要使用它。
-
更一般的问题是为什么C ++标准库没有用C ++字符串,C ++向量或其他C ++标准元素重新实现标准C库而不是保留旧的C标准库并迫使我们使用旧的
char *
接口?它耗时并且在这两个接口之间转换数据类型的代码并不容易优雅。
-
这是出于兼容的原因,考虑到比现在有更多的遗留C代码,保留这些C标准接口会使从C代码到C ++的转换更容易吗?
-
另外,我听说过许多其他可用于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
如果strtol
,strtoul
,strtoll
或strtoull
报告不能执行转换。 如果转换后的值超出返回类型的可表示值范围,则抛出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::stringstream
或std::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库,但它也有一个非常好的标准库。