在方法/函数中使用`static const std::string`还是仅使用`const std::string`更好?

4

我有一个方法/函数:

void foo() {

  static const std::string strSQLQuery = "SELECT ... ";
  // or maybe
  const std::string strSQLQuery = "SELECT ... "; 

  // some operations on strSQLQuery
  // i.e. concatenating with WHERE, etc.:
  const std::string strSQL = strSQLQuery + strWHERE;

  doSthOnDataBase(strSQL);
}

(SQL只是一个例子)

  1. static const只会初始化一次,但持续存在于内存中直到进程结束。
  2. const每次运行foo()时都会初始化,但是内存(堆栈)在{}块结束时释放。

另一方面,字符串"SELECT ... "仍必须硬编码在程序代码中。使用1或2并不重要。

那么哪种方法更好?是使用static const std::string还是只用const std::string

或者可能没有一个答案,因为它取决于我如何使用foo()——每秒调用它1000次(那么我不想每次都初始化变量)还是每月调用它1000次(那我不关心)。

(我已经阅读了问题Difference between static const char* and const char*,特别是回答https://dev59.com/03A85IYBdhLWcg3wHv3B#2931146,但它们适用于const char*。)


7
看起来你已经概述了每种解决方案的优缺点。提示:没有绝对更好的选择。 - Shoe
我会选择静态常量,因为它需要更少的内存(堆栈是一件非常昂贵的事情,浪费起来很不划算),并且基本上具有相同的效果。顺便说一下,在方法中定义const是非常不常见的,通常你会将其定义为全局变量或结构体成员... - Pandrei
1
@Pandrei:什么意思?静态字符串意味着如果在多线程环境中使用,您的代码将会崩溃。非静态线程意味着几个字节被放在堆栈上,其余部分被放在堆上。我不知道你在谈论什么关于“浪费堆栈内存很昂贵”的问题。 - jalf
@jalf,我建议你多了解一下多线程环境和C++的相关知识;Scott Meyers的《Effective C++》是一本不错的读物。在多线程环境中,静态常量字符串是可以使用的,关键在于const属性-因此你实际上不需要任何锁定。 - Pandrei
1
你可以使用 const char* const strSQLQuery = "SELECT ... " - Jarod42
显示剩余13条评论
4个回答

2

对其进行性能剖析。很可能,对数据库的访问完全超过了字符串初始化成本,这样就不重要了。

正如已经指出的那样,在某些编译器上,局部静态变量不是线程安全的。它们在GCC中是线程安全的,并且C++11要求它们是线程安全的,但是直到VS2013,微软才实现了它们。


微软直到VS2013才能够正确地实现一些C++11的功能。但他们仍然没有完全达到目标... - rubenvb
我知道,weTeim的回答与这个类似,但是我认为Sebastian更好地“切入了重点”,并且没有重复我在问题中提到的想法。 - jacek.ciach

1
如果成员变量是静态常量,只要不故意破坏const部分,它就是安全的。但从您链接的评论中可以看出,局部变量是不同的:
一个局部静态变量在第一次遇到其定义时被初始化,但当函数退出时不会被销毁。因此,在函数调用之间保持其值。
为了使这个过程安全,gcc发出锁定以保护初始化,但MS C++不会,如此处所述,因此这可能是安全的或不安全的,这取决于编译器,即使被认为是安全的也可能有不良副作用。
这里的权衡似乎是效率与可维护性之间的。那额外的速度是否值得引入一些微妙的错误呢?在研究这个问题时,我已经完全改变了我的观点,现在我会说通常不是。特别是在这种情况下,它只是一个简单的字符串初始化,然后是一个漫长的数据库调用。

0
据我所知,在您的情况下,“static”是不必要的。 1)当您声明和定义一个const变量时,编译器有机会将所有出现替换为您分配的值,如下所示:
const int var = 9;
int b = sqrt( var );

将变成

int b = sqrt( 9 );

这个语法和C风格中的 #define 差不多。如果你想要这样的效果,那么你已经可以不用“static”来实现了。

2)正如其他人所说,静态变量会在 foo() 返回后依然存在。我猜这不是你的目标。


1
std::string 是动态初始化和用户定义的。祝你好运找到一个可以在其中进行常量传播的编译器。 - Sebastian Redl

0

静态非基本类型局部变量 (const 或者不是) 在每次函数调用时进行原子读取 (初始化标志)。只有在通过编译器标志特别请求时,它们才是线程安全的 (谈到 GCC)。在运行时构建的字符串 strSQL 会比静态初始化引起的原子读取更大的性能损失 (由于堆分配)。

最快的做法可能是这样的:

void call(std::string const& where) {
    static char prefix[] = "SELECT ...";

    std::string strSQL;
    strSQL.reserve(sizeof(prefix)/sizeof(char) + strWHERE.size()); 
    strSQL.append(prefix, sizeof(prefix)/sizeof(char)).append(strWHERE);
    ...
}

是否需要为了速度而牺牲可读性是另一个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接