class中static const int的使用

本文探讨了在类中声明static const int变量时,该变量在模板函数与引用传参时的链接过程中的表现及原因,通过实例分析了变量是否分配地址及实例化时对类型信息获取的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传统认识

我们都知道,对于class 中static const int 的变量,可以在类中进行初始化,并省去外部的定义,向下面的这种方式;此时,编译,执行均是正确的。

class test
{
 static const int tmp_int = 1;
 public:
 void print() {
    printf("%d", tmp_int);
    // foo(tmp_int);
    //foo1(tmp_int);
    //std::list<int> i_list;
    //i_list.push_back(tmp_int);
  }
};

意料之外

  1. 在函数模板中使用tmp_int
    但是,当对tmp_int采用一些特殊用法时,程序链接过程中则会出现错误,代码如下:
template<class T>
void foo(const T &a)
{
  printf("%d", a);
}
void foo1(int a)
    {
      printf("%d", a);
    }
class test
{
  static const int tmp_int = 1;
public:
  void print() {
    printf("%d", tmp_int);
    foo(tmp_int);// 此处会报找到不tmp_int的定义
    foo1(tmp_int);//正常通过编译连接
  }
};
  1. 作为一个参数传递给需要引用类型实参的函数
void foo1(int &a)
    {
      printf("%d", a);
    }
class test
{
  static const int tmp_int = 1;
public:
  void print() {
    printf("%d", tmp_int);
    // foo(tmp_int);
    foo1(tmp_int);//此处报错,同上
    std::list<int> i_list;
    //i_list.push_back(tmp_int);//此处同样会报错,现象和原因同上
  }
};

情理之中

上诉现象的主要原因可以通过两点进行概括:1.在class中对tmp_int进行初始化,并不会对tmp_int真正的分配地址;2.无论是模板还是引用,都需要地址。
查看tmp_int的符号表,只在类中进行初始化,得到如下结果:

tingshuai@yantingshuaideMacBook-Air ~$ nm test.o
0000000000000070 S __Z3fooIiEvRKT_
0000000000000030 S __ZN4test5printEv
                 U __ZN4test7tmp_intE
0000000000000000 T _main
                 U _printf

在类外加上tmp_int的定义,结果如下:

tingshuai@yantingshuaideMacBook-Air ~$ nm test.o
0000000000000070 S __Z3fooIiEvRKT_
0000000000000030 S __ZN4test5printEv
000000000000009c S __ZN4test7tmp_intE
0000000000000000 T _main
                 U _printf

谍影重重

  1. 编译器对class中的static const int 如何处理的?为什么会没有地址
  2. 模板编译时需要实例化(是在编译时?需要进一步确定),实例化需要参数的地址来获取变量类型?那为何当为函数模板赋值一直常量时可以获取类型信息,而tmp_int不可以?
template<class T>
void foo(const T &a)
{
  printf("%d", a);
}
void foo1(int &a)
    {
      printf("%d", a);
    }
class test
{
  static const int tmp_int = 1;
public:
  void print() {
    printf("%d", tmp_int);
    foo(3);//可以通过编译连接,执行结果正确
    // foo1(3);
    std::list<int> i_list;
    //i_list.push_back(tmp_int);//tmp_int报错,传递常量则没有问题
  }
};

参考

https://bytes.com/topic/c/answers/850988-const-static-member
http://stackoverflow.com/questions/5391973/undefined-reference-to-static-const-int
http://www.cplusplus.com/forum/general/36478/

刨根问底

  1. 通过观察汇编可知,对于模板的代码是在编译阶段生成的(其实这是想当然的事情,难道在程序的执行过程中能生成代码,只能对自己以前的理解呵呵了);只不过是当模板函数或模板类被真正调用时,才会为其生成真正的代码段;如下面的代码
template<class T>
void foo(const T &a)
{

  printf("%d", a);
}

int main(int argc, char *argv[])
{
  foo(3);//第一次调用目标函数
  double d = 1.245;
  foo(d);//第二次调用
  return 0;
}

其汇编如下:

void foo(const T &a)
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
   8:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
{

  printf("%d", a);
   c:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  10:   8b 00                   mov    (%rax),%eax
  12:   89 c6                   mov    %eax,%esi
  14:   bf 00 00 00 00          mov    $0x0,%edi
  19:   b8 00 00 00 00          mov    $0x0,%eax
  1e:   e8 00 00 00 00          callq  23 <_Z3fooIiEvRKT_+0x23>
}
  23:   c9                      leaveq
  24:   c3                      retq

Disassembly of section .text._Z3fooIdEvRKT_:

0000000000000000 <_Z3fooIdEvRKT_>:
template<class T>
void foo(const T &a)
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
   8:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
{

  printf("%d", a);
   c:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  10:   f2 0f 10 00             movsd  (%rax),%xmm0
  14:   bf 00 00 00 00          mov    $0x0,%edi
  19:   b8 01 00 00 00          mov    $0x1,%eax
  1e:   e8 00 00 00 00          callq  23 <_Z3fooIdEvRKT_+0x23>
}
  23:   c9                      leaveq
  24:   c3                      retq

显而易见,生成了两个模板函数foo的实例化

### 静态常量整型变量 `static const int` 的用法 静态常量整型变量 (`static const int`) 是一种具有内部链接性的常量,在定义它的源文件内有效。这意味着该变量不会被其他翻译单元所访问。 #### 定义初始化 当声明一个 `static const int` 变量时,通常会在其第一次使用之前进行初始化: ```cpp // 文件 scope.cpp #include <iostream> namespace { static const int MAX_SIZE = 100; } void printMaxSize() { std::cout << "Maximum size is: " << MAX_SIZE << std::endl; } ``` 上述代码展示了如何在一个匿名命名空间中定义初始化了一个名为 `MAX_SIZE` 的静态常量[^1]。 #### 使用场景 ##### 函数参数传递优化 对于基本数据类型的参数,如果不需要修改,则可以考虑将其作为 `const` 参数传入,并且可以在类成员函数中设置为 `static const` 来节省内存开销: ```cpp class MyClass { public: static const int DEFAULT_VALUE; void setDefault(int& value) const { value = DEFAULT_VALUE; } }; const int MyClass::DEFAULT_VALUE = 42; // 类外初始化 ``` 这里展示了一种常见模式:将默认值设为类级别的静态常数。 ##### 编译时常量表达式 C++ 支持编译期计算,因此可以用作模板参数或数组大小等场合下的固定数值: ```cpp template<static const int N> struct ArrayWrapper { double data[N]; }; ``` 此结构体接受一个由外部指定的静态常量来决定数组的实际长度。 #### 常见错误及解决方法 尝试改变已声明为 `const` 的对象会导致编译失败;而试图通过指针间接更改则可能引发未定义行为。例如下面这段不合法的操作会触发编译器警告甚至报错: ```cpp int main(){ static const int VALUE = 98765; *(const_cast<int*>(&VALUE)) = 12345; // 不推荐的做法,可能导致程序崩溃或其他不可预测的结果 return 0; } ``` 这种做法违反了类型系统的安全性原则,应该避免这样做。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值