成员初始值设定项列表,不带参数的指针初始化

Member initializer list, pointer initialization without argument

本文关键字:参数 初始化 指针 列表 成员      更新时间:2023-10-16

在一个曾经使用许多智能指针而现在使用原始指针的大型框架中,我经常遇到这样的情况:

class A {
public:
        int* m;
        A() : m() {}
};

原因是int* m曾经是一个智能指针,因此初始值设定项列表称为默认构造函数。现在int* m是一个原始指针,我不确定这是否等同于:

class A {
public:
        int* m;
        A() : m(nullptr) {}
};

没有显式nullptr A::m仍然初始化为零吗?看看没有优化objdump -d它似乎是肯定的,但我不确定。我觉得答案是肯定的原因是由于objdump -d中的这一行(我在下面发布了更多objdump -d):

  400644:       48 c7 00 00 00 00 00    movq   $0x0,(%rax)

试图找到未定义行为的小程序

class A {
public:
        int* m;
        A() : m(nullptr) {}
};
int main() {
        A buf[1000000];
        unsigned int count = 0;
        for (unsigned int i = 0; i < 1000000; ++i) {
                count += buf[i].m ? 1 : 0;
        }
        return count;
}

编译、执行和返回值

g++ -std=c++14 -O0 foo.cpp
./a.out; echo $?
0

objdump -d的相关装配部分

00000000004005b8 <main>:
  4005b8:       55                      push   %rbp
  4005b9:       48 89 e5                mov    %rsp,%rbp
  4005bc:       41 54                   push   %r12
  4005be:       53                      push   %rbx
  4005bf:       48 81 ec 10 12 7a 00    sub    $0x7a1210,%rsp
  4005c6:       48 8d 85 e0 ed 85 ff    lea    -0x7a1220(%rbp),%rax
  4005cd:       bb 3f 42 0f 00          mov    $0xf423f,%ebx
  4005d2:       49 89 c4                mov    %rax,%r12
  4005d5:       eb 10                   jmp    4005e7 <main+0x2f>
  4005d7:       4c 89 e7                mov    %r12,%rdi
  4005da:       e8 59 00 00 00          callq  400638 <_ZN1AC1Ev>
  4005df:       49 83 c4 08             add    $0x8,%r12
  4005e3:       48 83 eb 01             sub    $0x1,%rbx
  4005e7:       48 83 fb ff             cmp    $0xffffffffffffffff,%rbx
  4005eb:       75 ea                   jne    4005d7 <main+0x1f>
  4005ed:       c7 45 ec 00 00 00 00    movl   $0x0,-0x14(%rbp)
  4005f4:       c7 45 e8 00 00 00 00    movl   $0x0,-0x18(%rbp)
  4005fb:       eb 23                   jmp    400620 <main+0x68>
  4005fd:       8b 45 e8                mov    -0x18(%rbp),%eax
  400600:       48 8b 84 c5 e0 ed 85    mov    -0x7a1220(%rbp,%rax,8),%rax
  400607:       ff
  400608:       48 85 c0                test   %rax,%rax
  40060b:       74 07                   je     400614 <main+0x5c>
  40060d:       b8 01 00 00 00          mov    $0x1,%eax
  400612:       eb 05                   jmp    400619 <main+0x61>
  400614:       b8 00 00 00 00          mov    $0x0,%eax
  400619:       01 45 ec                add    %eax,-0x14(%rbp)
  40061c:       83 45 e8 01             addl   $0x1,-0x18(%rbp)
  400620:       81 7d e8 3f 42 0f 00    cmpl   $0xf423f,-0x18(%rbp)
  400627:       76 d4                   jbe    4005fd <main+0x45>
  400629:       8b 45 ec                mov    -0x14(%rbp),%eax
  40062c:       48 81 c4 10 12 7a 00    add    $0x7a1210,%rsp
  400633:       5b                      pop    %rbx
  400634:       41 5c                   pop    %r12
  400636:       5d                      pop    %rbp
  400637:       c3                      retq
0000000000400638 <_ZN1AC1Ev>:
  400638:       55                      push   %rbp
  400639:       48 89 e5                mov    %rsp,%rbp
  40063c:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  400640:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  400644:       48 c7 00 00 00 00 00    movq   $0x0,(%rax)
  40064b:       5d                      pop    %rbp
  40064c:       c3                      retq
  40064d:       0f 1f 00                nopl   (%rax)

()初始值设定项代表 C++98 中的默认初始化和 C++03 及更高版本中的值初始化。对于标量类型(包括指针),值初始化/默认初始化会导致零初始化

这意味着在您的情况下,m()m(nullptr) 将具有完全相同的效果:在这两种情况下,m 都初始化为 null 指针。在C++,自标准化时代开始以来就是这样。