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

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; // 日 };

Read more

Node.js 版本管理全指南:卸载 Node、安装 NVM、常用命令及问题解决

Node.js 版本管理全指南:卸载 Node、安装 NVM、常用命令及问题解决

一、卸载现有 Node.js(避免版本冲突) 在安装 Node Version Manager (NVM) 前,必须彻底卸载电脑中已有的 Node.js(若从未安装可跳过此步骤),否则可能导致 NVM 管理失效或版本冲突问题。 1. Windows 系统卸载流程 步骤 1:通过控制面板卸载 Node.js 1. 按下 Win + R 组合键打开运行窗口 2. 输入 control 并回车打开「控制面板」 3. 导航至「程序」→「程序和功能」 4. 在程序列表中找到所有 Node.js 相关条目(可能有多个版本) 5. 对每个 Node.js

By Ne0inhk
RabbitMQ如何成为分布式系统的“神经中枢“?——从安装部署到C++调用实战的完整流程,带你体验它的奥妙所在!​

RabbitMQ如何成为分布式系统的“神经中枢“?——从安装部署到C++调用实战的完整流程,带你体验它的奥妙所在!​

文章目录 * 本篇摘要 * ①·RabbitMq(轻量级消息队列中间件) 介绍 * RabbitMQ 是什么? * 核心功能与特点 * 1. **核心功能** * 2. **核心优势** * RabbitMQ 的核心概念 * 1. **生产者(Producer)** * 2. **消费者(Consumer)** * 3. **队列(Queue)** * 4. **交换机(Exchange)** * 5. **绑定(Binding)** * 工作流程(以 Direct 交换机为例) * 常见应用场景 * RabbitMQ 与相关技术对比 * 图像理解 * 总结一句话 * ②·RabbitMq 安装教程 * RabbitMq安装 * **1. 安装 RabbitMQ** * **2. 启动 & 检查状态** * **3. 创建管理员用户(

By Ne0inhk
【OpenClaw从入门到精通】第12篇:OpenClaw 2026子代理实战全攻略:用ACP架构搭建多智能体协作网络(附完整代码)

【OpenClaw从入门到精通】第12篇:OpenClaw 2026子代理实战全攻略:用ACP架构搭建多智能体协作网络(附完整代码)

摘要:本文聚焦OpenClaw 2026.3.2版本默认启用的ACP(Agent Communication Protocol)子代理机制,从核心概念、底层原理到企业级实操全流程拆解。先解析子代理的定义、ACP协议通信原理及主从代理分工逻辑,再提供环境准备的详细步骤(版本验证、配置调整、技能安装);随后通过三个递进式虚拟案例(客服分流、智能文档处理、企业级招聘全流程),提供可直接运行的完整代码、执行结果及技术要点解析;最后补充子代理管理运维、性能优化与避坑指南。全文注重实用价值,语言通俗,新手可按步骤快速搭建基础子代理,进阶读者可参考企业级案例扩展复杂协作网络,真正实现AI从“单打独斗”到“军团作战”的升级。 优质专栏欢迎订阅! 【DeepSeek深度应用】【Python高阶开发:AI自动化与数据工程实战】【YOLOv11工业级实战】 【机器视觉:C# + HALCON】【大模型微调实战:平民级微调技术全解】 【人工智能之深度学习】【AI 赋能:Python 人工智能应用实战】【数字孪生与仿真技术实战指南】

By Ne0inhk
Mysql之事务(下)

Mysql之事务(下)

🏝️专栏:Mysql_猫咪-9527的博客-ZEEKLOG博客 🌅主页:猫咪-9527-ZEEKLOG博客  “欲穷千里目,更上一层楼。会当凌绝顶,一览众山小。” 目录 5. 事务的隔离级别与并发控制 5.1事务的隔离级别 5.2查看与设置事务的隔离级别  查看全局隔离级别: 查看当前(会话)隔离级别 5.3读未提交(Read Uncommitted) 5.4读提交(Read Committed) 5.5可重复读(Repeatable Read) 5.6串行化(Serializable) 6.多版本并发控制(MVCC)   6.1数据库并发的三种场景及MVCC 6.2三个隐藏字段  6.3undo log 6.4READ VIEW 6.5事务简单流程

By Ne0inhk