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

时间:2009-11-20 15:16:00

标签: c++ c stl

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

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

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

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

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

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

10 个答案:

答案 0 :(得分:14)

  

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

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

对于atoi等, C ++标准库中{/ 1}}类的新版本。

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

std::stringstream

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

  

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

Boost的T in; U out; std::stringstream sstr(in); sstr >> out; 包裹boost::lexical_cast。使用该功能,您可以将上述内容写为:

std::stringstream

答案 1 :(得分:7)

即使在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存储的内容。如果函数没有抛出异常并且idx != 0,则函数将*idx存储在str的第一个未转换元素的索引中。

     

返回:已转换的结果。

     如果invalid_argumentstrtolstrtoulstrtoll报告无法执行转换,则

引发strtoull。如果转换后的值超出了返回类型的可表示值范围,则抛出out_of_range

答案 2 :(得分:5)

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

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

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

答案 3 :(得分:4)

我看到提供的解决方案使用std::stringstreamstd::istringstream。 对于单线程应用程序而言,这可能是完全正常的,但如果应用程序有很多线程,并且通常以这种方式调用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=<not available>)
    at /tmp/gcc-4.3.1.tar.gz/gcc-4.3.1/libstdc++-v3/src/ios.cc:83
#3  0x200000007ee8cd70 in std::basic_istringstream<char,std::char_traits<char>,std::allocator<char> >::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的本地变量时,您将锁定全局互斥锁,并且在多线程应用程序中,可能会导致等待此互斥锁。

因此,在多线程应用程序中不使用std::stringstream会更好。例如,只需致电atoi(const char*)

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

答案 4 :(得分:3)

对于您的示例,您有两个选择:

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

或类似的东西:

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

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

答案 5 :(得分:1)

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

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

答案 6 :(得分:1)

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

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

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

答案 7 :(得分:1)

您可以将更通用的字符串写入数字转换为:

template <class T>
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<unsigned long>(strValue);
int intValue             = strToNum<int>(strValue);

int intValueFromHex      = strToNum<int>(strHexValue,std::hex);
unsigned long ulOctValue = strToNum<unsigned long>(strOctVal, std::oct);

答案 8 :(得分:0)

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

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

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

答案 9 :(得分:0)

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

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

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

要解决您的痛苦,请放弃STL并查看接班人:尝试boost,也许Qt。是的,Qt是一个UI库,但它也有一个非常好的标准库。