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

Mysql超详细安装配置教程(保姆级)

MySQL 一、下载 MySQL (一)下载地址 官网下载社区版 MySQL,推荐选择 MySQL 8.0.44 社区版(稳定版,兼容性强),下载地址:MySQL Community Downloads (二)下载步骤 1. Select Operating System 选择 Microsoft Windows; 2. 下载选项选择: * 推荐:Windows (x86, 64-bit), ZIP Archive(免安装压缩包,灵活配置),文件大小约 231.7M,点击 Download; * 备选:MySQL Installer for Windows(图形化安装程序,

By Ne0inhk
Flutter 组件 simple_cluster 的适配 鸿蒙Harmony 实战 - 驾驭轻量级集群分发架构、实现鸿蒙端多节点任务调度与高性能负载均衡方案

Flutter 组件 simple_cluster 的适配 鸿蒙Harmony 实战 - 驾驭轻量级集群分发架构、实现鸿蒙端多节点任务调度与高性能负载均衡方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 simple_cluster 的适配 鸿蒙Harmony 实战 - 驾驭轻量级集群分发架构、实现鸿蒙端多节点任务调度与高性能负载均衡方案 前言 在鸿蒙(OpenHarmony)生态迈向“万物互联、万物协同”的深水区后,单一设备孤岛式的算力模式已经无法满足复杂的工业控制、分布式协同办公以及大规模 IoT 设备管理的需求。面对需要将一个繁重的计算任务(如:海量 Hex 数据的指纹比对)分发给附近的 5 台鸿蒙平板协同处理;面对需要管理数十个传感器节点的实时状态同步。 如果依靠传统的手动 Socket 连接管理。那么不仅会导致通讯代码极其臃肿且难以维护。更会因为缺乏确定性的负载均衡(Load Balancing)与节点心跳(Heartbeat)逻辑。引发整个系统的雪崩式失效方案。 我们需要一种“逻辑集群化、操作极简化”的算力平衡艺术。

By Ne0inhk
【MYSQL】MYSQL学习的一大重点:MYSQL库的操作

【MYSQL】MYSQL学习的一大重点:MYSQL库的操作

🎬 个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》 ⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平 🎬 艾莉丝的简介: 文章目录 * 0 ~> 实际场景:创建和删除数据库 * 0.1 创建方式1 * 0.2 创建方式2 * 0.3 创建方式3 * 1 ~> 数据库的编码集 * 1.1 目前整个数据库支持的字符集 * 1.2 目前整个数据库支持的字符集 * 1.3 UTF-8需要设置配置文件 * 1.4 MySQL 中与字符集排序规则(

By Ne0inhk
Spring Boot 数据缓存与性能优化

Spring Boot 数据缓存与性能优化

Spring Boot 数据缓存与性能优化 23.1 学习目标与重点提示 学习目标:掌握Spring Boot数据缓存与性能优化的核心概念与使用方法,包括数据缓存的定义与特点、Spring Boot与数据缓存的集成、Spring Boot与数据缓存的配置、Spring Boot与数据缓存的基本方法、Spring Boot的实际应用场景,学会在实际开发中处理数据缓存与性能优化问题。 重点:数据缓存的定义与特点、Spring Boot与数据缓存的集成、Spring Boot与数据缓存的配置、Spring Boot与数据缓存的基本方法、Spring Boot的实际应用场景。 23.2 数据缓存概述 数据缓存是Java开发中的重要组件。 23.2.1 数据缓存的定义 定义:数据缓存是一种存储机制,用于将常用数据存储在高速存储设备中,以便快速访问。 作用: * 提高应用程序的性能。 * 减少数据库的访问次数。 * 提高用户体验。 常见的数据缓存: * EhCache:Apache EhCache是一款开源的缓存库。 * Caffeine:

By Ne0inhk