C++学习之编译器生成各种构造函数

下面所介绍的条件是从网上找的,不一定正确

一.默认构造函数

条件:

没有定义任何构造函数

注 : 编译器生成的默认构造函数会调用基类和成员的默认构造函数,
      如果基类和函数没有默认构造函数会编译报错
#include <iostream>
#include <string>

class A { 
public:
std::string a_; 
int b_; 
};

int main() {
  A a;
  std::cout << a.a_ << " " << a.b_ << std::endl;
  return 0;
}

通过反汇编可以看见生成下面的默认构造函数A::A(),其中调用了string的构造函数basic_string()

Dump of assembler code for function A::A():
   0x0000000000400cc0 <+0>:	push   %rbp
   0x0000000000400cc1 <+1>:	mov    %rsp,%rbp
   0x0000000000400cc4 <+4>:	sub    $0x10,%rsp
   0x0000000000400cc8 <+8>:	mov    %rdi,-0x8(%rbp)
=> 0x0000000000400ccc <+12>:	mov    -0x8(%rbp),%rax
   0x0000000000400cd0 <+16>:	mov    %rax,%rdi
   0x0000000000400cd3 <+19>:	callq  0x400a40 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev@plt>
   0x0000000000400cd8 <+24>:	nop
   0x0000000000400cd9 <+25>:	leaveq 
   0x0000000000400cda <+26>:	retq   
$ c++filt _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string()

如果定义了构造函数则编译器不会生成默认构造函数

#include <iostream>
#include <string>

class A { 
public:
A(int b) : b_(b) {}
public:
std::string a_; 
int b_; 
};

int main() {
  A a;
  std::cout << a.a_ << " " << a.b_ << std::endl;
  return 0;
}

编译时会报错提示没有默认构造函数

main.cc: In function ‘int main()’:
main.cc:13:5: error: no matching function for call to ‘A::A()’
   A a;
     ^
main.cc:6:1: note: candidate: A::A(int)
 A(int b) : b_(b) {}
 ^
main.cc:6:1: note:   candidate expects 1 argument, 0 provided
main.cc:4:7: note: candidate: A::A(const A&)
 class A {
       ^
main.cc:4:7: note:   candidate expects 1 argument, 0 provided
main.cc:4:7: note: candidate: A::A(A&&)
main.cc:4:7: note:   candidate expects 1 argument, 0 provided
Makefile:2: recipe for target 'aa' failed
make: *** [aa] Error 1

二.拷贝构造函数

生成条件:

1 有虚函数,需要生成虚表,初始化虚表指针

2 有虚基类,需要初始化虚指针

3 基类有默认的拷贝构造函数

4 有一个成员对象有默认的构造函数(???没有默认构造函数也生成了)

编译器生成的拷贝构造函数会先调用基类的拷贝构造函数然后调用成员的拷贝构造函数

#include <iostream>
#include <string>

class A { 
public:
std::string a;
};

class C : A {
public:
C() = delete;
C(int a) : a_(a) {}
private:
int a_; 
std::string s;
};

int main() {
  C a(1);
  C b = a;  
  return 0;
}

   0x0000000000400b70 <+0>:	push   %rbp
   0x0000000000400b71 <+1>:	mov    %rsp,%rbp
   0x0000000000400b74 <+4>:	push   %rbx
   0x0000000000400b75 <+5>:	sub    $0x18,%rsp
   0x0000000000400b79 <+9>:	mov    %rdi,-0x18(%rbp)
   0x0000000000400b7d <+13>:	mov    %rsi,-0x20(%rbp)
=> 0x0000000000400b81 <+17>:	mov    -0x20(%rbp),%rdx
   0x0000000000400b85 <+21>:	mov    -0x18(%rbp),%rax
   0x0000000000400b89 <+25>:	mov    %rdx,%rsi
   0x0000000000400b8c <+28>:	mov    %rax,%rdi
   0x0000000000400b8f <+31>:	callq  0x400b4a <A::A(A const&)>
   0x0000000000400b94 <+36>:	mov    -0x20(%rbp),%rax
   0x0000000000400b98 <+40>:	mov    0x20(%rax),%edx
   0x0000000000400b9b <+43>:	mov    -0x18(%rbp),%rax
   0x0000000000400b9f <+47>:	mov    %edx,0x20(%rax)
   0x0000000000400ba2 <+50>:	mov    -0x20(%rbp),%rax
   0x0000000000400ba6 <+54>:	lea    0x28(%rax),%rdx
   0x0000000000400baa <+58>:	mov    -0x18(%rbp),%rax
   0x0000000000400bae <+62>:	add    $0x28,%rax
   0x0000000000400bb2 <+66>:	mov    %rdx,%rsi
   0x0000000000400bb5 <+69>:	mov    %rax,%rdi
   0x0000000000400bb8 <+72>:	callq  0x400880 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_@plt>
   0x0000000000400bbd <+77>:	jmp    0x400bd9 <C::C(C const&)+105>
   0x0000000000400bbf <+79>:	mov    %rax,%rbx
   0x0000000000400bc2 <+82>:	mov    -0x18(%rbp),%rax
   0x0000000000400bc6 <+86>:	mov    %rax,%rdi
   0x0000000000400bc9 <+89>:	callq  0x400aca <A::~A()>
   0x0000000000400bce <+94>:	mov    %rbx,%rax
   0x0000000000400bd1 <+97>:	mov    %rax,%rdi
   0x0000000000400bd4 <+100>:	callq  0x4008a0 <_Unwind_Resume@plt>
   0x0000000000400bd9 <+105>:	add    $0x18,%rsp
   0x0000000000400bdd <+109>:	pop    %rbx
   0x0000000000400bde <+110>:	pop    %rbp
   0x0000000000400bdf <+111>:	retq   
$ c++filt _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)

自定义的拷贝构造函数如果没有对基类和成员进行设置,会调用默认构造函数

#include <iostream>
#include <string>

class A {
public:
std::string a;
};

class C : A {
public:
C() = delete;
C(int a) : a_(a) {}
C(const C& c) {
  a_ = c.a_;
}
private:
int a_;
std::string s;
A aa_;
};

int main() {
  C a(1);
  C b = a; 
  return 0;
}
13	C(const C& c) {
   0x000000000040098e <+0>:	push   %rbp
   0x000000000040098f <+1>:	mov    %rsp,%rbp
   0x0000000000400992 <+4>:	sub    $0x10,%rsp
   0x0000000000400996 <+8>:	mov    %rdi,-0x8(%rbp)
   0x000000000040099a <+12>:	mov    %rsi,-0x10(%rbp)
=> 0x000000000040099e <+16>:	mov    -0x8(%rbp),%rax
   0x00000000004009a2 <+20>:	mov    %rax,%rdi
   0x00000000004009a5 <+23>:	callq  0x40090e <A::A()>
   0x00000000004009aa <+28>:	mov    -0x8(%rbp),%rax
   0x00000000004009ae <+32>:	add    $0x28,%rax
   0x00000000004009b2 <+36>:	mov    %rax,%rdi
   0x00000000004009b5 <+39>:	callq  0x400700 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev@plt>
   0x00000000004009ba <+44>:	mov    -0x8(%rbp),%rax
   0x00000000004009be <+48>:	add    $0x48,%rax
   0x00000000004009c2 <+52>:	mov    %rax,%rdi
   0x00000000004009c5 <+55>:	callq  0x40090e <A::A()>
$ c++filt _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string()

三.移动构造函数

条件

if (定义了 拷贝构造函数 、 拷贝赋值运算符 或 析构函数) {
    不会合成移动构造函数和移动赋值运算符。此时会使用对应 拷贝 操作来代替 移动。
    如果没有定义拷贝构造函数,编译器会生成拷贝构造函数。
}
else if (类的所有成员都可以移动) {
    则会为类合成移动构造函数或移动赋值运算符。
}
else {
    合成拷贝构造函数和拷贝复制运算符。
}

下例中b=std::move(a),如果定义了拷贝构造函数C(const C&),则不会生成移动构造函数
如果同时定义了拷贝构造函数C(const C&)和移动构造函数C(C&&)会匹配移动构造函数。

编译器生成的移动构造函数时,会调用基类的移动构造函数和成员的移动构造函数

#include <iostream>
#include <string>

class A {
public:
std::string a;
};

class C : A {
public:
C() = delete;
C(int a) : a_(a) {}
private:
int a_;
std::string s;
A aa_;
};

int main() {
  C a(1);
  C b = std::move(a); 
  return 0;
}
   0x0000000000400a8a <+0>:	push   %rbp
   0x0000000000400a8b <+1>:	mov    %rsp,%rbp
   0x0000000000400a8e <+4>:	sub    $0x10,%rsp
   0x0000000000400a92 <+8>:	mov    %rdi,-0x8(%rbp)
   0x0000000000400a96 <+12>:	mov    %rsi,-0x10(%rbp)
=> 0x0000000000400a9a <+16>:	mov    -0x10(%rbp),%rdx
   0x0000000000400a9e <+20>:	mov    -0x8(%rbp),%rax
   0x0000000000400aa2 <+24>:	mov    %rdx,%rsi
   0x0000000000400aa5 <+27>:	mov    %rax,%rdi
   0x0000000000400aa8 <+30>:	callq  0x400a64 <A::A(A&&)>
   0x0000000000400aad <+35>:	mov    -0x10(%rbp),%rax
   0x0000000000400ab1 <+39>:	mov    0x20(%rax),%edx
   0x0000000000400ab4 <+42>:	mov    -0x8(%rbp),%rax
   0x0000000000400ab8 <+46>:	mov    %edx,0x20(%rax)
   0x0000000000400abb <+49>:	mov    -0x10(%rbp),%rax
   0x0000000000400abf <+53>:	lea    0x28(%rax),%rdx
   0x0000000000400ac3 <+57>:	mov    -0x8(%rbp),%rax
   0x0000000000400ac7 <+61>:	add    $0x28,%rax
   0x0000000000400acb <+65>:	mov    %rdx,%rsi
   0x0000000000400ace <+68>:	mov    %rax,%rdi
   0x0000000000400ad1 <+71>:	callq  0x400780 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EOS4_@plt>
   0x0000000000400ad6 <+76>:	mov    -0x10(%rbp),%rax
   0x0000000000400ada <+80>:	lea    0x48(%rax),%rdx
   0x0000000000400ade <+84>:	mov    -0x8(%rbp),%rax
   0x0000000000400ae2 <+88>:	add    $0x48,%rax
   0x0000000000400ae6 <+92>:	mov    %rdx,%rsi
   0x0000000000400ae9 <+95>:	mov    %rax,%rdi
   0x0000000000400aec <+98>:	callq  0x400a64 <A::A(A&&)>
   0x0000000000400af1 <+103>:	nop
   0x0000000000400af2 <+104>:	leaveq 
   0x0000000000400af3 <+105>:	retq   

End of assembler dump.
$ c++filt _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EOS4_
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值