受够了反复写构造函数?这个特性让你告别重复劳动

开发
C++11 为我们带来了救星 —— 神奇的 =default 关键字!它就像是一位贴心的管家 ,帮我们处理那些繁琐的家务事。

还在为写类时重复编写那些无聊的构造函数而烦恼吗?是不是觉得手写默认构造函数就像被迫吃黑暗料理一样痛苦?别担心!C++11 为我们带来了救星 —— 神奇的 =default 关键字!它就像是一位贴心的管家 🫅,帮我们处理那些繁琐的家务事。今天就让我们一起来探索这位现代 C++ 中的清洁能手吧!相信我,看完这篇文章,你会爱上这个默默无闻的小帮手的!

从前的烦恼... 

让我们看看传统 C++ 中定义一个简单类时的痛点:

class Student {
    std::string name;    // 👤 学生姓名
    int age;            // 📅 学生年龄
public:
    // 😅 不得不写的默认构造函数
    Student() {
        age = 0;        // ⚠️ 手动初始化,容易遗漏
        // 💭 name 会自动调用 string 的默认构造,但代码看起来不够完整
    }
    
    // 📝 冗长的拷贝构造函数
    Student(const Student& other) {
        name = other.name;  // 📋 简单的赋值操作
        age = other.age;    // 🔄 完全可以由编译器自动完成
    }
    
    // 🗑️ 空空如也的析构函数
    ~Student() {
        // 🤷 什么都不需要做,却还是写了出来...
    }
};class Student {
    std::string name;    // 👤 学生姓名
    int age;            // 📅 学生年龄
public:
    // 😅 不得不写的默认构造函数
    Student() {
        age = 0;        // ⚠️ 手动初始化,容易遗漏
        // 💭 name 会自动调用 string 的默认构造,但代码看起来不够完整
    }
    
    // 📝 冗长的拷贝构造函数
    Student(const Student& other) {
        name = other.name;  // 📋 简单的赋值操作
        age = other.age;    // 🔄 完全可以由编译器自动完成
    }
    
    // 🗑️ 空空如也的析构函数
    ~Student() {
        // 🤷 什么都不需要做,却还是写了出来...
    }
};

为什么这样写不好?

  • 代码冗长:需要写很多模板代码
  • 容易出错:手动实现可能会遗漏成员
  • 性能不佳:编译器自动生成的代码通常更优化
  • 维护困难:增加新成员时需要修改多处代码

现代 C++ 的救星:=default 登场!

💡 让我们看看如何用 =default 让类的定义变得简单优雅!

class Student {
    // 成员变量声明 📦
    std::string name;            // 👤 存储学生姓名
    int age = 0;                 // 📅 存储年龄,直接初始化更现代!

public:
    // 特殊成员函数三剑客 ⚔️
    Student() = default;         // 🎯 默认构造:编译器自动生成最优实现
    Student(const Student&) = default;  // 📝 拷贝构造:自动完成深拷贝
    ~Student() = default;        // 🧹 析构函数:自动清理资源
    
    // 💡 注意:编译器生成的代码通常比手写的更优化!
};

要点总结:

  • 使用 =default 让代码更简洁清晰
  • 自动处理所有成员的初始化/拷贝/清理
  • 获得编译器优化的性能优势
  • 减少手动编码错误的风险

default 的强大功能展示

来看看 =default 如何让我们的代码更优雅、更高效!

// 1️⃣ 极简写法演示
class MagicBox {
    int treasure;    // 💎 存储宝藏值
public:
    // ✨ 一行代码替代繁琐的手动实现
    // 🔄 编译器会自动初始化 treasure
    MagicBox() = default;  // 简洁优雅!
};

// 2️⃣ 性能优化演示
class SuperFast {
    std::string data;    // 📦 存储数据
public:
    // 🚀 编译器优化:自动生成最高效的拷贝实现
    // 🛡️ 自动处理深拷贝,无需手动编写
    SuperFast(const SuperFast&) = default;
};

// 3️⃣ 代码意图清晰演示
class ClearIntent {
    int value;    // 📊 数值存储
public:
    // 📝 显式声明使用默认实现
    // 🎯 让其他开发者一目了然
    ClearIntent() = default;
};

要点总结:

  • 代码更简洁:一行代码替代冗长实现
  • 性能更好:利用编译器优化能力
  • 可读性强:明确表达代码意图
  • 更安全:避免手动实现的潜在错误

default 默认函数的生成规则:编译器如何帮我们省心省力? 

让我们一起揭秘 =default 背后的故事,看看编译器是如何智能地为我们生成代码的!

  • 基本类型成员的处理
class BasicTypes {
    int number;     // 🔵 整型成员
    double value;   // 💰 浮点成员
public:
    BasicTypes() = default;  // 🤖 编译器生成的代码大致等价于:
    /*
    BasicTypes() {
        // ⚠️ 基本类型不会被初始化!保持未定义状态
        // 💡 如需初始化,建议使用类内初始化:int number = 0;
    }
    */
};
  • 类类型成员的处理
class WithClassMembers {
    std::string text;      // 📝 字符串成员
    std::vector<int> nums; // 📊 容器成员
public:
    WithClassMembers() = default;  // 🎯 编译器自动处理:
    /*
    WithClassMembers() {
        // ✨ 类类型成员自动调用它们的默认构造函数
        // 📜 text 初始化为空字符串
        // 🗃️ nums 初始化为空向量
    }
    */
};
  • 拷贝构造的生成规则
class CopyRules {
    int count;          // 🔢 计数器
    std::string name;   // 👤 名称
public:
    CopyRules(const CopyRules& other) = default;  // 📋 自动生成拷贝逻辑:
    /*
    CopyRules(const CopyRules& other) {
        // 🔄 基本类型:按位复制
        count = other.count;
        // 📚 类类型:调用对应的拷贝构造函数
        name = other.name;  // 深拷贝
    }
    */
};
  • 特殊情况和注意事项
class SpecialCases {
    const int fixed;     // 🔒 常量成员
    int& reference;      // 🔗 引用成员
public:
    // ❌ const/引用成员导致默认构造函数无法自动生成
    // SpecialCases() = default;  // 编译失败!
    
    // ✅ 拷贝构造函数仍然可以使用 default
    SpecialCases(const SpecialCases& other) = default;
};

小贴士:

  • 基本类型成员默认不初始化,建议使用类内初始化赋予初值
  • 类类型成员会自动调用它们的默认构造函数,无需担心
  • 拷贝操作会自动处理深浅拷贝,非常智能
  • 对于特殊成员(const/引用),要特别注意构造函数的限制

这样的代码组织既保持了简洁性,又让编译器发挥了它的长处。记住:让编译器做它最擅长的事! 

什么时候应该避免使用 default?

让我们来看看哪些情况下不适合使用 =default,这些知识点对写出健壮的 C++ 代码至关重要!

class NoDefault {
    std::unique_ptr<int> ptr;  // 🔐 需要特殊管理的智能指针
    std::mutex& mtx;           // 🔗 引用类型成员
    constint id;              // 🔒 常量成员
    
public:
    // ⚠️ 以下情况必须手动实现构造函数:
    // 1️⃣ 有引用成员需要初始化
    // 2️⃣ 智能指针需要特殊管理
    // 3️⃣ const 成员需要初始化值
    NoDefault(std::mutex& m) 
        : mtx(m)              // 🔗 初始化引用成员
        , id(generateId())    // 🔢 初始化常量成员
    {
        // 🎯 智能指针的特殊初始化
        ptr = std::make_unique<int>(42);
    }
    
    // ❌ 以下声明都将导致编译错误
    // NoDefault() = default;                // 无法默认构造
    // NoDefault(const NoDefault&) = default; // 引用成员无法默认拷贝
};
  • 含有引用成员时不能用 default
  • 需要特殊资源管理时要手动实现
  • 有 const 成员时需要提供初始化
  • 需要自定义初始化逻辑时应该手写构造函数

记住:编译器很聪明,但不是万能的!在这些特殊情况下,还是需要程序员亲自掌控!

实用小贴士:让代码更优雅!

来看看如何在实际项目中运用 default 让代码更优雅吧!首先,我们从一个简单的游戏角色类开始:

class GameCharacter {
    std::string name;        // 👤 角色名称
    int health = 100;        // ❤️ 生命值(默认100)
    std::vector<std::string> inventory;  // 🎒 物品栏
public:
    // 让编译器帮我们处理所有基础工作 🤖
    GameCharacter() = default;  // ✨ 完美处理所有成员的初始化
};

哇!看看这个清爽的类定义!所有成员都会被完美初始化:

  • name 会自动初始化为空字符串
  • health 使用了类内初始值 100
  • inventory 会自动初始化为空向量

接下来看看如何处理资源管理:

class ResourceManager {
    std::shared_ptr<int> data;     // 🔐 共享资源
    std::vector<float> cache;      // 💾 缓存数据
public:
    // 让默认函数三剑客来保护我们的资源 ⚔️
    ResourceManager() = default;                      // 🎯 完美初始化
    ResourceManager(const ResourceManager&) = default; // 📋 智能处理拷贝
    ~ResourceManager() = default;                     // 🧹 自动清理资源
    
    // 💡 提示:shared_ptr 会被正确拷贝,无需手动管理!
};

看!这就是现代 C++ 的魔力!我们甚至可以处理更复杂的场景 :

class AdvancedPlayer {
    std::string playerName;          // 👤 玩家名
    std::vector<int> scores;         // 🎯 得分记录
    std::map<std::string, int> achievements;  // 🏆 成就系统
public:
    // 一行代码搞定所有特殊成员函数!超级简洁! 🎉
    AdvancedPlayer() = default;                // 🎮 游戏开始
    AdvancedPlayer(const AdvancedPlayer&) = default;  // 🔄 完美复制角色
    AdvancedPlayer& operator=(const AdvancedPlayer&) = default;  // ✍️ 角色数据转移
    ~AdvancedPlayer() = default;               // 👋 优雅告别
    
    // 🌟 所有容器类型都会被完美处理,包括深拷贝!
};

记住这个黄金法则:如果你的类只需要默认行为,就果断用 default 吧!

让我们看最后一个实战案例:

class SmartDevice {
    std::string deviceId;    // 📱 设备ID
    bool isOnline = false;   // 🔌 在线状态
    
    std::vector<std::string> logs;  // 📝 日志记录
public:
    // 智能设备的完美默认行为 🤖
    SmartDevice() = default;  // 开箱即用!
    
    // 💡 小提示:
    // - deviceId 自动初始化为空字符串
    // - isOnline 使用类内初始值 false
    // - logs 自动初始化为空容器
    // 编译器都帮我们处理好啦! 🎉
};

看到了吗?使用 default 不仅让代码更简洁,还能让我们专注于真正重要的业务逻辑!这就是现代 C++ 的优雅之道! 记住:让编译器做它最擅长的事,我们程序员就能专注于创造性的工作啦! 

总结 

有了 =default:

  • 代码更短,更干净
  • 不用写重复的模板代码了
  • 编译器生成的代码性能更好
  • 程序员终于可以专注于真正的业务逻辑了

记住:让编译器做它最擅长的事,我们专注于创造性的工作!这才是现代 C++ 的精髓! 

责任编辑:赵宁宁 来源: everystep
相关推荐

2024-06-11 00:00:01

系统技术代码

2023-04-27 07:57:52

AutoIt自动化工具

2023-04-26 08:55:30

2018-02-23 09:55:12

程序员压迫Python

2019-07-02 09:30:31

程序员劳动陷阱

2024-04-12 08:13:24

2015-08-10 10:26:08

2011-10-25 09:24:08

2025-01-22 14:00:00

C++11委托构造函数代码

2020-05-22 15:16:45

远程工作办公互联网

2021-06-07 15:27:34

机器人人工智能AI

2011-11-08 11:22:35

技术周刊

2023-08-29 06:50:01

Javamaven

2014-03-06 09:23:19

Git服务器Github

2009-02-10 09:34:00

2024-12-24 12:00:00

inlinC++17头文件

2021-07-01 05:17:52

Windows 11操作系统微软

2021-03-08 10:17:58

开发技能代码

2009-10-30 08:49:30

Windows 7锁定IE工具栏

2015-07-09 15:08:08

点赞
收藏

51CTO技术栈公众号