下面所介绍的条件是从网上找的,不一定正确
一.默认构造函数
条件:
没有定义任何构造函数
注 : 编译器生成的默认构造函数会调用基类和成员的默认构造函数,
如果基类和函数没有默认构造函数会编译报错
#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> >&&)