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

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

详细解析了 C++ 类中的六大默认成员函数,涵盖构造函数、析构函数、拷贝构造函数、赋值运算符重载及取地址运算符重载。重点阐述了构造函数的初始化机制与重载规则,析构函数的资源清理职责及对象销毁顺序,以及拷贝构造与赋值运算符在深浅拷贝上的关键区别。通过代码示例展示了如何正确处理内存资源以避免泄漏或重复释放,并补充了 const 成员函数与取地址运算符的重载细节,帮助开发者深入理解对象生命周期管理。

古灵精怪发布于 2026/3/16更新于 2026/10/566 浏览
C++ 类和对象(二):默认成员函数详解

在 C++ 面向对象编程中,类的默认成员函数是非常核心的概念。很多时候我们并没有显式实现某些函数,但编译器会自动生成它们,这些就是默认成员函数。

默认成员函数概览

默认成员函数是指用户没有显式实现,由编译器自动生成的成员函数。一个类在不写任何成员函数的情况下,编译器会默认生成以下 6 个函数:

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

其中前 4 个是我们需要重点掌握的,后两个在大多数情况下使用编译器自动生成的即可。另外,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) {
    //     _year = year;
    //     _month = month;
    //     _day = day;
    // }
    
    void Print() {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

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

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

析构函数

析构函数的功能并不是销毁对象本身(对象在生命周期结束时会自动销毁),而是完成对象中资源的清理释放工作,类比之前实现的 Destroy 函数。

析构函数的特点:

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

typedef int STDataType;

class Stack {
public:
    Stack(int n = 4) {
        _a = (STDataType*)malloc(sizeof(STDataType) * n);
        if (nullptr == _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;
};

// 两个 Stack 实现队列
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++ 规定自定义类型对象进行拷贝行为必须调用拷贝构造(如传值传参、传值返回)
  • 若未显式定义,编译器会生成默认拷贝构造函数,对内置类型成员进行值拷贝 / 浅拷贝,对自定义类型成员调用其拷贝构造
  • 成员全是内置类型且无资源的类(如 Date)不需要显式实现;有资源的类(如 Stack)需要显式实现深拷贝,否则会导致双重释放
  • 传值返回会产生临时对象并调用拷贝构造;传引用返回可减少拷贝,但需确保返回对象在函数结束后仍存在
#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) // 使用 const 引用避免修改原对象
    {
        _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(); // 2025/11/30
    
    // 拷贝构造
    Date d2(d1);
    d2.Print(); // 2025/11/30
    
    Date d3 = d2; // 也是拷贝构造
    d3.Print();   // 2025/11/30
    return 0;
}

深拷贝实现(以 Stack 为例)

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

深 / 浅拷贝区别

  • 浅拷贝:按字节拷贝,对于指针成员只拷贝地址,不拷贝资源,可能导致多个对象共享同一份资源
  • 深拷贝:不仅拷贝指针本身,还对指针指向的资源重新申请空间并复制数据,每个对象拥有独立的资源

赋值运算符重载

赋值运算符重载用于完成两个已存在对象之间的拷贝赋值,与拷贝构造的区别是:拷贝构造用于用已有对象初始化新对象,而赋值重载用于两个已存在对象之间的赋值。

运算符重载基础:

  • 运算符重载是具有特殊名字的函数,形式为 operator 运算符
  • 重载运算符的参数个数与运算对象数量一致,一元运算符 1 个参数,二元运算符 2 个参数
  • 若为成员函数,第一个参数为隐式的 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 修饰的成员函数,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 成员函数

更多推荐文章

查看全部
  • Linux 进程间通信进阶:消息队列与信号量
  • Microi 吾码:基于 Spring Boot 的微服务框架与表单引擎解析
  • MySQL 数据库基础入门:从概念到实战
  • 互联网大厂 Java 与 Android 面试真题解析及求职准备指南
  • 攻防世界 Web 安全挑战题解 (八): 加密、反序列化与 RCE
  • MybatisPlus 集成 PostgreSQL 存储百度天气数据实战
  • 基于 Spring Cloud 的电商系统设计与实现:用户与商品模块
  • 数据结构基础:树的概念与堆的实现详解
  • 程序员海外接单指南:Freelancer、Upwork、Fiverr 与 Toptal 平台对比分析
  • 具身智能 Wrapper 架构解析与 Python 实战
  • 算法实战:双指针解决三数之和与四数之和
  • Mac 系统 OpenClaw 本地 AI 执行引擎安装与配置指南
  • 大语言模型(LLM)学习路径:从入门到实战指南
  • Clawdbot 整合 Qwen3-32B 本地部署与 18789 端口调试指南
  • OpenClaw 本地 AI 智能体入门与实战指南
  • Whisper 语音识别技术:本地部署与使用指南
  • LLM-AWQ多模态基准:10个INT4量化模型在20项任务上的全面评估
  • Kafka 核心架构与分布式存储详解
  • Python FastAPI 入门指南:从零构建生产级 RESTful API
  • Whisper 本地部署完整指南

相关免费在线工具

  • 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

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online