Search code examples
c++pointersinitializer-list

Member initializer list, pointer initialization without argument


In a large framework which used to use many smart pointers and now uses raw pointers, I come across situations like this quite often:

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

The reason is because int* m used to be a smart pointer and so the initializer list called a default constructor. Now that int* m is a raw pointer I am not certain if this is equivalent to:

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

Without the explicit nullptr is A::m still initialized to zero? A look at no optimization objdump -d makes it appear to be yes but I am not certain. The reason I feel that the answer is yes is due to this line in the objdump -d (I posted more of the objdump -d below):

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

Little program that tries to find undefined behavior:

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;
}

Compilation, execution, and return value:

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

Relevant assembly sections from 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)

Solution

  • Empty () initializer stands for default-initialization in C++98 and for value-initialization in C++03 and later. For scalar types (including pointers) value-initialization/default-initialization leads to zero-initialization.

    Which means that in your case m() and m(nullptr) will have exactly the same effect: in both cases m is initialized as a null pointer. In C++ it was like that since the beginning of standardized times.