5

在编写 C++ 应用程序时,我通常会限制自己使用 C++ 特定的语言功能。大多数情况下,这意味着尽可能使用 STL 而不是 CRT。

对我来说,STL 比使用 CRT 更加流畅和可维护。考虑以下:

std::string str( "Hello" );
if( str == "Hello" ) { ... }

C-Runtime 等价物是:

char const* str = "Hello";
if( strcmp( str, "Hello" ) == 0 ) { ... }

就我个人而言,我发现前一个例子更容易看。我更清楚发生了什么。当我编写第一次代码时,我的第一件事总是以最自然的方式编写代码。

我的团队对前一个示例的一个担忧是动态分配。如果字符串是静态的或者已经分配到其他地方,他们认为可能导致碎片或在此处进行浪费分配是没有意义的。我反对这一点的论点是首先以最自然的方式编写代码,然后在证明代码导致问题后返回并更改它。

我不喜欢后一个示例的另一个原因是它使用了 C 库。通常我会不惜一切代价避免它,因为它不是 C++,它的可读性较差,更容易出错,并且存在更多的安全风险。

所以我的问题是,我是否正确避免使用 C 运行时?我真的应该关心编码中这一步的额外分配吗?在这种情况下,我很难判断我是对还是错。

4

5 回答 5

5

我觉得我的评论llvm::StringRef被忽略了,所以我会从中做出回答。

llvm::StringRef str("Hello");

这实质上是设置一个指针,调用 strlen,然后设置另一个指针。没有分配。

if (str == "Hello") { do_something(); }

可读,但仍然没有分配。它也适用于std::string.

std::string str("Hello");
llvm::StringRef stref(str);

但是,您必须小心,因为如果字符串被销毁或重新分配,则 StringRef 变得无效。

if (str == stref) { do_something(); }

在适当的地方使用这个类时,我注意到了相当大的性能优势。这是一个强大的工具,你只需要小心它。我发现它对字符串文字最有用,因为它们可以保证在程序的生命周期内持续存在。另一个很酷的功能是您可以在不创建新字符串的情况下获取子字符串。

顺便说一句,有人提议在标准库中添加一个与此类似的类。

于 2012-05-23T17:52:17.483 回答
4

你在做 C++ 还是 C?这些是完全不同的语言,有着完全不同的思维方式。

如果是 C++:

std::string str( "Hello" );
if( str == "Hello" ) { ... }

如果 C:

char const* str = "Hello";
if( strcmp( str, "Hello" ) == 0 ) { ... }

不要混合两者。

于 2012-05-23T17:12:35.440 回答
4

使用实现Small String Optimization的编译器,我得到以下结果:

main    PROC                        ; COMDAT

; 6    : {

$LN124:
  00000 48 83 ec 48       sub    rsp, 72            ; 00000048H

; 7    :    std::string str( "Hello" );

  00004 8b 05 00 00 00
        00                mov    eax, DWORD PTR ??_C@_05COLMCDPH@Hello?$AA@

; 8    : 
; 9    :    if( str == "Hello" )

  0000a 48 8d 15 00 00
        00 00            lea     rdx, OFFSET FLAT:??_C@_05COLMCDPH@Hello?$AA@
  00011 48 8d 4c 24 20   lea     rcx, QWORD PTR str$[rsp]
  00016 89 44 24 20      mov     DWORD PTR str$[rsp], eax
  0001a 0f b6 05 04 00
        00 00            movzx   eax, BYTE PTR ??_C@_05COLMCDPH@Hello?$AA@+4
  00021 41 b8 05 00 00
        00               mov     r8d, 5
  00027 c6 44 24 37 00   mov     BYTE PTR str$[rsp+23], 0
  0002c 48 c7 44 24 38
        05 00 00 00      mov     QWORD PTR str$[rsp+24], 5
  00035 c6 44 24 25 00   mov     BYTE PTR str$[rsp+5], 0
  0003a 88 44 24 24      mov     BYTE PTR str$[rsp+4], al
  0003e e8 00 00 00 00   call    memcmp
  00043 85 c0            test    eax, eax
  00045 75 1d            jne     SHORT $LN123@main

; 10   :    { printf("Yes!\n"); }

  00047 48 8d 0d 00 00
        00 00            lea     rcx, OFFSET FLAT:??_C@_05IOIEDEHB@Yes?$CB?6?$AA@
  0004e e8 00 00 00 00   call    printf

; 11   : 
; 12   : }

看不到一个内存分配!

于 2012-05-23T18:19:01.623 回答
0

在幕后,std::string::operator== 表面上是在调用 strcmp。老实说,如果碎片对您来说不是问题,并且您喜欢利用 stl 更易读的语法,那就去使用 stl。如果性能是一个问题并且您分析代码并且您看到 std::string 内部数据的常量分配/释放是一个热点/瓶颈,请在那里优化。如果您不喜欢不一致的编码风格混合 operator==() 和 strcmp,请编写如下内容:

inline bool str_eq(const char* const lhs, const char* const rhs)
{
    return strcmp(lhs, rhs) == 0;
}
inline bool str_eq(const std::string& lhs, const char* const rhs)
{
    return str_eq(lhs.c_str(), rhs);
}
inline bool str_eq(const char* const lhs, const std::string& rhs)
{
    return str_eq(lhs, rhs.c_str());
}
inline bool str_eq(const std::string& lhs, const std::string& rhs)
{
    return lhs == rhs;
}

这不应该是真正的宗教对话。两者的工作方式相同。现在如果你看到有人在写

std::string str( "Hello" );
if( strcmp(str.c_str(), "Hello") == 0 ) { ... }

或者

std::string str( "Hello" );
if( str.compare( "Hello" ) == 0) { ... }

那么你可以就混合风格进行辩论,因为使用 operator== 显然这两种风格都会更清晰

于 2012-05-23T17:41:08.623 回答
0

如果您的团队使用 C++ 编码,您应该使用它提供的所有功能。当然,正确使用 C++ 会注意内存分配(构造函数和析构函数)和更自然的语法(对于 ==、+)。

您可能认为 OOP 样式可能会更慢。但是您必须首先衡量瓶颈是字符串操作。在大多数情况下不太可能。过早的优化是万恶之源。正确设计的 C++ 类不会输给方便编写的 C 代码。

回到您的问题,混合库的最差变体。您可以用 OOP 库替换 C 字符串,但仍然使用老式的 IO 例程和数学。

于 2012-05-23T18:38:48.367 回答