成员初始化列表

成员初始化列表

1. 何时必须用成员初始化列表

  • 如果这个成员是个引用;
  • 如果是个const类型成员;
  • 如果这个类是继承一个基类,并且基类中有构造函数,这个构造函数里边还有参数;
  • 如果你的成员变量类型是某个类类型,而这个类的构造函数带参数时;

2. 使用初始化列表的优势

2.1 没有使用初始化列表

#include<iostream>
using namespace std;

class X {
public:
  int m_value;

public:
  X() {
    m_value = 0;
    printf("this = %p ", this);
    printf("X()默认构造函数被调用\n");
  }

  X(int value)
    : m_value(value) {
    printf("this = %p ", this);
    printf("X(int)构造函数被调用\n");
  }

  X(const X& other)
    : m_value(other.m_value) {
    printf("this = %p ", this);
    printf("X拷贝构造函数被调用\n");
  }

  X& operator=(const X& other) {
    m_value = other.m_value;
    printf("this = %p ", this);
    printf("X拷贝赋值函数被调用\n");
    return *this;
  }

  ~X() {
    printf("this = %p ", this);
    printf("X析构函数被调用\n");
  }
};

class A {
public:
  int m_aElement;
  X objX;

public:
  A(int value) {
    objX = value;
    m_aElement = 500;
  }
};

int main()
{
  A objA(1000);
}

输出:

this = 0x7ffff9c8b054 X()默认构造函数被调用

this = 0x7ffff9c8b024 X(int)构造函数被调用
this = 0x7ffff9c8b054 X拷贝赋值函数被调用
this = 0x7ffff9c8b024 X析构函数被调用

this = 0x7ffff9c8b054 X析构函数被调用

查看汇编代码:

Dump of assembler code for function A::A(int):
=> 0x00005555555553aa <+0>:     endbr64 
   0x00005555555553ae <+4>:     push   rbp
   0x00005555555553af <+5>:     mov    rbp,rsp
   0x00005555555553b2 <+8>:     push   rbx
   0x00005555555553b3 <+9>:     sub    rsp,0x28
   0x00005555555553b7 <+13>:    mov    QWORD PTR [rbp-0x28],rdi
   0x00005555555553bb <+17>:    mov    DWORD PTR [rbp-0x2c],esi
   0x00005555555553be <+20>:    mov    rax,QWORD PTR fs:0x28
   0x00005555555553c7 <+29>:    mov    QWORD PTR [rbp-0x18],rax
   0x00005555555553cb <+33>:    xor    eax,eax
   0x00005555555553cd <+35>:    mov    rax,QWORD PTR [rbp-0x28]
   0x00005555555553d1 <+39>:    add    rax,0x4
   0x00005555555553d5 <+43>:    mov    rdi,rax
   # 最开始就调用X的默认构造函数将 objX 构造出来了
   0x00005555555553d8 <+46>:    call   0x5555555552a2 <X::X()>
   0x00005555555553dd <+51>:    mov    edx,DWORD PTR [rbp-0x2c]
   0x00005555555553e0 <+54>:    lea    rax,[rbp-0x1c]
   0x00005555555553e4 <+58>:    mov    esi,edx
   0x00005555555553e6 <+60>:    mov    rdi,rax
   # 调用X::X(int)构造一个临时对象
   0x00005555555553e9 <+63>:    call   0x5555555552e4 <X::X(int)>
   0x00005555555553ee <+68>:    mov    rax,QWORD PTR [rbp-0x28]
   0x00005555555553f2 <+72>:    lea    rdx,[rax+0x4]
   0x00005555555553f6 <+76>:    lea    rax,[rbp-0x1c]
   0x00005555555553fa <+80>:    mov    rsi,rax
   0x00005555555553fd <+83>:    mov    rdi,rdx
   # 调用拷贝赋值函数将临时对象赋值给 objX
   0x0000555555555400 <+86>:    call   0x555555555328 <X::operator=(X const&)>
   0x0000555555555405 <+91>:    lea    rax,[rbp-0x1c]
   0x0000555555555409 <+95>:    mov    rdi,rax
   # 将临时对象析构
   0x000055555555540c <+98>:    call   0x555555555372 <X::~X()>
   0x0000555555555411 <+103>:   mov    rax,QWORD PTR [rbp-0x28]
   0x0000555555555415 <+107>:   mov    DWORD PTR [rax],0x1f4
   0x000055555555541b <+113>:   nop
   0x000055555555541c <+114>:   mov    rax,QWORD PTR [rbp-0x18]
   0x0000555555555420 <+118>:   xor    rax,QWORD PTR fs:0x28
   0x0000555555555429 <+127>:   je     0x555555555469 <A::A(int)+191>
   0x000055555555542b <+129>:   jmp    0x555555555464 <A::A(int)+186>
   0x000055555555542d <+131>:   endbr64 

2.2 使用初始化列表

/* ************************************************************************
> File Name:     test.cpp
> Author:        niu0217
> Created Time:  Tue 30 Jul 2024 07:59:02 AM CST
> Description:   
 ************************************************************************/

// g++ -g -o test test.cpp

#include<iostream>
using namespace std;

class X {
public:
  int m_value;

public:
  X() {
    m_value = 0;
    printf("this = %p ", this);
    printf("X()默认构造函数被调用\n");
  }

  X(int value)
    : m_value(value) {
    printf("this = %p ", this);
    printf("X(int)构造函数被调用\n");
  }

  X(const X& other)
    : m_value(other.m_value) {
    printf("this = %p ", this);
    printf("X拷贝构造函数被调用\n");
  }

  X& operator=(const X& other) {
    m_value = other.m_value;
    printf("this = %p ", this);
    printf("X拷贝赋值函数被调用\n");
    return *this;
  }

  ~X() {
    printf("this = %p ", this);
    printf("X析构函数被调用\n");
  }
};

class A {
public:
  int m_aElement;
  X objX;

public:
  A(int value)
    : m_aElement(100), objX(value) {
    }
};

int main()
{
  A objA(1000);
}

输出:

this = 0x7fff72ea7b24 X(int)构造函数被调用
this = 0x7fff72ea7b24 X析构函数被调用

汇编代码:

Dump of assembler code for function A::A(int):
=> 0x00005555555552fe <+0>:     endbr64 
   0x0000555555555302 <+4>:     push   rbp
   0x0000555555555303 <+5>:     mov    rbp,rsp
   0x0000555555555306 <+8>:     sub    rsp,0x10
   0x000055555555530a <+12>:    mov    QWORD PTR [rbp-0x8],rdi
   0x000055555555530e <+16>:    mov    DWORD PTR [rbp-0xc],esi
   0x0000555555555311 <+19>:    mov    rax,QWORD PTR [rbp-0x8]
   0x0000555555555315 <+23>:    mov    DWORD PTR [rax],0x64
   0x000055555555531b <+29>:    mov    rax,QWORD PTR [rbp-0x8]
   0x000055555555531f <+33>:    lea    rdx,[rax+0x4]
   0x0000555555555323 <+37>:    mov    eax,DWORD PTR [rbp-0xc]
   0x0000555555555326 <+40>:    mov    esi,eax
   0x0000555555555328 <+42>:    mov    rdi,rdx
   0x000055555555532b <+45>:    call   0x555555555282 <X::X(int)>
   0x0000555555555330 <+50>:    nop
   0x0000555555555331 <+51>:    leave  
   0x0000555555555332 <+52>:    ret    
End of assembler dump.

3. 注意事项

成员初始化列表是按照变量定义顺序来初始化的,所以一定要注意!!!

代码:

#include<iostream>
using namespace std;

class A {
public:
    int m_aElement;
    int m_bElement;
    A() : m_bElement{1000}, m_aElement{m_bElement} {

    }
};

int main()
{
    A objA;
    cout<<objA.m_aElement<<endl; //32765
    cout<<objA.m_bElement<<endl; //1000
}

我们希望两个都是1000,但是这段代码会先给m_aElement进行赋值,因为m_aElement先定义,但是这个时候m_bElement没有赋值,所以是个随机值,因此不会是1000。而接下来给m_bElement赋值变成1000,不会影响到m_aElement

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落后的炫幕あ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值