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

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

C++ 类中的六个默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载以及取地址运算符重载。阐述了各函数的定义、特点、调用时机及注意事项,重点区分了浅拷贝与深拷贝的概念,并通过 Date 和 Stack 类的代码示例展示了资源管理、内存泄漏预防及 const 成员函数的使用规范。

心动瞬间发布于 2026/3/30更新于 2026/10/2590 浏览
C++ 类和对象(二):默认成员函数详解

在 C++ 面向对象编程中,类的默认成员函数是非常重要的概念。当我们没有显式实现某些成员函数时,编译器会自动生成它们,这些函数被称为默认成员函数。本文将详细介绍 C++ 类的 6 个默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载以及取地址运算符重载。

一、默认成员函数概述

默认成员函数是指用户没有显式实现,编译器会自动生成的成员函数。一个类在我们不写任何成员函数的情况下,编译器会默认生成以下 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. 拷贝构造函数示例
  8. 深拷贝实现(以 Stack 为例)
  9. 五、赋值运算符重载
  10. 赋值运算符重载示例
  11. 六、取地址运算符重载
  12. const 成员函数
  13. 取地址运算符重载示例

更多推荐文章

查看全部
  • AI Coding 核心概念、工作流与工程化实践指南
  • DeepSeek 各版本说明与优缺点分析
  • OpenClaw AI 智能体对接腾讯 QQ 实战操作详解
  • GitHub 代码文件抓取与数据可视化实践(Python 实现)
  • Flutter Web 开发:构建跨平台 Web 应用
  • Ubuntu 22.04 配置 VMware 共享文件夹实战指南
  • 滑动窗口算法实战:串联所有单词子串与最小覆盖子串
  • Node.js + Vue 电影推荐系统设计与实现
  • C++ 深入理解:string 类的模拟实现与核心机制
  • 前端响应式进阶:从 vw/vh 到 clamp() 的实战思考
  • iRobotCAM 机器人离线编程软件在激光加工中的应用优势
  • 从 vw/vh 到 clamp():前端响应式设计的痛点与进化
  • 基于 YOLO12 的无人机航拍视角目标检测系统
  • C++ 哈希表封装:模拟实现 unordered_map 与 unordered_set
  • Spring Cloud Gateway Web MVC 使用指南
  • AI 绘画精讲与 AIGC 时代游戏美术设计:Stable Diffusion 实战指南
  • C++ 基础概念详解:命名空间、引用与函数重载
  • Java 中间件:RabbitMQ 消费端限流实战(basicQos 配置)
  • Flutter webrtc_interface 鸿蒙化适配与实战指南
  • Flutter 组件 tavily_dart 在鸿蒙系统的适配与进阶应用

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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