构造函数语义学——Default Constructor 篇

这一章原书主要分析了:编译器关于对象构造过程的干涉,即在对象构造这个过程中,编译器到底在背后做了什么

这一章的重点在于 default constructor 和 copy constructor 的相关知识

Default Constructor 详解

一些 C++的书籍中告诉我们:如果不写任何构造函数,那么编译器会为我们生成一个默认构造函数(default constructor)

那么,针对这个观点,有两个问题:

  • 这句话是正确的吗?
  • 如果这句话是正确的,那么编译器为我们生成的 default constructor 里有什么动作吗?

1 这句话是正确的吗?

这句话大体是正确的,但是 cppreference 中有更详细的说明:

若不对类类型(struct、class 或 union)提供任何用户声明的构造函数,则编译器将始终声明一个作为其类的 inline public 成员的默认构造函数。

当存在用户声明的构造函数时,用户仍可以关键词 default 强制编译器自动生成原本隐式声明的默认构造函数。

上面这段话告诉我们:只有在没有任何用户声明的构造函数的情况下,编译器才会声明一个 default constructor。换言之,只要有任何用户声明的构造函数(不一定要是 default constructor),哪怕该构造函数不是 default constructor,那么编译器就不会为我们生成 default constructor(除了强制编译器生成)

隐式定义的默认构造函数

若隐式声明的默认构造函数未被定义为弃置的,则当其被 ODR 式使用时,它为编译器所定义(即生成函数体并编译之),且它与拥有空函数体和空初始化器列表的用户定义的构造函数有严格相同的效果。即它调用这个类的各基类和各非静态成员的默认构造函数

有没有什么证据能证明编译器真的隐式定义了一个默认构造函数呢?有,考虑如下的一个类:

  1. class A {};

这个类中没有任何成员变量或者成员函数,此时打印出该类的相关信息如下:

  1. // clang -cc1 -fdump-record-layouts -emit-llvm main.cpp 命令
  2. *** Dumping AST Record Layout
  3. 0 | class A (empty)
  4. | [sizeof=1, dsize=1, align=1,
  5. | nvsize=1, nvalign=1]
  6. *** Dumping IRgen Record Layout
  7. Record: CXXRecordDecl 0x55c6626abc70 <main.cpp:1:1, line:3:1> line:1:7 referenced class A definition
  8. |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
  9. | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr
  10. | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
  11. | |-MoveConstructor exists simple trivial
  12. | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
  13. | |-MoveAssignment exists simple trivial needs_implicit
  14. | `-Destructor simple irrelevant trivial needs_implicit
  15. |-CXXRecordDecl 0x55c6626abd88 <col:1, col:7> col:7 implicit class A
  16. |-CXXConstructorDecl 0x55c6626ac020 <col:7> col:7 implicit used constexpr A 'void () noexcept' inline default trivial
  17. | `-CompoundStmt 0x55c6626ac4b8 <col:7>
  18. |-CXXConstructorDecl 0x55c6626ac158 <col:7> col:7 implicit constexpr A 'void (const A &)' inline default trivial noexcept-unevaluated 0x55c6626ac158
  19. | `-ParmVarDecl 0x55c6626ac268 <col:7> col:7 'const A &'
  20. `-CXXConstructorDecl 0x55c6626ac308 <col:7> col:7 implicit constexpr A 'void (A &&)' inline default trivial noexcept-unevaluated 0x55c6626ac308
  21. `-ParmVarDecl 0x55c6626ac418 <col:7> col:7 'A &&'
  22. Layout: <CGRecordLayout
  23. LLVMType:%class.A = type { i8 }
  24. NonVirtualBaseLLVMType:%class.A = type { i8 }
  25. IsZeroInitializable:1
  26. BitFields:[
  27. ]>

DefaultConstructor exists trivial constexpr defaulted_is_constexpr这一行证明了这个类存在一个 default constructor 且是 trivial 的,而我们并没有声明和定义,那么自然是编译器为我们自动生成的

2 隐式定义的默认构造函数里发生了什么?

在解决这个问题之前,首先有需要了解一些额外的知识:

default constructor 是分为 trivial default constructor(平凡默认构造函数,即不进行任何构造动作)和 non trivial default constructor(非平凡默认构造函数)

什么情况下 default constructor 是 trivial 的呢?cppreference 中说明了这一点:

当下列各项全部为真时,类 T 的默认构造函数为平凡的(平凡默认构造函数是不进行任何动作的构造函数。):

构造函数并非用户提供(即为隐式定义或于其首个声明中预置的)

T 没有虚成员函数

T 没有虚基类

T 没有拥有默认初始化器的非静态数据成员。

每个 T 的直接基类都拥有平凡默认构造函数

每个类类型的非静态成员都拥有平凡默认构造函数

注意上述条件中的第一点也说明了一个事实:如果构造函数是用户定义的,那么必定是非平凡的

再回到隐式定义的默认构造函数里发生了什么?这个问题,就需要从两个角度来考虑了:

  1. 当编译器隐式定义的默认构造函数是一个 trivial default constructor 时,那么该 trivial default constructor 什么也不做
  2. 那么当编译器隐式定义的默认构造函数是一个 non-trivial default constructor 时,该 non-trivial default constructor 调用这个类的各基类和各非静态成员的默认构造函数

通过以上,我们已经知道了编译器在特定情况下,真的会隐式定义一个 default constructor,也知道了在哪些情况下产生的 default constructor 是 trivial 的,那么再来详细的讨论一下,什么情况下,编译器生成的 default constructor 是 non-trivial 的

  1. 带有 default constructor 的 member class object

    如果一个 class A 没有任何的 constructor,但是它内含一个 member object,而该 member object 拥有 non-trivial default constructor,那么编译器为这个 class 隐式定义的 default constructor 是 non-trivial。为什么呢?因为编译器为该 class A 产生的 default constructor 里必须要调用 member object 的 non-trivial default constructor,否则无法完整的构造 class A 的对象

    仍然用代码来证明:

    1. class B {
    2. private:
    3. int b;
    4. public:
    5. B() {}
    6. };
    7. class A {
    8. private:
    9. B b;
    10. int a;
    11. };

    打印出 class A 的相关信息,如下:

    1. *** Dumping AST Record Layout
    2. 0 | class B
    3. 0 | int b
    4. | [sizeof=4, dsize=4, align=4,
    5. | nvsize=4, nvalign=4]
    6. *** Dumping AST Record Layout
    7. 0 | class A
    8. 0 | class B b
    9. 0 | int b
    10. 4 | int a
    11. | [sizeof=8, dsize=8, align=4,
    12. | nvsize=8, nvalign=4]
    13. *** Dumping IRgen Record Layout
    14. Record: CXXRecordDecl 0x55ba37562c50 <main.cpp:1:1, line:6:1> line:1:7 referenced class B definition
    15. |-DefinitionData pass_in_registers standard_layout trivially_copyable has_user_declared_ctor can_const_default_init
    16. | |-DefaultConstructor exists non_trivial user_provided
    17. | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
    18. | |-MoveConstructor exists simple trivial
    19. | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
    20. | |-MoveAssignment exists simple trivial needs_implicit
    21. | `-Destructor simple irrelevant trivial
    22. |-CXXRecordDecl 0x55ba37562d68 <col:1, col:7> col:7 implicit referenced class B
    23. |-AccessSpecDecl 0x55ba37562df8 <line:2:2, col:9> col:2 private
    24. |-FieldDecl 0x55ba37562e38 <line:3:3, col:7> col:7 b 'int'
    25. |-AccessSpecDecl 0x55ba37562e88 <line:4:2, col:8> col:2 public
    26. |-CXXConstructorDecl 0x55ba37562f38 <line:5:3, col:8> col:3 used B 'void ()'
    27. | `-CompoundStmt 0x55ba37562ff8 <col:7, col:8>
    28. |-CXXDestructorDecl 0x55ba37563590 <line:1:7> col:7 implicit ~B 'void ()' inline default trivial noexcept-unevaluated 0x55ba37563590
    29. |-CXXConstructorDecl 0x55ba375636c8 <col:7> col:7 implicit constexpr B 'void (const B &)' inline default trivial noexcept-unevaluated 0x55ba375636c8
    30. | `-ParmVarDecl 0x55ba375637d8 <col:7> col:7 'const B &'
    31. `-CXXConstructorDecl 0x55ba37563878 <col:7> col:7 implicit constexpr B 'void (B &&)' inline default trivial noexcept-unevaluated 0x55ba37563878
    32. `-ParmVarDecl 0x55ba37563988 <col:7> col:7 'B &&'
    33. Layout: <CGRecordLayout
    34. LLVMType:%class.B = type { i32 }
    35. NonVirtualBaseLLVMType:%class.B = type { i32 }
    36. IsZeroInitializable:1
    37. BitFields:[
    38. ]>
    39. *** Dumping IRgen Record Layout
    40. Record: CXXRecordDecl 0x55ba37563008 <main.cpp:8:1, line:12:1> line:8:7 referenced class A definition
    41. |-DefinitionData pass_in_registers standard_layout trivially_copyable
    42. | |-DefaultConstructor exists non_trivial
    43. | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
    44. | |-MoveConstructor exists simple trivial
    45. | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
    46. | |-MoveAssignment exists simple trivial needs_implicit
    47. | `-Destructor simple irrelevant trivial needs_implicit
    48. |-CXXRecordDecl 0x55ba37563128 <col:1, col:7> col:7 implicit class A
    49. |-AccessSpecDecl 0x55ba375631b8 <line:9:2, col:9> col:2 private
    50. |-FieldDecl 0x55ba375631f0 <line:10:3, col:5> col:5 b 'B'
    51. |-FieldDecl 0x55ba37563258 <line:11:3, col:7> col:7 a 'int'
    52. |-CXXConstructorDecl 0x55ba375634b0 <line:8:7> col:7 implicit used A 'void () noexcept(false)' inline default
    53. | |-CXXCtorInitializer Field 0x55ba375631f0 'b' 'B'
    54. | | `-CXXConstructExpr 0x55ba37592090 <col:7> 'B' 'void ()'
    55. | `-CompoundStmt 0x55ba375920e8 <col:7>
    56. |-CXXConstructorDecl 0x55ba375639f8 <col:7> col:7 implicit constexpr A 'void (const A &)' inline default trivial noexcept-unevaluated 0x55ba375639f8
    57. | `-ParmVarDecl 0x55ba37591e30 <col:7> col:7 'const A &'
    58. `-CXXConstructorDecl 0x55ba37591ec8 <col:7> col:7 implicit constexpr A 'void (A &&)' inline default trivial noexcept-unevaluated 0x55ba37591ec8
    59. `-ParmVarDecl 0x55ba37591fd8 <col:7> col:7 'A &&'
    60. Layout: <CGRecordLayout
    61. LLVMType:%class.A = type { %class.B, i32 }
    62. NonVirtualBaseLLVMType:%class.A = type { %class.B, i32 }
    63. IsZeroInitializable:1
    64. BitFields:[
    65. ]>
    66. DefaultConstructor exists non_trivial user_provided

    这一行中说明 class B 是用户定义的一个 default constructor,是 non-trivial 的

    1. DefaultConstructor exists non_trivial

    第二行中则说明 class A 的 default constructor 也是存在的且是 non-trivial 的。在这个 non-trivial default constructor 中,编译器会调用 class B 的 default constructor 来初始化 class A 的对象中的 b 成分,但是不会初始化 a(编译器看来,初始化 b 是它的工作,但是初始化 a 是程序员的工作)

    1. |-CXXConstructorDecl 0x55ba375634b0 <line:8:7> col:7 implicit used A 'void () noexcept(false)' inline default
    2. | |-CXXCtorInitializer Field 0x55ba375631f0 'b' 'B'
    3. | | `-CXXConstructExpr 0x55ba37592090 <col:7> 'B' 'void ()'
    4. | `-CompoundStmt 0x55ba375920e8 <col:7>

    这一段则说明了在 class A 的 default constructor 中调用了 class B 的 default constructor

    如果,class A 中提供了一个 default constructor,那么编译器会怎么办呢?

    1. class B {
    2. private:
    3. int b;
    4. public:
    5. B() {}
    6. };
    7. class A {
    8. private:
    9. B b;
    10. int a;
    11. public:
    12. A() {
    13. a = 0;
    14. }
    15. };

    打印相关信息如下:

    1. *** Dumping AST Record Layout
    2. 0 | class B
    3. 0 | int b
    4. | [sizeof=4, dsize=4, align=4,
    5. | nvsize=4, nvalign=4]
    6. *** Dumping AST Record Layout
    7. 0 | class A
    8. 0 | class B b
    9. 0 | int b
    10. 4 | int a
    11. | [sizeof=8, dsize=8, align=4,
    12. | nvsize=8, nvalign=4]
    13. *** Dumping IRgen Record Layout
    14. Record: CXXRecordDecl 0x5641e5965d20 <main.cpp:1:1, line:6:1> line:1:7 referenced class B definition
    15. |-DefinitionData pass_in_registers standard_layout trivially_copyable has_user_declared_ctor can_const_default_init
    16. | |-DefaultConstructor exists non_trivial user_provided
    17. | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
    18. | |-MoveConstructor exists simple trivial
    19. | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
    20. | |-MoveAssignment exists simple trivial needs_implicit
    21. | `-Destructor simple irrelevant trivial
    22. |-CXXRecordDecl 0x5641e5965e38 <col:1, col:7> col:7 implicit referenced class B
    23. |-AccessSpecDecl 0x5641e5965ec8 <line:2:2, col:9> col:2 private
    24. |-FieldDecl 0x5641e5965f08 <line:3:3, col:7> col:7 b 'int'
    25. |-AccessSpecDecl 0x5641e5965f58 <line:4:2, col:8> col:2 public
    26. |-CXXConstructorDecl 0x5641e5966008 <line:5:3, col:8> col:3 used B 'void ()'
    27. | `-CompoundStmt 0x5641e59660c8 <col:7, col:8>
    28. |-CXXConstructorDecl 0x5641e59664e8 <line:1:7> col:7 implicit constexpr B 'void (const B &)' inline default trivial noexcept-unevaluated 0x5641e59664e8
    29. | `-ParmVarDecl 0x5641e59665f8 <col:7> col:7 'const B &'
    30. |-CXXConstructorDecl 0x5641e5966698 <col:7> col:7 implicit constexpr B 'void (B &&)' inline default trivial noexcept-unevaluated 0x5641e5966698
    31. | `-ParmVarDecl 0x5641e59667a8 <col:7> col:7 'B &&'
    32. `-CXXDestructorDecl 0x5641e59953b8 <col:7> col:7 implicit ~B 'void ()' inline default trivial noexcept-unevaluated 0x5641e59953b8
    33. Layout: <CGRecordLayout
    34. LLVMType:%class.B = type { i32 }
    35. NonVirtualBaseLLVMType:%class.B = type { i32 }
    36. IsZeroInitializable:1
    37. BitFields:[
    38. ]>
    39. *** Dumping IRgen Record Layout
    40. Record: CXXRecordDecl 0x5641e59660d8 <main.cpp:8:1, line:16:1> line:8:7 referenced class A definition
    41. |-DefinitionData pass_in_registers standard_layout trivially_copyable has_user_declared_ctor can_const_default_init
    42. | |-DefaultConstructor exists non_trivial user_provided
    43. | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
    44. | |-MoveConstructor exists simple trivial
    45. | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
    46. | |-MoveAssignment exists simple trivial needs_implicit
    47. | `-Destructor simple irrelevant trivial needs_implicit
    48. |-CXXRecordDecl 0x5641e59661f8 <col:1, col:7> col:7 implicit referenced class A
    49. |-AccessSpecDecl 0x5641e5966288 <line:9:2, col:9> col:2 private
    50. |-FieldDecl 0x5641e59662c0 <line:10:3, col:5> col:5 b 'B'
    51. |-FieldDecl 0x5641e5966328 <line:11:3, col:7> col:7 referenced a 'int'
    52. |-AccessSpecDecl 0x5641e5966378 <line:12:2, col:8> col:2 public
    53. |-CXXConstructorDecl 0x5641e5966400 <line:13:3, line:15:3> line:13:3 used A 'void ()'
    54. | |-CXXCtorInitializer Field 0x5641e59662c0 'b' 'B'
    55. | | `-CXXConstructExpr 0x5641e5966818 <col:3> 'B' 'void ()'
    56. | `-CompoundStmt 0x5641e59668f0 <col:7, line:15:3>
    57. | `-BinaryOperator 0x5641e59668d0 <line:14:5, col:9> 'int' lvalue '='
    58. | |-MemberExpr 0x5641e5966880 <col:5> 'int' lvalue ->a 0x5641e5966328
    59. | | `-CXXThisExpr 0x5641e5966870 <col:5> 'A *' implicit this
    60. | `-IntegerLiteral 0x5641e59668b0 <col:9> 'int' 0
    61. |-CXXConstructorDecl 0x5641e5966b18 <line:8:7> col:7 implicit constexpr A 'void (const A &)' inline default trivial noexcept-unevaluated 0x5641e5966b18
    62. | `-ParmVarDecl 0x5641e5995348 <col:7> col:7 'const A &'
    63. `-CXXConstructorDecl 0x5641e59954c8 <col:7> col:7 implicit constexpr A 'void (A &&)' inline default trivial noexcept-unevaluated 0x5641e59954c8
    64. `-ParmVarDecl 0x5641e59955d8 <col:7> col:7 'A &&'
    65. Layout: <CGRecordLayout
    66. LLVMType:%class.A = type { %class.B, i32 }
    67. NonVirtualBaseLLVMType:%class.A = type { %class.B, i32 }
    68. IsZeroInitializable:1
    69. BitFields:[
    70. ]>
    71. |-CXXConstructorDecl 0x5641e5966400 <line:13:3, line:15:3> line:13:3 used A 'void ()'
    72. | |-CXXCtorInitializer Field 0x5641e59662c0 'b' 'B'
    73. | | `-CXXConstructExpr 0x5641e5966818 <col:3> 'B' 'void ()'

    可以发现,在我们定义的 default constructor 中,也调用了 class B 的 default constructor,且这个调用动作发生在 a = 0;这条语句之前,然而我们并没有写呀,这便是编译器自动安插的调用

    也就是说,在这种情况下,编译器会扩充已经存在 constructor,在其中安插一些代码,使得在该 constgructor 中所有代码执行之前,先调用必要的 default constructor

    如果 class A 提供了其他的 constructors,但是没有提供 default constructor,那么编译器还会为我们生成 default consturctor 吗?

    这个之前已经说过了,答案是不会。依然用事实证明:

    1. class B {
    2. private:
    3. int b;
    4. public:
    5. B() {}
    6. };
    7. class A {
    8. private:
    9. B b;
    10. int a;
    11. public:
    12. A(int _a) : a(_a) {}
    13. };
    14. int main() {
    15. A a(1);
    16. }
    17. *** Dumping AST Record Layout
    18. 0 | class B
    19. 0 | int b
    20. | [sizeof=4, dsize=4, align=4,
    21. | nvsize=4, nvalign=4]
    22. *** Dumping AST Record Layout
    23. 0 | class A
    24. 0 | class B b
    25. 0 | int b
    26. 4 | int a
    27. | [sizeof=8, dsize=8, align=4,
    28. | nvsize=8, nvalign=4]
    29. *** Dumping IRgen Record Layout
    30. Record: CXXRecordDecl 0x55df47367d20 <main.cpp:1:1, line:6:1> line:1:7 referenced class B definition
    31. |-DefinitionData pass_in_registers standard_layout trivially_copyable has_user_declared_ctor can_const_default_init
    32. | |-DefaultConstructor exists non_trivial user_provided
    33. | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
    34. | |-MoveConstructor exists simple trivial
    35. | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
    36. | |-MoveAssignment exists simple trivial needs_implicit
    37. | `-Destructor simple irrelevant trivial
    38. |-CXXRecordDecl 0x55df47367e38 <col:1, col:7> col:7 implicit referenced class B
    39. |-AccessSpecDecl 0x55df47367ec8 <line:2:2, col:9> col:2 private
    40. |-FieldDecl 0x55df47367f08 <line:3:3, col:7> col:7 b 'int'
    41. |-AccessSpecDecl 0x55df47367f58 <line:4:2, col:8> col:2 public
    42. |-CXXConstructorDecl 0x55df47368008 <line:5:3, col:8> col:3 used B 'void ()'
    43. | `-CompoundStmt 0x55df473680c8 <col:7, col:8>
    44. |-CXXConstructorDecl 0x55df47368628 <line:1:7> col:7 implicit constexpr B 'void (const B &)' inline default trivial noexcept-unevaluated 0x55df47368628
    45. | `-ParmVarDecl 0x55df47368738 <col:7> col:7 'const B &'
    46. |-CXXConstructorDecl 0x55df473687d8 <col:7> col:7 implicit constexpr B 'void (B &&)' inline default trivial noexcept-unevaluated 0x55df473687d8
    47. | `-ParmVarDecl 0x55df473688e8 <col:7> col:7 'B &&'
    48. `-CXXDestructorDecl 0x55df473978a8 <col:7> col:7 implicit ~B 'void ()' inline default trivial noexcept-unevaluated 0x55df473978a8
    49. Layout: <CGRecordLayout
    50. LLVMType:%class.B = type { i32 }
    51. NonVirtualBaseLLVMType:%class.B = type { i32 }
    52. IsZeroInitializable:1
    53. BitFields:[
    54. ]>
    55. *** Dumping IRgen Record Layout
    56. Record: CXXRecordDecl 0x55df473680d8 <main.cpp:8:1, line:14:1> line:8:7 referenced class A definition
    57. |-DefinitionData pass_in_registers standard_layout trivially_copyable has_user_declared_ctor can_const_default_init
    58. | |-DefaultConstructor
    59. | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
    60. | |-MoveConstructor exists simple trivial
    61. | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
    62. | |-MoveAssignment exists simple trivial needs_implicit
    63. | `-Destructor simple irrelevant trivial needs_implicit
    64. |-CXXRecordDecl 0x55df473681f8 <col:1, col:7> col:7 implicit referenced class A
    65. |-AccessSpecDecl 0x55df47368288 <line:9:2, col:9> col:2 private
    66. |-FieldDecl 0x55df473682c0 <line:10:3, col:5> col:5 b 'B'
    67. |-FieldDecl 0x55df47368328 <line:11:3, col:7> col:7 a 'int'
    68. |-AccessSpecDecl 0x55df47368378 <line:12:2, col:8> col:2 public
    69. |-CXXConstructorDecl 0x55df473684b8 <line:13:3, col:22> col:3 used A 'void (int)'
    70. | |-ParmVarDecl 0x55df473683b8 <col:5, col:9> col:9 used _a 'int'
    71. | |-CXXCtorInitializer Field 0x55df473682c0 'b' 'B'
    72. | | `-CXXConstructExpr 0x55df47368958 <col:3> 'B' 'void ()'
    73. | |-CXXCtorInitializer Field 0x55df47368328 'a' 'int'
    74. | | `-ImplicitCastExpr 0x55df473685c0 <col:17> 'int' <LValueToRValue>
    75. | | `-DeclRefExpr 0x55df47368580 <col:17> 'int' lvalue ParmVar 0x55df473683b8 '_a' 'int'
    76. | `-CompoundStmt 0x55df473689b8 <col:21, col:22>
    77. |-CXXConstructorDecl 0x55df47397720 <line:8:7> col:7 implicit constexpr A 'void (const A &)' inline default trivial noexcept-unevaluated 0x55df47397720
    78. | `-ParmVarDecl 0x55df47397838 <col:7> col:7 'const A &'
    79. `-CXXConstructorDecl 0x55df473979b8 <col:7> col:7 implicit constexpr A 'void (A &&)' inline default trivial noexcept-unevaluated 0x55df473979b8
    80. `-ParmVarDecl 0x55df47397ac8 <col:7> col:7 'A &&'
    81. Layout: <CGRecordLayout
    82. LLVMType:%class.A = type { %class.B, i32 }
    83. NonVirtualBaseLLVMType:%class.A = type { %class.B, i32 }
    84. IsZeroInitializable:1
    85. BitFields:[
    86. ]>
    87. Record: CXXRecordDecl 0x55df473680d8 <main.cpp:8:1, line:14:1> line:8:7 referenced class A definition
    88. |-DefinitionData pass_in_registers standard_layout trivially_copyable has_user_declared_ctor can_const_default_init
    89. | |-DefaultConstructor

    这几行证明了,编译器没有为 class A 生成 default constructor

    1. |-CXXConstructorDecl 0x55df473684b8 <line:13:3, col:22> col:3 used A 'void (int)'
    2. | |-ParmVarDecl 0x55df473683b8 <col:5, col:9> col:9 used _a 'int'
    3. | |-CXXCtorInitializer Field 0x55df473682c0 'b' 'B'
    4. | | `-CXXConstructExpr 0x55df47368958 <col:3> 'B' 'void ()'

    这几行证明了编译器扩充了 A 的单参数 constructor,在里面安插了对 B 的 default consturctor 的调用

  2. 带有 default constructor 的 Base class

    如果一个没有任何user_provided constructors的class派生自一个带有default constructor的base class,那么这个 dervied class 的 default constructor 会被编译器自动合成出来且是 non-trivial 的

    一个需要注意的地方是,上述成立的一个充分不必要条件是:base class 的 default constructor 只有是 non-tirvial 时,dervied class 被合成的这个 default constructor 才是 non-trivial 的;具体原因可以回顾一下之前所说的平凡默认构造函数的所需条件

    同理,如果 derived class 提供了 defualt constructor 或者其他 constructors 的话,其结果也与第 1 点中所述情况类似,读者可以自行实验

  3. 带有一个 virtual function 的 class

    如果一个没有任何user_provided constructors的class中声明(或继承)了一个virtual function,那么编译器会自动为该 class 生成一个 default constructor,且该 defalut constructor 是 non-trivial 的

    举例证明:

    1. // 定义了一个virtual function
    2. class A {
    3. public:
    4. virtual void vfunc() {}
    5. };
    6. // 继承了一个virtual function
    7. class B: public A {
    8. };

    打印相关信息:

    1. *** Dumping AST Record Layout
    2. 0 | class A
    3. 0 | (A vtable pointer)
    4. | [sizeof=8, dsize=8, align=8,
    5. | nvsize=8, nvalign=8]
    6. *** Dumping AST Record Layout
    7. 0 | class B
    8. 0 | class A (primary base)
    9. 0 | (A vtable pointer)
    10. | [sizeof=8, dsize=8, align=8,
    11. | nvsize=8, nvalign=8]
    12. *** Dumping IRgen Record Layout
    13. Record: CXXRecordDecl 0x55d1c6dc4c50 <main.cpp:1:1, line:4:1> line:1:7 referenced class A definition
    14. |-DefinitionData polymorphic literal has_constexpr_non_copy_move_ctor can_const_default_init
    15. | |-DefaultConstructor exists non_trivial constexpr defaulted_is_constexpr
    16. | |-CopyConstructor simple non_trivial has_const_param implicit_has_const_param
    17. | |-MoveConstructor exists simple non_trivial
    18. | |-CopyAssignment non_trivial has_const_param implicit_has_const_param
    19. | |-MoveAssignment exists simple non_trivial
    20. | `-Destructor simple irrelevant trivial
    21. |-CXXRecordDecl 0x55d1c6dc4d68 <col:1, col:7> col:7 implicit class A
    22. |-AccessSpecDecl 0x55d1c6dc4df8 <line:2:2, col:8> col:2 public
    23. |-CXXMethodDecl 0x55d1c6dc4e98 <line:3:3, col:25> col:16 vfunc 'void ()' virtual
    24. | `-CompoundStmt 0x55d1c6dc53c0 <col:24, col:25>
    25. |-CXXMethodDecl 0x55d1c6dc4f98 <line:1:7> col:7 implicit constexpr operator= 'A &(const A &)' inline default noexcept-unevaluated 0x55d1c6dc4f98
    26. | `-ParmVarDecl 0x55d1c6dc50a8 <col:7> col:7 'const A &'
    27. |-CXXMethodDecl 0x55d1c6dc5148 <col:7> col:7 implicit constexpr operator= 'A &(A &&)' inline default noexcept-unevaluated 0x55d1c6dc5148
    28. | `-ParmVarDecl 0x55d1c6dc5258 <col:7> col:7 'A &&'
    29. |-CXXDestructorDecl 0x55d1c6dc52e0 <col:7> col:7 implicit ~A 'void ()' inline default trivial noexcept-unevaluated 0x55d1c6dc52e0
    30. |-CXXConstructorDecl 0x55d1c6df4028 <col:7> col:7 implicit used constexpr A 'void () noexcept' inline default
    31. | `-CompoundStmt 0x55d1c6df4738 <col:7>
    32. |-CXXConstructorDecl 0x55d1c6df4100 <col:7> col:7 implicit constexpr A 'void (const A &)' inline default noexcept-unevaluated 0x55d1c6df4100
    33. | `-ParmVarDecl 0x55d1c6df4218 <col:7> col:7 'const A &'
    34. `-CXXConstructorDecl 0x55d1c6df4288 <col:7> col:7 implicit constexpr A 'void (A &&)' inline default noexcept-unevaluated 0x55d1c6df4288
    35. `-ParmVarDecl 0x55d1c6df4398 <col:7> col:7 'A &&'
    36. Layout: <CGRecordLayout
    37. LLVMType:%class.A = type { i32 (...)** }
    38. NonVirtualBaseLLVMType:%class.A = type { i32 (...)** }
    39. IsZeroInitializable:1
    40. BitFields:[
    41. ]>
    42. *** Dumping IRgen Record Layout
    43. Record: CXXRecordDecl 0x55d1c6dc53d0 <main.cpp:7:1, line:8:1> line:7:7 referenced class B definition
    44. |-DefinitionData polymorphic literal has_constexpr_non_copy_move_ctor can_const_default_init
    45. | |-DefaultConstructor exists non_trivial constexpr defaulted_is_constexpr
    46. | |-CopyConstructor simple non_trivial has_const_param implicit_has_const_param
    47. | |-MoveConstructor exists simple non_trivial
    48. | |-CopyAssignment non_trivial has_const_param implicit_has_const_param
    49. | |-MoveAssignment exists simple non_trivial
    50. | `-Destructor simple irrelevant trivial
    51. |-public 'A'
    52. |-CXXRecordDecl 0x55d1c6dc5528 <col:1, col:7> col:7 implicit class B
    53. |-CXXMethodDecl 0x55d1c6dc5618 <col:7> col:7 implicit constexpr operator= 'B &(const B &)' inline default noexcept-unevaluated 0x55d1c6dc5618
    54. | `-ParmVarDecl 0x55d1c6dc5728 <col:7> col:7 'const B &'
    55. |-CXXMethodDecl 0x55d1c6dc57c8 <col:7> col:7 implicit constexpr operator= 'B &(B &&)' inline default noexcept-unevaluated 0x55d1c6dc57c8
    56. | `-ParmVarDecl 0x55d1c6dc58d8 <col:7> col:7 'B &&'
    57. |-CXXDestructorDecl 0x55d1c6dc5960 <col:7> col:7 implicit ~B 'void ()' inline default trivial noexcept-unevaluated 0x55d1c6dc5960
    58. |-CXXConstructorDecl 0x55d1c6df3f30 <col:7> col:7 implicit used constexpr B 'void () noexcept' inline default
    59. | |-CXXCtorInitializer 'A'
    60. | | `-CXXConstructExpr 0x55d1c6df4748 <col:7> 'A' 'void () noexcept'
    61. | `-CompoundStmt 0x55d1c6df47b0 <col:7>
    62. |-CXXConstructorDecl 0x55d1c6df4408 <col:7> col:7 implicit constexpr B 'void (const B &)' inline default noexcept-unevaluated 0x55d1c6df4408
    63. | `-ParmVarDecl 0x55d1c6df4518 <col:7> col:7 'const B &'
    64. `-CXXConstructorDecl 0x55d1c6df4588 <col:7> col:7 implicit constexpr B 'void (B &&)' inline default noexcept-unevaluated 0x55d1c6df4588
    65. `-ParmVarDecl 0x55d1c6df4698 <col:7> col:7 'B &&'
    66. Layout: <CGRecordLayout
    67. LLVMType:%class.B = type { %class.A }
    68. NonVirtualBaseLLVMType:%class.B = type { %class.A }
    69. IsZeroInitializable:1
    70. BitFields:[
    71. ]>
    72. Record: CXXRecordDecl 0x55d1c6dc4c50 <main.cpp:1:1, line:4:1> line:1:7 referenced class A definition
    73. |-DefinitionData polymorphic literal has_constexpr_non_copy_move_ctor can_const_default_init
    74. | |-DefaultConstructor exists non_trivial constexpr defaulted_is_constexpr
    75. Record: CXXRecordDecl 0x55d1c6dc53d0 <main.cpp:7:1, line:8:1> line:7:7 referenced class B definition
    76. |-DefinitionData polymorphic literal has_constexpr_non_copy_move_ctor can_const_default_init
    77. | |-DefaultConstructor exists non_trivial constexpr defaulted_is_constexpr

    这几行证明了之前的所述的观点

    与之前的情况不同的时,这种情况下所生成的 default constructor,不仅需要调用必须的基类的 default constructor,还需要完成设定 vptr 的动作,才能满足虚机制的运行

    与之前相同的是,如果提供了任何用户声明的 constructors,那么自然不会生成 default constructor,编译器会对现有的 constrcutors 进行扩充,来完成必要的工作

    还是举个例子吧:

    1. class A {
    2. public:
    3. virtual void vfunc() {}
    4. A(int a) : _a(a) {}
    5. private:
    6. int _a;
    7. };
    8. class B : public A {
    9. public:
    10. B(int a, int b) : A(a), _b(b) {}
    11. private:
    12. int _b;
    13. };

    打印相关信息:

    1. *** Dumping AST Record Layout
    2. 0 | class A
    3. 0 | (A vtable pointer)
    4. 8 | int _a
    5. | [sizeof=16, dsize=12, align=8,
    6. | nvsize=12, nvalign=8]
    7. *** Dumping AST Record Layout
    8. 0 | class B
    9. 0 | class A (primary base)
    10. 0 | (A vtable pointer)
    11. 8 | int _a
    12. 12 | int _b
    13. | [sizeof=16, dsize=16, align=8,
    14. | nvsize=16, nvalign=8]
    15. *** Dumping IRgen Record Layout
    16. Record: CXXRecordDecl 0x56506d274d60 <main.cpp:1:1, line:7:1> line:1:7 referenced class A definition
    17. |-DefinitionData polymorphic has_user_declared_ctor can_const_default_init
    18. | |-DefaultConstructor
    19. | |-CopyConstructor simple non_trivial has_const_param implicit_has_const_param
    20. | |-MoveConstructor exists simple non_trivial
    21. | |-CopyAssignment non_trivial has_const_param implicit_has_const_param
    22. | |-MoveAssignment exists simple non_trivial
    23. | `-Destructor simple irrelevant trivial
    24. |-CXXRecordDecl 0x56506d274e78 <col:1, col:7> col:7 implicit referenced class A
    25. |-AccessSpecDecl 0x56506d274f08 <line:2:2, col:8> col:2 public
    26. |-CXXMethodDecl 0x56506d274fa8 <line:3:3, col:25> col:16 vfunc 'void ()' virtual
    27. | `-CompoundStmt 0x56506d275700 <col:24, col:25>
    28. |-CXXConstructorDecl 0x56506d275138 <line:4:3, col:21> col:3 used A 'void (int)'
    29. | |-ParmVarDecl 0x56506d275060 <col:5, col:9> col:9 used a 'int'
    30. | |-CXXCtorInitializer Field 0x56506d275228 '_a' 'int'
    31. | | `-ImplicitCastExpr 0x56506d275750 <col:17> 'int' <LValueToRValue>
    32. | | `-DeclRefExpr 0x56506d275710 <col:17> 'int' lvalue ParmVar 0x56506d275060 'a' 'int'
    33. | `-CompoundStmt 0x56506d275798 <col:20, col:21>
    34. |-AccessSpecDecl 0x56506d2751e8 <line:5:2, col:9> col:2 private
    35. |-FieldDecl 0x56506d275228 <line:6:3, col:7> col:7 _a 'int'
    36. |-CXXMethodDecl 0x56506d2752d8 <line:1:7> col:7 implicit operator= 'A &(const A &)' inline default noexcept-unevaluated 0x56506d2752d8
    37. | `-ParmVarDecl 0x56506d2753e8 <col:7> col:7 'const A &'
    38. |-CXXMethodDecl 0x56506d275488 <col:7> col:7 implicit operator= 'A &(A &&)' inline default noexcept-unevaluated 0x56506d275488
    39. | `-ParmVarDecl 0x56506d275598 <col:7> col:7 'A &&'
    40. |-CXXDestructorDecl 0x56506d275620 <col:7> col:7 implicit ~A 'void ()' inline default trivial noexcept-unevaluated 0x56506d275620
    41. |-CXXConstructorDecl 0x56506d2a4af0 <col:7> col:7 implicit constexpr A 'void (const A &)' inline default noexcept-unevaluated 0x56506d2a4af0
    42. | `-ParmVarDecl 0x56506d2a4c08 <col:7> col:7 'const A &'
    43. `-CXXConstructorDecl 0x56506d2a4c78 <col:7> col:7 implicit constexpr A 'void (A &&)' inline default noexcept-unevaluated 0x56506d2a4c78
    44. `-ParmVarDecl 0x56506d2a4d88 <col:7> col:7 'A &&'
    45. Layout: <CGRecordLayout
    46. LLVMType:%class.A = type <{ i32 (...)**, i32, [4 x i8] }>
    47. NonVirtualBaseLLVMType:%class.A.base = type <{ i32 (...)**, i32 }>
    48. IsZeroInitializable:1
    49. BitFields:[
    50. ]>
    51. *** Dumping IRgen Record Layout
    52. Record: CXXRecordDecl 0x56506d2757a8 <main.cpp:9:1, line:14:1> line:9:7 referenced class B definition
    53. |-DefinitionData polymorphic has_user_declared_ctor can_const_default_init
    54. | |-DefaultConstructor
    55. | |-CopyConstructor simple non_trivial has_const_param implicit_has_const_param
    56. | |-MoveConstructor exists simple non_trivial
    57. | |-CopyAssignment non_trivial has_const_param implicit_has_const_param
    58. | |-MoveAssignment exists simple non_trivial
    59. | `-Destructor simple irrelevant trivial
    60. |-public 'A'
    61. |-CXXRecordDecl 0x56506d275908 <col:1, col:7> col:7 implicit referenced class B
    62. |-AccessSpecDecl 0x56506d275998 <line:10:2, col:8> col:2 public
    63. |-CXXConstructorDecl 0x56506d275b68 <line:11:3, col:34> col:3 used B 'void (int, int)'
    64. | |-ParmVarDecl 0x56506d2759d8 <col:5, col:9> col:9 used a 'int'
    65. | |-ParmVarDecl 0x56506d275a58 <col:12, col:16> col:16 used b 'int'
    66. | |-CXXCtorInitializer 'A'
    67. | | `-CXXConstructExpr 0x56506d2a4e10 <col:21, col:24> 'A' 'void (int)'
    68. | | `-ImplicitCastExpr 0x56506d2a4df8 <col:23> 'int' <LValueToRValue>
    69. | | `-DeclRefExpr 0x56506d2a4aa0 <col:23> 'int' lvalue ParmVar 0x56506d2759d8 'a' 'int'
    70. | |-CXXCtorInitializer Field 0x56506d2a45d0 '_b' 'int'
    71. | | `-ImplicitCastExpr 0x56506d2a4ea8 <col:30> 'int' <LValueToRValue>
    72. | | `-DeclRefExpr 0x56506d2a4e68 <col:30> 'int' lvalue ParmVar 0x56506d275a58 'b' 'int'
    73. | `-CompoundStmt 0x56506d2a4ef8 <col:33, col:34>
    74. |-AccessSpecDecl 0x56506d275c20 <line:12:2, col:9> col:2 private
    75. |-FieldDecl 0x56506d2a45d0 <line:13:3, col:7> col:7 _b 'int'
    76. |-CXXMethodDecl 0x56506d2a4678 <line:9:7> col:7 implicit operator= 'B &(const B &)' inline default noexcept-unevaluated 0x56506d2a4678
    77. | `-ParmVarDecl 0x56506d2a4788 <col:7> col:7 'const B &'
    78. |-CXXMethodDecl 0x56506d2a4828 <col:7> col:7 implicit operator= 'B &(B &&)' inline default noexcept-unevaluated 0x56506d2a4828
    79. | `-ParmVarDecl 0x56506d2a4938 <col:7> col:7 'B &&'
    80. |-CXXDestructorDecl 0x56506d2a49c0 <col:7> col:7 implicit ~B 'void ()' inline default trivial noexcept-unevaluated 0x56506d2a49c0
    81. |-CXXConstructorDecl 0x56506d2a5150 <col:7> col:7 implicit constexpr B 'void (const B &)' inline default noexcept-unevaluated 0x56506d2a5150
    82. | `-ParmVarDecl 0x56506d2a5268 <col:7> col:7 'const B &'
    83. `-CXXConstructorDecl 0x56506d2a52d8 <col:7> col:7 implicit constexpr B 'void (B &&)' inline default noexcept-unevaluated 0x56506d2a52d8
    84. `-ParmVarDecl 0x56506d2a53e8 <col:7> col:7 'B &&'
    85. Layout: <CGRecordLayout
    86. LLVMType:%class.B = type { %class.A.base, i32 }
    87. NonVirtualBaseLLVMType:%class.B = type { %class.A.base, i32 }
    88. IsZeroInitializable:1
    89. BitFields:[
    90. ]>

    读者想必应该能够自己寻找答案了

  4. 带有一个 virtual base class 的 class

    为了实现 virtual base class 的语义和功能,在 derived class 中必然需要进行一些额外的工作,这些工作是编译器来完成的

    如果存在任何 user_provided constructors,那么这些工作会被安插进入这些 user_provided constructors 中,如果没有 user_provided constructors,那么编译器会合成 non-trivial default constructor 来完成必要的工作(比如调用基类的 default constructor、设定虚机制所必须的相关信息之类)

    最后再举个例子吧:

    1. class A {
    2. private:
    3. int _a;
    4. };
    5. class B: public virtual A {
    6. private:
    7. int _b;
    8. };

    打印相关信息:

    1. *** Dumping AST Record Layout
    2. 0 | class A
    3. 0 | int _a
    4. | [sizeof=4, dsize=4, align=4,
    5. | nvsize=4, nvalign=4]
    6. *** Dumping AST Record Layout
    7. 0 | class B
    8. 0 | (B vtable pointer)
    9. 8 | int _b
    10. 12 | class A (virtual base)
    11. 12 | int _a
    12. | [sizeof=16, dsize=16, align=8,
    13. | nvsize=12, nvalign=8]
    14. *** Dumping IRgen Record Layout
    15. Record: CXXRecordDecl 0x557ad6558c50 <main.cpp:1:1, line:4:1> line:1:7 referenced class A definition
    16. |-DefinitionData pass_in_registers standard_layout trivially_copyable trivial literal
    17. | |-DefaultConstructor exists trivial
    18. | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
    19. | |-MoveConstructor exists simple trivial
    20. | |-CopyAssignment trivial has_const_param implicit_has_const_param
    21. | |-MoveAssignment exists simple trivial
    22. | `-Destructor simple irrelevant trivial
    23. |-CXXRecordDecl 0x557ad6558d68 <col:1, col:7> col:7 implicit class A
    24. |-AccessSpecDecl 0x557ad6558df8 <line:2:2, col:9> col:2 private
    25. |-FieldDecl 0x557ad6558e38 <line:3:3, col:7> col:7 _a 'int'
    26. |-CXXMethodDecl 0x557ad6559368 <line:1:7> col:7 implicit constexpr operator= 'A &(const A &)' inline default trivial noexcept-unevaluated 0x557ad6559368
    27. | `-ParmVarDecl 0x557ad6559478 <col:7> col:7 'const A &'
    28. |-CXXMethodDecl 0x557ad6559518 <col:7> col:7 implicit constexpr operator= 'A &(A &&)' inline default trivial noexcept-unevaluated 0x557ad6559518
    29. | `-ParmVarDecl 0x557ad6559628 <col:7> col:7 'A &&'
    30. |-CXXDestructorDecl 0x557ad6559970 <col:7> col:7 implicit ~A 'void ()' inline default trivial noexcept-unevaluated 0x557ad6559970
    31. |-CXXConstructorDecl 0x557ad6586a08 <col:7> col:7 implicit used A 'void () noexcept' inline default trivial
    32. | `-CompoundStmt 0x557ad6587118 <col:7>
    33. |-CXXConstructorDecl 0x557ad6586c68 <col:7> col:7 implicit constexpr A 'void (const A &)' inline default trivial noexcept-unevaluated 0x557ad6586c68
    34. | `-ParmVarDecl 0x557ad6586d78 <col:7> col:7 'const A &'
    35. `-CXXConstructorDecl 0x557ad6586de8 <col:7> col:7 implicit constexpr A 'void (A &&)' inline default trivial noexcept-unevaluated 0x557ad6586de8
    36. `-ParmVarDecl 0x557ad6586ef8 <col:7> col:7 'A &&'
    37. Layout: <CGRecordLayout
    38. LLVMType:%class.A = type { i32 }
    39. NonVirtualBaseLLVMType:%class.A = type { i32 }
    40. IsZeroInitializable:1
    41. BitFields:[
    42. ]>
    43. *** Dumping IRgen Record Layout
    44. Record: CXXRecordDecl 0x557ad6558ea0 <main.cpp:6:1, line:9:1> line:6:7 referenced class B definition
    45. |-DefinitionData
    46. | |-DefaultConstructor exists non_trivial
    47. | |-CopyConstructor simple non_trivial has_const_param implicit_has_const_param
    48. | |-MoveConstructor exists simple non_trivial
    49. | |-CopyAssignment non_trivial has_const_param implicit_has_const_param
    50. | |-MoveAssignment exists simple non_trivial
    51. | `-Destructor simple irrelevant trivial
    52. |-virtual public 'A'
    53. |-CXXRecordDecl 0x557ad6559010 <col:1, col:7> col:7 implicit class B
    54. |-AccessSpecDecl 0x557ad65590a0 <line:7:2, col:9> col:2 private
    55. |-FieldDecl 0x557ad65590e0 <line:8:3, col:7> col:7 _b 'int'
    56. |-CXXMethodDecl 0x557ad6559188 <line:6:7> col:7 implicit operator= 'B &(const B &)' inline default noexcept-unevaluated 0x557ad6559188
    57. | `-ParmVarDecl 0x557ad6559298 <col:7> col:7 'const B &'
    58. |-CXXMethodDecl 0x557ad65596c8 <col:7> col:7 implicit operator= 'B &(B &&)' inline default noexcept-unevaluated 0x557ad65596c8
    59. | `-ParmVarDecl 0x557ad65597d8 <col:7> col:7 'B &&'
    60. |-CXXDestructorDecl 0x557ad6559860 <col:7> col:7 implicit ~B 'void ()' inline default trivial noexcept-unevaluated 0x557ad6559860
    61. |-CXXConstructorDecl 0x557ad6586910 <col:7> col:7 implicit used B 'void () noexcept' inline default
    62. | |-CXXCtorInitializer 'A'
    63. | | `-CXXConstructExpr 0x557ad6587128 <col:7> 'A' 'void () noexcept'
    64. | `-CompoundStmt 0x557ad6587190 <col:7>
    65. |-CXXConstructorDecl 0x557ad6586ae0 <col:7> col:7 implicit B 'void (const B &)' inline default noexcept-unevaluated 0x557ad6586ae0
    66. | `-ParmVarDecl 0x557ad6586bf8 <col:7> col:7 'const B &'
    67. `-CXXConstructorDecl 0x557ad6586f68 <col:7> col:7 implicit B 'void (B &&)' inline default noexcept-unevaluated 0x557ad6586f68
    68. `-ParmVarDecl 0x557ad6587078 <col:7> col:7 'B &&'
    69. Layout: <CGRecordLayout
    70. LLVMType:%class.B = type <{ i32 (...)**, i32, %class.A }>
    71. NonVirtualBaseLLVMType:%class.B.base = type <{ i32 (...)**, i32 }>
    72. IsZeroInitializable:1
    73. BitFields:[
    74. ]>
    75. Record: CXXRecordDecl 0x557ad6558ea0 <main.cpp:6:1, line:9:1> line:6:7 referenced class B definition
    76. |-DefinitionData
    77. | |-DefaultConstructor exists non_trivial

    可以看到 class B 存在 default constructor 且是 non-trivial 的

总结

  1. 如果没有任何 user_provided constructor,那么编译器会始终生成一个 default constructor(反之,如果存在任何 user_provided constrcutor,那么除非强制生成,否则编译器是不会生成 defualt construcotor 的)
  2. 编译器生成的 default constructor 分 trivial 和 non-trivial 两种

C++新手一般有两个误区:

  • 任何 class 如果没有定义 default constructor,编译器就会自动合成一个
  • 编译器合成出来的 default constructor 会为成员变量设定默认值

PS:default constructor 的大概知识就到此结束啦,本文也只是走马观花的介绍了 default constructor 的相关知识,更加详细的还是请移步至 cppreference 查阅相关文档

构造函数语义学——Default Constructor篇的更多相关文章

  1. 构造函数语义学——Copy Constructor 篇

    构造函数语义学--Copy Constructor 篇 本文主要介绍<深度探索 C++对象模型>之<构造函数语义学>中的 Copy Constructor 构造函数的调用时机 ...

  2. 构造函数语义学——Copy Constructor 的构造操作

    前言 在三种情况下,会以一个 object 的内容作为另一个 class object 的初值: object明确初始化 class X{...}; X x; X xx = x; object 被当作 ...

  3. 深度探索C++对象模型之第二章:构造函数语意学之Default constructor的构造操作

    C++新手一般由两个常见的误解: 如果任何class没有定义默认构造函数(default constructor),编译器就会合成一个来. 编译器合成的的default constructor会显示的 ...

  4. 构造函数语义学之Default Constructor构建操作

    一.Default Constructor的构建操作 首先大家要走出两个误区: 1).任何class如果没有定义default constructor,就会被合成一个来. 2).便以其合成出来的def ...

  5. 构造函数语义学之Copy Constructor构建操作(2)

    二.详述条件 3 和 4 那么好,我又要问大家了,条件1 和 2比较容易理解.因为member object或 base class 含有copy constructor.那么member objec ...

  6. C++对象模型(一):The Semantics of Constructors The Default Constructor (默认构造函数什么时候会被创建出来)

    本文是 Inside The C++ Object Model, Chapter 2的部分读书笔记. C++ Annotated Reference Manual中明确告诉我们: default co ...

  7. The Semantics of Constructors: The Default Constructor (默认构造函数什么时候会被创建出来)

    本文是 Inside The C++ Object Model, Chapter 2的部分读书笔记. C++ Annotated Reference Manual中明确告诉我们: default co ...

  8. 实体类No default constructor found 找不到默认构造函数;

    root cause org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [c ...

  9. 构造函数语义学之Copy Constructor构建操作(1)

    一.Copy Constructor的构建操作 就像 default constructor 一样,如果class没有申明一个 copy constructor,就会隐含的声明或隐含的定义一个.生成的 ...

随机推荐

  1. 选择正确的 Fragment#commitXXX() 函数

    转自: http://www.tuicool.com/articles/q6R7nii 最新版本(v24.0.0)的 Support v4 库中的 FragmentTransaction 添加了 co ...

  2. Hbase与传统关系型数据库对比

    在说HBase之前,我想再唠叨几句.做互联网应用的哥们儿应该都清楚,互联网应用这东西,你没办法预测你的系统什么时候会被多少人访问,你面临的用户到底有多少,说不定今天你的用户还少,明天系统用户就变多了, ...

  3. CSS——段落处理

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. SpringBoot的一个小彩蛋

    彩蛋这种东西还算比较常见,在电影或者游戏里面我们也遇见过不少.今天就简单介绍一下SpringBoot里面的一个小彩蛋. 玩过SpringBoot的同志都知道,SpringBoot的启动界面是这酱紫的: ...

  5. 零基础快速入门Java的秘诀

    刚开始学习Java时要端正自己的学习的目标和态度,明确学习Java基础重点要做什么. 首先第一点,Java基础的学习,基础很重要,基础好地基牢,才能爬的高! 大家要从以下4个方向努力: 代码能力:一定 ...

  6. Docker Gitlab CI 部署 Spring Boot 项目

    目前在学习这一块的内容,但是可能每个人环境都不同,导致找不到一篇博客能够完全操作下来没有错误的,所以自己也写一下,记录一下整个搭建的过程. Docker 的安装这里就不赘述了,基本上几行命令都可以了, ...

  7. [C++] 重载运算符与类型转换(1)

      1.形式:返回值 operator符号(参数列表){}   2.不能被重载的运算符::: 作用域运算符  .*   . 成员访问运算符   ?: 条件运算符:某些运算符(逗号,,取地址&, ...

  8. RabbitMQ的六种工作模式总结

    最近学习RabbitMQ的使用方式,记录下来,方便以后使用,也方便和大家共享,相互交流. RabbitMQ的六种工作模式: 1.Work queues2.Publish/subscribe3.Rout ...

  9. jenkins自动化部署项目5 -- 系统管理-系统设置ssh配置

    [系统管理]-[系统设置] 如果应用服务(前端后台)要部署在linux服务器上,我选择的是用ssh 为了jenkins登录远程登录linux服务器可以免密登录,先配置公钥和私钥: 我是在windows ...

  10. 一次五分钟 angularJS (1)—— Binding

    引用angularjs 需要使用AngularJS,需要引用AngularJS的文件 ng-app 要将angular用到页面绑定的时候,我们需要指明它的作用域. 在上图中,ng-app=" ...