字符串操作和内存管理

时间:2013-06-20 08:11:14

标签: c++ string memory-management char type-conversion

我想为C风格的函数strftime编写一个方便的包装器。我已经找到了将char-array转换为字符串的一些选项,反之亦然。这是我的代码:

std::string Time::getAsFormattedString ( const std::string& format , const size_t& maxStringSize = 999 )
{

    char* timeArray = 0;
    std::string timeString;

    //  [OPTION_0]
    timeArray = reinterpret_cast <char*> (malloc(sizeof(char)*maxStringSize)));

    //  [OPTION_1]
    timeArray = const_cast <char*> (timeString.c_str());

    //  [OPTION_2]
    timeArray = &(*(timeString.begin()));

    strftime(timeArray,maxStringSize,format.c_str(),&this->time);
    timeString = timeArray;

    //  [OPTION_0]
    free(timeArray);

    return timeString;

}

№0选项看起来很安全,因为在释放内存之前不能抛出任何异常(编辑:timeString = timeArray可以抛出一个,在该行周围需要try-catch)

№1const-casting总是看起来像黑客

№2似乎是最好的我不知道是否可能存在一些问题

你能否告诉我,哪一个是最安全,最正确,最佳,也许是最佳实践。

谢谢。

5 个答案:

答案 0 :(得分:3)

您提出的任何选项都不是真的可以接受;该 第二个和第三个甚至不会工作。在全球范围内,有两个 “可接受的”解决方案。

最简单的是:

char buffer[ 1000 ];
size_t n = strftime( buffer, sizeof( buffer ), format.c_str(), &time );
if ( n == 0 ) {
    throw SomeError;    //  or you might just abort...
}
return std::string( buffer );

这具有简单的优点,但您必须这样做 将最大大小记录为界面中的约束。 (这对我来说似乎是一个合理的限制。)

或者,您可以删除约束:

std::vector<char> buffer( 100 );
size_t n = strftime( &buffer[0], buffer.size(), format.c_str(), &time );
while ( n == 0 ) {
    buffer.resize( 2 * buffer.size() );
    n = strftime( &buffer[0], buffer.size(), format.c_str(), &time );
}
return std::string( buffer.begin(), buffer.begin() + n );

(在C ++ 11中,在C ++ 03的实践中,您可以使用 直接std::string,而不是std::vector。其中 例如,您需要在结果字符串上调用resize( n ) 在返回之前。)

答案 1 :(得分:2)

首先,唯一不会崩溃的选项是选项0。其他人会崩溃,因为你说你已经分配了999个字节,但事实上内部字符串可能只分配了1个字节,并且会发生悲伤的事情。

但是我可能会在这里通过在堆栈上分配一大块字符来做到这一点。

char timeArray[2048];
strftime(timeArray,2048,format.c_str(),&this->time);
return string(timeArray);

这样您就不必进行任何投射或动态分配,几乎肯定会更整洁,更快。

答案 2 :(得分:1)

选项2(或更好,&timeString[0])应该是首选。

你对选项1中的const_cast不好是正确的,而在选项0中,你至少可以使用new而不是{{来清理代码。 1}}(并避免演员表)

但更喜欢选项2。

(哦,正如评论者指出的那样,如果你正在写字符串本身,你显然必须首先调整它的大小,以至于你不会写出超出界限。鉴于支持者,我应该可能已经明确表示了这一点)

答案 3 :(得分:1)

选项1和2不好,因为您无意改变从std::string::c_str()得到的字符串(c代表constant)。选项2将需要“调整大小”字符串才能使用它。但我不确定字符串是否可以保证从同一缓冲区中复制...

我的解决方案是:

char timeArray[1000];     

(虽然这样做太过分了。除非你多次重复相同的格式说明符,否则它不可能超过100个字符,而且非常冗长 - 所以“理智”的组合不会达到1000个字符以内的任何地方。 )

请注意timeString = timeArray可以为bad_alloc抛出异常,所以如果你不想在那种情况下泄漏内存,你需要使用基于堆栈的存储(如我的建议),智能指针或代码部分周围的try / catch块。

答案 4 :(得分:1)

唯一可行的解​​决方案是选项0(尽管可以执行一些小的调整)。正如其他人所指出的那样,该标准表示c_str()方法返回的空间并不是用于写入目的,实际上它是一种允许std::string被读取的方法。 C标准库(它是C ++标准库的一部分)。

标准中写道:

  

返回:指向长度大小为()的数组的初始元素的指针   + 1,其第一个size()元素等于由* this控制的字符串的相应元素,其最后一个元素为null   charT()指定的字符。

     

需要:程序不得   更改存储在数组中的任何值。该计划也不应该   在任何后续之后将返回值视为有效指针值   调用类basic_string的非const成员函数   指定与此相同的对象。

所以,我只是快速修复你的代码:

const char * Time::getAsFormattedString(const std::string& format)
{
    static char timeArray[256];

    std::strftime( timeArray, 256, format.c_str(), &this->time );

    return timeArray;
}

这使得方法的缓冲区可以在程序启动时创建,并且可以连续重用,因此不会出现内存错误(因为堆不受影响)。

唯一的问题是堆栈中有空间来创建你将存储函数结果的字符串,但无论如何这将在调用函数后发生,函数本身不会触及堆,并且只有最小的堆栈。

实际上,该功能的用处不受影响,因为从const char *std::string的自动转换,您可以通常的方式安全地调用它:

std::string strTime = time.getAsFormattedString( "%F %T" );

希望这有帮助。