C++中类的默认构造函数详解和示例

下面将从默认构造函数的定义与分类何时编译器会自动生成显式声明(= default= delete与其他特殊成员函数的关系性能与可用性注意事项等方面进行详解,并配以示例代码。


一、默认构造函数是什么

  • 默认构造函数(default constructor)指的是不带任何参数(或所有参数都有默认值)的构造函数,用于创建对象时不传入显式初始值的场景。

  • 形式通常为:

    ClassName();
    

    或者

    ClassName() = default;
    
  • 默认构造函数用于初始化成员:

    • 内置类型(如 intdouble)如果未在类内提供默认初始值,则其值未定义(除非对象是静态或全局);
    • 类类型成员则调用其默认构造函数;
    • 聚合类型除外(C++11 起可使用聚合初始化)。

二、隐式生成规则

编译器会在没有用户声明任何构造函数时,为类自动生成一个隐式默认构造函数,其行为大致为“为每个直接基类和每个非静态数据成员都调用它们的默认构造”。

struct A { int x; };   // 没有构造函数
A a;                   // 调用隐式生成的默认构造:a.x 未初始化(非静态存储区则为垃圾)

但在以下情况下,编译器不会自动生成默认构造函数

  1. 类中声明了其他任何构造函数(包括带参构造);
  2. 类中声明了**= delete 的默认构造函数**;
  3. 某些特殊场景下(如存在不可默认构造的成员)。

三、显式声明 = default= delete

1. = default

  • 在类内显式写出

    ClassName() = default;
    

    可让编译器生成一个受控的constexprnoexcepttrivial 属性由成员决定)默认构造函数。

  • 常用于:

    • 想保留“隐式构造”的行为,但由于定义了其他构造(或析构、拷贝、移动),编译器不再自动生成,需要手动恢复;
    • 明确希望以“可见”的形式声明存在此构造器。
struct B {
    B(int) {}            // 用户定义构造,编译器不自动生成默认构造
    B() = default;       // 重新请求生成默认构造
    int x = 0;
};

2. = delete

  • 写成

    ClassName() = delete;
    

    禁止通过默认构造创建对象,若试图 T obj; 则编译期报错。

  • 用于设计上不允许无参构造,强制用户使用带参构造或工厂函数。

struct C {
    C() = delete;       // 禁用默认构造
    explicit C(int v): x(v) {}
    int x;
};

C c;   // ERROR: 调用被 delete 的构造函数
C c2(5); // OK

四、与其他特殊成员函数的交互

  1. 若用户声明了任何构造函数,编译器不再自动生成默认构造。

  2. = default 可放在类内(隐式内联)或类外(需加上 inline/constexpr 视情况而定)。

  3. 移动构造拷贝构造析构函数 的声明与生成规则相似,定义某些后会抑制其他隐式生成。

    • 例如:若显式声明了析构函数,则可能影响移动构造/赋值的隐式生成。
  4. 聚合类型(聚合)从 C++17 开始可仍进行聚合初始化,即使定义了用户声明的默认构造。


五、何时调用与何时省略

  • 直接初始化(如 T obj;T obj{};)会调用默认构造。

  • 聚合初始化(如 T obj = {};)则对聚合类型直接逐成员初始化,不调用用户定义构造。

  • new T;new T(); 行为不同:

    • new T; 对内置成员不初始化
    • new T(); 对内置成员值初始化(设为 0 或调用默认构造)。

六、性能与可用性注意事项

  1. 非静态内置成员未初始化

    struct S { int a; double b; };
    S s;            // s.a 和 s.b 未定义 → UB
    S s2{};         // 值初始化,s2.a=0, s2.b=0.0
    
    • 建议对 POD/内置成员使用类内默认初始化或在构造中显式赋值,或使用 {} 初始化。
  2. constexpr 默认构造

    • C++11 起可声明 constexpr ClassName() = default;,令对象可在编译期创建。
    • 仅在所有成员默认构造也为 constexpr 时可行。
  3. noexcept 与异常安全

    • 隐式生成的构造函数对此类成员的 noexcept 属性与成员默认构造函数一致。

    • 若希望保证 noexcept,可显式写:

      ClassName() noexcept = default;
      
  4. 规则五/三/零

    • :如果自定义了析构拷贝构造拷贝赋值,考虑自定义其他两项。
    • :加入移动构造移动赋值
    • :若无特殊资源管理需求,尽量都用 = default 或编译器生成,保持“零”自定义。

七、综合示例

#include <iostream>
#include <vector>

struct POD {
    int      a;      // 内置类型
    double   b{3.14}; // 类内默认初始化
    // 隐式生成默认构造:a 未初始化,b=3.14
};

struct Foo {
    Foo() = default;             // 请求生成默认构造
    Foo(int v): x(v) {}          // 带参构造抑制隐式默认构造
    ~Foo() noexcept = default;   // 默认析构,保证 noexcept
    Foo(const Foo&) = default;   // 显式拷贝
    Foo(Foo&&) = default;        // 显式移动
    Foo& operator=(const Foo&) = default;
    Foo& operator=(Foo&&) = default;

    int x = 0;
    std::vector<int> data;       // data 默认构造为空 vector
};

struct Bar {
    Bar() = delete;              // 禁用默认构造
    explicit Bar(int v): y(v) {}
    int y;
};

int main() {
    POD p1;           // p1.a 未定义,p1.b=3.14
    POD p2{};         // 值初始化,p2.a=0, p2.b=3.14

    Foo f1;           // OK:调用 Foo() = default → x=0, data.empty()
    Foo f2(10);       // 调用带参构造 → x=10
    Foo f3 = f2;      // 拷贝构造
    Foo f4 = std::move(f2); // 移动构造

    // Bar b;         // ERROR:Bar() 被 delete
    Bar b2(5);        // OK

    Foo* fp1 = new Foo;    // 隐式默认构造 → x=0
    Foo* fp2 = new Foo();  // 同上,值初始化没有差别

    return 0;
}

八、小结

  1. 自动生成:只有在未声明任何构造函数时,编译器才隐式生成默认构造。
  2. 显式控制:使用 = default 恢复或声明默认构造,用 = delete 禁用。
  3. 初始化语义T obj;T obj{};new T;new T(); 在初始化内置成员时有差异。
  4. 规则五/三/零:结合特殊成员函数设计,保证类型的正确管理与异常安全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

点云SLAM

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

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

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

打赏作者

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

抵扣说明:

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

余额充值