C++ 静态常量与重复调用的函数中的常量

C++ 静态常量与重复调用的函数中的常量,c++,static,constants,C++,Static,Constants,我只是想知道怎么做 void test() { const static int ABC = 12; cout << ABC; } void测试() { 常数static int ABC=12; 从理论上讲,前一种方法稍微好一点,因为它只对变量进行一次初始化 但是,编译器只需删除“const”行并替换“cout在第一次强制转换中,ABC只在第一次函数调用时初始化一次。在第二种情况下,ABC每次都会初始化。如果ABC是带构造函数的复杂类型,您会感觉到不同。它可以分配内存或

我只是想知道怎么做

void test()
{
   const static int ABC = 12;
   cout << ABC;
}
void测试()
{
常数static int ABC=12;

从理论上讲,前一种方法稍微好一点,因为它只对变量进行一次初始化


但是,编译器只需删除“const”行并替换“cout在第一次强制转换中,ABC只在第一次函数调用时初始化一次。在第二种情况下,ABC每次都会初始化。如果ABC是带构造函数的复杂类型,您会感觉到不同。它可以分配内存或初始化一些互斥。对于int,实际上没有区别

根据C++03标准3.7.1/2:

如果具有静态存储持续时间的对象具有初始化或具有副作用的析构函数,则即使该对象似乎未使用,也不应消除该对象,除非类对象或其副本可以按照中的规定消除 12.8


我会选择第二个,因为它更具可读性。为什么不必要地添加一个关键字(static)而不会给阅读代码的人带来任何价值呢。

对于一个基本类型,例如整数值,我会将static的使用归类为“过早优化”除非您做了一些基准测试并考虑了各种权衡(例如,将静态值初始化为非零值通常需要在表中输入一个条目,以指定加载代码时要设置的位置、大小和初始值)

除非您使用指向int的指针,并且它在函数返回后被取消引用,否则您不需要静态-让编译器进行优化


如果在函数退出后取消引用了指向该值的指针,那么我会将其归类为持久状态变量,最好在类或模块级别定义它,以明确这一点。

有几个因素会影响答案:

  • 首先,只要值是
    const
    ,它几乎肯定会在任何情况下得到优化。这意味着生成的代码很可能是相同的
  • 其次,
    静态
    成员存储在其他位置,这意味着更少的局部性,可能会导致缓存丢失
  • 第三,初始化的成本取决于类型。在您的例子中,对于int,初始化的成本基本上是不存在的。对于更复杂的用户定义类型,它可能是巨大的
所以答案是,在足够简单的情况下,编译器可以找出它们并进行优化,这是零差异的。在您的示例中,几乎肯定是这样

只要变量具有易于构造且成本低廉的类型,就应选择非静态类型以避免缓存丢失

如果该类型的构造成本很高,则可能需要使用
static

当然,最后也是最重要的是:

不要相信我们的猜测。如果您关心性能,只有一种正确的做法:

  • 测量它,以验证它实际上是一个问题
  • 衡量每个可能解决方案的性能
  • 选择能产生最佳性能的解决方案

这取决于编译器

在嵌入式软件中,静态常量通常存储在闪存(即代码存储器)中,可以像普通变量一样直接访问,无需初始化

相反,非静态常量的值可能存储在闪存中,但常量本身将像变量一样在堆栈上创建,并像变量一样初始化

如果编译器就是这样处理这些场景的,那么静态常量更有效,因为它既不需要堆栈分配,也不需要初始化


显然,非嵌入式编译器可能会对这些场景进行不同的处理。

静态常量示例肯定会为后续调用节省执行时间,对于使用静态常量的复杂对象构造,速度要快得多,但我怀疑是否需要将ABC限制在函数范围内,并在函数f上引入行为变化rom对以下调用的第一次调用。通常一个文件包含耦合的函数耦合函数,只需给出ABC文件的作用域并使用它即可。

您还可以通过使用编译器的汇编程序或类似于此的联机实用程序了解编译器将做什么:并查看生成的汇编代码

这值得一看,因为这个问题是根本性的

基于您的问题,考虑到本地与静态类型声明和实例化,查看原语与“复杂”(甚至不是很复杂)的示例:

#include <iostream>

struct non_primitive
{
    int v;
    non_primitive(const int v_) : v(v_) {}
};

void test_non_static_const_primitive()
{
   const int ABC = 12;
   std::cout << ABC;
}

void test_static_const_primitive()
{
   const static int ABC = 12;
   std::cout << ABC;
}

void test_non_static_constant_non_primitive_global_struct() 
{
    const non_primitive s(12);
    std::cout << s.v;
}

void test_non_static_constant_non_primitive_local_struct() 
{
    struct local_non_primitive
    {
        int v;
        local_non_primitive(const int v_) : v(v_) {}
    };
    const local_non_primitive s(12);
    std::cout << s.v;
}

void test_static_constant_non_primitive_global_struct() 
{
    const static non_primitive s(12);
    std::cout << s.v;
}

void test_static_constant_non_primitive_local_struct() 
{
    struct local_non_primitive
    {
        int v;
        local_non_primitive(const int v_) : v(v_) {}
    };
    const static local_non_primitive s(12);
    std::cout << s.v;
}
#包括
结构非_原语
{
INTV;
非_原语(const int v_uu):v(v_u){
};
无效测试非静态常量原语()
{
常数int ABC=12;

std::cout是的,我理解..我特别想问的是初始化它一次与每次初始化/销毁它的含义。如果编译器没有将两者优化到相同的程度,第一个版本会更慢,因为缓存未命中比整数赋值慢得多。-1否。使用“静态”这里并不是装饰性的;正如其他帖子所描述的那样,它起到了不同的作用。没有证据表明
static
更适合于这样一个简单的情况,在这种情况下,它很可能被优化为相同的代码-&即使没有,人们也可以很容易地推测,它可能会因为缓存问题而使事情变得更糟。好处是,您为它推荐全局变量没有什么好的理由,与不将任何对象放在比需要更大范围内的公认智慧相反。而且,在第一次调用和其他调用之间,“行为变化”(即检查是否构造实例)并不重要;这是一个实现细节,在
static
init确实有意义的情况下,它会被构造成本所抵消
#include <iostream>

struct non_primitive
{
    int v;
    non_primitive(const int v_) : v(v_) {}
};

void test_non_static_const_primitive()
{
   const int ABC = 12;
   std::cout << ABC;
}

void test_static_const_primitive()
{
   const static int ABC = 12;
   std::cout << ABC;
}

void test_non_static_constant_non_primitive_global_struct() 
{
    const non_primitive s(12);
    std::cout << s.v;
}

void test_non_static_constant_non_primitive_local_struct() 
{
    struct local_non_primitive
    {
        int v;
        local_non_primitive(const int v_) : v(v_) {}
    };
    const local_non_primitive s(12);
    std::cout << s.v;
}

void test_static_constant_non_primitive_global_struct() 
{
    const static non_primitive s(12);
    std::cout << s.v;
}

void test_static_constant_non_primitive_local_struct() 
{
    struct local_non_primitive
    {
        int v;
        local_non_primitive(const int v_) : v(v_) {}
    };
    const static local_non_primitive s(12);
    std::cout << s.v;
}
test_non_static_const_primitive():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_static_const_primitive():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_non_static_constant_non_primitive_global_struct():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_non_static_constant_non_primitive_local_struct():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_static_constant_non_primitive_global_struct():
  movzx eax, BYTE PTR guard variable for test_static_constant_non_primitive_global_struct()::s[rip]
  test al, al
  je .L7
  mov esi, DWORD PTR test_static_constant_non_primitive_global_struct()::s[rip]
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
.L7:
  sub rsp, 8
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_global_struct()::s
  call __cxa_guard_acquire
  test eax, eax
  mov esi, DWORD PTR test_static_constant_non_primitive_global_struct()::s[rip]
  je .L8
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_global_struct()::s
  mov DWORD PTR test_static_constant_non_primitive_global_struct()::s[rip], 12
  call __cxa_guard_release
  mov esi, 12
.L8:
  mov edi, OFFSET FLAT:std::cout
  add rsp, 8
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_static_constant_non_primitive_local_struct():
  movzx eax, BYTE PTR guard variable for test_static_constant_non_primitive_local_struct()::s[rip]
  test al, al
  je .L14
  mov esi, DWORD PTR test_static_constant_non_primitive_local_struct()::s[rip]
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
.L14:
  sub rsp, 8
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_local_struct()::s
  call __cxa_guard_acquire
  test eax, eax
  mov esi, DWORD PTR test_static_constant_non_primitive_local_struct()::s[rip]
  je .L15
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_local_struct()::s
  mov DWORD PTR test_static_constant_non_primitive_local_struct()::s[rip], 12
  call __cxa_guard_release
  mov esi, 12
.L15:
  mov edi, OFFSET FLAT:std::cout
  add rsp, 8
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
_GLOBAL__sub_I__Z31test_non_static_const_primitivev:
  sub rsp, 8
  mov edi, OFFSET FLAT:std::__ioinit
  call std::ios_base::Init::Init()
  mov edx, OFFSET FLAT:__dso_handle
  mov esi, OFFSET FLAT:std::__ioinit
  mov edi, OFFSET FLAT:std::ios_base::Init::~Init()
  add rsp, 8
  jmp __cxa_atexit