跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 类和对象:默认成员函数详解

C++ 默认成员函数涵盖构造函数、析构函数等六种。重点掌握前四种,包括初始化、资源清理、深浅拷贝及赋值重载。编译器生成的默认行为对内置类型有效,但涉及指针资源时需手动实现深拷贝以防泄漏。理解 const 成员函数与取地址重载有助于完善类的设计。

DotNetGuy发布于 2026/3/15更新于 2026/9/1060 浏览
C++ 类和对象:默认成员函数详解

在 C++ 面向对象编程里,类的默认成员函数是个绕不开的概念。很多时候我们没显式写某些函数,编译器却悄悄生成了它们。这篇文章就聊聊这六个默认成员函数:构造函数、析构函数、拷贝构造函数、赋值运算符重载,还有两个取地址运算符重载。

默认成员函数概览

默认成员函数就是用户没写、编译器自动补全的那些。一个类如果不写任何成员函数,编译器会生成以下六个:

  • 构造函数
  • 析构函数
  • 拷贝构造函数
  • 赋值运算符重载
  • 普通取地址运算符重载
  • const 取地址运算符重载

这六个里,前四个是核心,得自己拿捏;后两个多数场景下编译器生成的版本就够用了。另外,C++11 之后还多了移动构造和移动赋值,不过今天先不展开。

构造函数

构造函数负责对象实例化时的初始化,相当于替代了以前手动调用的 Init 函数,而且系统会自动调用它。

几个关键点:

  • 函数名必须和类名一致。
  • 没有返回值,连 void 都不能写。
  • 对象创建时系统自动触发。
  • 可以重载。
  • 如果没显式定义,编译器会生成无参的默认构造函数;一旦你写了带参的,编译器就不再提供无参版本了。
  • 编译器生成的构造函数对内置类型成员初始化是不确定的(可能是随机值),对自定义类型成员则会调用其默认构造函数。

来看个具体的例子:

#include <iostream>
using namespace std;

class Date {
public:
    // 无参构造函数
    Date() { _year = 1; _month = 1; _day = 1; }
    
    // 带参构造函数
    Date(int year, int month, int day) {
        _year = year; _month = month; _day = day;
    }
    
    // 全缺省构造函数不能与无参构造同时存在
    // Date(int year = 1, int month = 1, int day = 1) { ... }
    
    void Print() {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

{
    Date d1;              
    ;  
    
    
     ;
}
int main()
// 调用无参构造函数
Date d2(2025, 1, 1)
// 调用带参构造函数
// 注意:Date d3(); 是函数声明,不是对象实例化
return
0

大多数情况下我们需要自己实现构造函数。只有少数情况,比如类的成员全是自定义类型且这些类型有合适的默认构造函数时,编译器生成的才够用。

析构函数

别误会,析构函数不是用来销毁对象本身的,那是系统的事。它主要负责清理对象内部占用的资源,类比之前实现的 Destroy 函数。

注意事项:

  • 函数名是在类名前加 ~。
  • 无参数、无返回值。
  • 一个类只能有一个析构函数。
  • 对象生命周期结束时系统自动调用。
  • 编译器生成的析构函数对内置类型不做处理,对自定义类型成员会调用其析构函数。
  • 即使显式定义了析构函数,自定义类型成员的析构函数也会被自动调用。
  • 没有申请资源的类可以不写;有资源申请的类(如动态内存)必须自己写,否则会造成泄漏。
  • 局部域多个对象,后定义的先析构。

看个栈的例子,这里涉及动态内存管理:

#include <iostream>
#include <cstdlib>
using namespace std;

typedef int STDataType;

class Stack {
public:
    Stack(int n = 4) {
        _a = (STDataType*)malloc(sizeof(STDataType) * n);
        if (!_a) {
            perror("malloc 申请空间失败");
            return;
        }
        _capacity = n;
        _top = 0;
    }

    // 析构函数:释放资源
    ~Stack() {
        cout << "~Stack()" << endl;
        free(_a);
        _a = nullptr;
        _top = _capacity = 0;
    }

private:
    STDataType* _a;
    size_t _capacity;
    size_t _top;
};

class MyQueue {
public:
    // 编译器默认生成的析构函数会调用 Stack 的析构函数
    ~MyQueue() {
        cout << "~MyQueue()" << endl;
    }
private:
    Stack pushst;
    Stack popst;
};

int main() {
    Stack st;
    MyQueue mq;
    // 析构顺序:~MyQueue() -> ~Stack()(popst) -> ~Stack()(pushst) -> ~Stack()(st)
    return 0;
}

拷贝构造函数

拷贝构造函数是用一个已存在的对象初始化一个新创建的对象,它是构造函数的重载形式。

核心规则:

  • 第一个参数必须是自身类类型对象的引用,传值会引发无穷递归。
  • 后续参数必须有缺省值。
  • C++ 规定自定义类型对象进行拷贝行为(如传值传参、传值返回)必须调用拷贝构造。
  • 若未显式定义,编译器生成默认拷贝构造函数,对内置类型做浅拷贝,对自定义类型调用其拷贝构造。
  • 成员全是内置类型且无资源的类不需要显式实现;有资源的类需要显式实现深拷贝,否则会导致双重释放。
#include <iostream>
using namespace std;

class Date {
public:
    Date(int year = 1, int month = 1, int day = 1) {
        _year = year; _month = month; _day = day;
    }
    
    // 拷贝构造函数
    Date(const Date& d) {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    
    void Print() {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main() {
    Date d1(2025, 11, 30);
    d1.Print();
    
    // 拷贝构造
    Date d2(d1);
    d2.Print();
    
    Date d3 = d2; // 也是拷贝构造
    d3.Print();
    return 0;
}

对于像 Stack 这样有指针成员的类,必须实现深拷贝:

// 栈的深拷贝构造
Stack(const Stack& st) {
    // 对指针指向的资源重新申请空间并复制数据
    _a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);
    if (!_a) {
        perror("malloc 申请空间失败!!!");
        return;
    }
    memcpy(_a, st._a, sizeof(STDataType) * st._top);
    _top = st._top;
    _capacity = st._capacity;
}

深浅拷贝区别:

  • 浅拷贝:按字节拷贝,指针只拷贝地址,多个对象共享同一份资源,危险。
  • 深拷贝:不仅拷贝指针本身,还对资源重新申请并复制,每个对象独立拥有资源。

赋值运算符重载

赋值运算符用于完成两个已存在对象之间的拷贝赋值。这和拷贝构造不一样,拷贝构造是初始化新对象,赋值是修改已存在对象。

基础规则:

  • 运算符重载本质是特殊名字的函数,形式为 operator 运算符。
  • 若为成员函数,第一个参数是隐式的 this 指针。
  • 不能重载的运算符包括 .*, ::, sizeof, ?:, .。
  • 前置 ++ 和后置 ++ 区分在于后置重载多一个 int 形参。

赋值运算符特点:

  • 必须重载为成员函数。
  • 参数建议为 const 当前类类型引用,避免传值拷贝。
  • 返回值建议为当前类类型引用,支持连续赋值。
  • 有资源的类需要显式实现深拷贝的赋值重载。
// 赋值运算符重载
Date& operator=(const Date& d) {
    // 避免自己给自己赋值
    if (this != &d) {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    return *this; // 支持连续赋值
}

int main() {
    Date d1(2025, 11, 30);
    Date d2(2025, 12, 1);
    d1 = d2;      // 赋值重载,两个已存在对象
    Date d3(d2);  // 拷贝构造,用 d2 初始化新对象 d3
    Date d4 = d2; // 拷贝构造,不是赋值
    return 0;
}

取地址运算符重载

取地址运算符分为普通取地址和 const 取地址两种,一般情况下编译器自动生成的即可满足需求,不需要显式实现。

const 成员函数

const 成员函数是指用 const 修饰的成员函数,放在参数列表后面,实际修饰的是隐含的 this 指针,表明在该函数中不能修改类的任何成员。

// const 成员函数示例
void Print() const // 实际为 void Print(const Date* const this)
{
    cout << _year << "-" << _month << "-" << _day << endl;
}

取地址运算符重载示例:

class Date {
public:
    // 普通取地址运算符重载
    Date* operator&() {
        return this;
        // return nullptr; // 可以自定义返回值
    }
    
    // const 取地址运算符重载
    const Date* operator&() const {
        return this;
        // return nullptr; // 可以自定义返回值
    }
private:
    int _year;
    int _month;
    int _day;
};

目录

  1. 默认成员函数概览
  2. 构造函数
  3. 析构函数
  4. 拷贝构造函数
  5. 赋值运算符重载
  6. 取地址运算符重载
  7. const 成员函数

更多推荐文章

查看全部
  • YOLOv11 数据集训练与推理实战指南
  • MAX4466 模拟麦克风语音前端放大电路设计与调试
  • 用 Prompt 生成正则表达式进行文本匹配
  • 零基础入门网络安全:学习路径与岗位方向解析
  • Python 标准库与生态应用指南:从入门到机器学习实战
  • 大模型微调的技术含量与实施策略深度解析
  • Claude Skills 开源仓库精选与安装配置实战指南
  • Stable Diffusion 模型加载失败排查与修复指南
  • 2025 年国产 AI 模型深度测评:文心大模型 4.5、DeepSeek、Qwen3 能力对比
  • Rust 异步 Web 框架 Axum 深入原理与高级用法
  • 企业为何强调建立标准操作流程 SOP
  • 电商 AI 绘画:产品提示词撰写实战指南
  • Flutter WalletConnect 鸿蒙适配:Web3 钱包连接与 DApp 授权实战
  • Linux 基础 IO:文件描述符、重定向与缓冲区原理
  • Android Studio JDK 与 SDK 配置指南
  • Linux 进程核心原理:从体系结构到实战操作(含 fork、状态与优先级)
  • 实战:从零构建自定义 Spring Boot Starter
  • MATLAB 2025a 发布:深色模式与 Copilot 编程助手详解
  • Qt Creator 配置 GitHub Copilot AI 编程插件
  • 前端实战:基于 Vue3 实现无限滚动、懒加载与瀑布流模块及优化策略

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online