【C++修炼之路】C++类类和对象进一步探索,六个幕后英雄

【C++修炼之路】C++类类和对象进一步探索,六个幕后英雄
Alt

🏝️专栏: 【C++修炼之路】
🌅主页: f狐o狸x

“于高山之巅,方见大河奔涌;于群峰之上,更觉长风浩荡” 


        在上一篇《C++类与对象入门:从封装到this指针的初探》中,我们学习了如何定义类、创建对象,并通过封装保护数据。然而,类的真正力量远不止于此——

        当你在代码中写下MyClass obj;时,编译器默默为你生成了6个关键函数,它们掌控着对象的诞生、复制、移动与消亡。

        一、类的默认6个成员函数

        当你写了一个类,但是里面什么都没有,简称空类。

class Date { };

        空类里面就真的什么都没有吗?

        并不是这样的,其实编译器已经帮你默认生成了六个函数

 二、构造函数:对象的“出生证明”

        在C++中,构造函数是类的特殊成员函数,负责在对象创建时进行初始化。它是对象的“出生证明”,确保对象在诞生时处于有效状态。如果没有构造函数,对象可能包含未初始化的数据,导致程序行为不可预测。

        2.1 构造函数的概念

        构造函数的名称必须与类名相同,且没有返回值(包括void)。例如:

class MyClass { public: MyClass() { // 默认构造函数 std::cout << "默认构造函数被调用!" << std::endl; } };

        构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

         2.2 构造函数的特征

  1. 无参数或所有参数有默认值。
  2. 如果未定义任何构造函数,编译器会自动生成一个默认构造函数。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
 class Date {  public:      // 1.无参构造函数      Date()     {}        // 2.带参构造函数      Date(int year, int month, int day)     {          _year = year;          _month = month;          _day = day;     }  private:      int _year;      int _month;      int _day; };    void TestDate() {      Date d1; // 调用无参构造函数      Date d2(2015, 1, 1); // 调用带参的构造函数 }

        5.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦

用户显式定义编译器将不再生成。

        6.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

三、析构函数:对象的“临终遗言”

        3.1析构函数的概念

         在C++中,析构函数是类的特殊成员函数,负责在对象销毁时释放资源。它是对象的“临终遗言”,确保对象在离开内存舞台时不会留下“垃圾”。如果没有析构函数,动态分配的资源可能无法释放,导致内存泄漏或资源浪费。

        与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

        3.2 析构函数的特性

        1. 析构函数名是在类名前加上字符 ~。
        2. 无参数无返回值类型。
        3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
        4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

#define _CRT_SECURE_NO_WARNINGS 1; //class Date //{ //public: // Date() // { // _year = 1900; // _month = 1; // _day = 1; // } // Date(int year = 1900, int month = 1, int day = 1) // { // _year = year; // _month = month; // _day = day; // } //private: // int _year; // int _month; // int _day; //}; //// 以下测试函数能通过编译吗? //void main() //{ // Date d1(2004,2,5); //} class Time { public: ~Time() { cout << "~Time()" << endl; } private: int _hour; int _minute; int _second; }; class Date { private: // 基本类型(内置类型) int _year = 1970; int _month = 1; int _day = 1; // 自定义类型 Time _t; }; int main() { Date d; return 0; }

        5. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

        四、拷贝构造函数:对象的“克隆术”

        4.1 拷贝构造函数的概念

        在C++中,拷贝构造函数是类的特殊成员函数,用于用一个对象初始化另一个对象。它是对象的“克隆术”,确保新对象的内容与原对象一致。如果没有拷贝构造函数,对象复制时可能导致资源管理问题(如内存泄漏或重复释放)。 

        拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用

        4.2 拷贝构造函数的特性

        1. 拷贝构造函数是构造函数的一个重载形式。

        2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

class MyClass { public: MyClass(const MyClass& other) { // 拷贝构造函数 std::cout << "拷贝构造函数被调用!" << std::endl; } };

        3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

class Time { public: Time() { _hour = 1; _minute = 1; _second = 1; } Time(const Time& t) { _hour = t._hour; _minute = t._minute; _second = t._second; cout << "Time::Time(const Time&)" << endl; } private: int _hour; int _minute; int _second; }; class Date { private: // 基本类型(内置类型) int _year = 1970; int _month = 1; int _day = 1; // 自定义类型 Time _t; }; int main() { Date d1; Date d2(d1); return 0; }

        注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。

         4.3 浅拷贝和深拷贝

        如果未定义拷贝构造函数,编译器会生成一个默认拷贝构造函数。但默认拷贝构造函数是浅拷贝,可能导致资源管理问题。

class MyClass { public: int* ptr; MyClass() { ptr = new int(10); // 动态分配内存 } // 未定义拷贝构造函数,编译器生成默认拷贝构造函数 };

        上述代码中,默认拷贝构造函数会复制指针ptr的值,导致两个对象共享同一块内存。当其中一个对象销毁时,另一个对象的指针将指向无效内存。

        深拷贝与浅拷贝

浅拷贝:只复制指针的值,不复制指针指向的内容。深拷贝:复制指针指向的内容,确保每个对象拥有独立的资源。
class MyClass { public: int* ptr; MyClass() { ptr = new int(10); // 动态分配内存 } MyClass(const MyClass& other) { // 深拷贝构造函数 ptr = new int(*other.ptr); // 复制指针指向的内容 std::cout << "深拷贝构造函数被调用!" << std::endl; } ~MyClass() { delete ptr; // 释放内存 } };

 五、运算符重载:让自定义类型“支持”运算符

        在C++中,运算符重载是一种强大的特性,允许我们为自定义类型(如类)定义运算符的行为。通过运算符重载,可以使代码更直观、易读。例如,我们可以让两个对象直接相加,而不需要调用繁琐的函数。

        5.1 运算符重载的基本特性

        运算符重载的函数名是operator后接运算符符号(如operator+)。它可以是成员函数或全局函数。例如:

class MyClass { public: int value; // 成员函数形式的运算符重载 MyClass operator+(const MyClass& other) { MyClass result; result.value = this->value + other.value; return result; } };

        5.2 常见运算符的重载

        (1)算术运算符

        算术运算符(如+-*/)通常用于数学运算。例如:

class MyClass { public: int value; // 加法运算符重载 MyClass operator+(const MyClass& other) { MyClass result; result.value = this->value + other.value; return result; } // 减法运算符重载 MyClass operator-(const MyClass& other) { MyClass result; result.value = this->value - other.value; return result; } };

        (2)关系运算符

        关系运算符(如==!=<>)通常用于比较对象。例如:

class MyClass { public: int value; // 相等运算符重载 bool operator==(const MyClass& other) { return this->value == other.value; } // 不等运算符重载 bool operator!=(const MyClass& other) { return this->value != other.value; } };

        (3)赋值运算符

        赋值运算符(如=)用于对象赋值。例如:

class MyClass { public: int* ptr; // 赋值运算符重载 MyClass& operator=(const MyClass& other) { if (this != &other) { // 处理自赋值 delete ptr; // 释放原有资源 ptr = new int(*other.ptr); // 复制指针指向的内容 } return *this; } };

        (4)流插入和提取运算符

        流插入(<<)和提取(>>)运算符通常用于输入输出。例如:

#include <iostream> class MyClass { public: int value; // 流插入运算符重载(全局函数) friend std::ostream& operator<<(std::ostream& os, const MyClass& obj) { os << "MyClass value: " << obj.value; return os; } // 流提取运算符重载(全局函数) friend std::istream& operator>>(std::istream& is, MyClass& obj) { is >> obj.value; return is; } };

5.3  注意事项

  • 不能重载的运算符
    部分运算符不能重载,如.::?:等。
  • 优先级和结合性
    运算符重载不能改变运算符的优先级和结合性。
  • 语义一致性
    运算符重载应保持语义一致性。例如,+应用于加法,而不是减法。

5.4  总结

        运算符重载是C++中一种强大的特性,它允许我们为自定义类型定义运算符的行为。通过运算符重载,可以使代码更直观、易读。理解运算符重载的基本规则和使用场景,是掌握C++面向对象编程的重要一步。

六、取地址及const取地址操作符重载

        这两个成员函数一般用的频率不高,所以这里就不详细讲了哈

        这两个默认成员函数一般不用重新定义 ,编译器默认会生成

class Date { public: Date* operator&() { return this; } const Date* operator&()const { return this; } private: int _year; // 年 int _month; // 月 int _day; // 日 };

        C++的面向对象编程是一个庞大而精妙的体系,掌握这些基础知识是迈向高级编程的必经之路。希望本文能为你打下坚实的基础,助你在C++的世界中游刃有余!

Read more

最新Spring Security实战教程(十一)CSRF攻防实战 - 从原理到防护的最佳实践

最新Spring Security实战教程(十一)CSRF攻防实战 - 从原理到防护的最佳实践

🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Micro麦可乐的博客 🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战 🌺《RabbitMQ》专栏19年编写主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战 🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解 🌛《开源项目》本专栏主要介绍目前热门的开源项目,带大家快速了解并轻松上手使用 ✨《开发技巧》本专栏包含了各种系统的设计原理以及注意事项,并分享一些日常开发的功能小技巧 💕《Jenkins实战》专栏主要介绍Jenkins+Docker的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程 🌞《Spring Boot》专栏主要介绍我们日常工作项目中经常应用到的功能以及技巧,代码样例完整 🌞《Spring Security》专栏中我们将逐步深入Spring Security的各个技术细节,带你从入门到精通,全面掌握这一安全技术 如果文章能够给大家带来一定的帮助!欢迎关注、评

By Ne0inhk
windows 如何更新npm及node.js到最新版本

windows 如何更新npm及node.js到最新版本

在 Windows 系统上更新 npm(Node Package Manager)可以通过以下步骤完成: 方法 1:直接更新 npm 1. 以管理员身份打开命令行工具(CMD 或 PowerShell): * 右键点击「开始菜单」→ 选择 Windows Terminal (Admin) 或 命令提示符(管理员)。 * -g 表示全局安装。 * 如果遇到权限问题,可以尝试使用系统代理或关闭杀毒软件。 验证更新是否成功: npm-v 更新 npm 到最新版本: npminstall-g npm@latest 方法 2:更新 Node.js 以附带更新 npm 如果 npm 版本过旧,可能需要直接升级 Node.

By Ne0inhk
【MySQL筑基篇】新手必看:聚簇索引、非聚簇索引与回表,一篇扫清盲区

【MySQL筑基篇】新手必看:聚簇索引、非聚簇索引与回表,一篇扫清盲区

🍃 予枫:个人主页 📚 个人专栏: 《Java 从入门到起飞》《读研码农的干货日常》 💻 Debug 这个世界,Return 更好的自己! 引言 做后端开发的同学,大概率都听过“索引优化”,也用过主键索引提升查询速度。但你真的懂索引吗?日常开发中,不少同学遇到查询卡顿就盲目加索引,结果反而导致数据增删改效率下降;还有人疑惑,为什么同样是索引,主键查询秒出结果,普通索引查询却要慢半拍?除了主键,还有哪些核心索引类型?为什么有的查询不用“绕路”,有的却要额外“回表”?今天咱们从数据库物理存储的底层逻辑出发,逐字拆解聚簇索引与非聚簇索引的核心概念,帮你夯实索引入门基础,为后续吃透B+树结构、搞定索引优化铺路~ 建议点赞收藏,避免后续需要时找不到! 文章目录 * 引言 * 一、索引基础:不止是“快速查找” * 1.1 为什么需要关注物理存储? * 二、聚簇索引:数据与索引“合二为一”

By Ne0inhk
Spring Boot AOP(三) 通知执行链源码解析

Spring Boot AOP(三) 通知执行链源码解析

博主社群介绍: ① 群内初中生、高中生、本科生、研究生、博士生遍布,可互相学习,交流困惑。 ② 热榜top10的常客也在群里,也有数不清的万粉大佬,可以交流写作技巧,上榜经验,涨粉秘籍。 ③ 群内也有职场精英,大厂大佬,跨国企业主管,可交流技术、面试、找工作的经验。 进群免费赠送写作秘籍一份,助你由写作小白晋升为创作大佬,进群赠送ZEEKLOG评论防封脚本,送真活跃粉丝,助你提升文章热度。 群公告里还有全网大赛约稿汇总/博客提效工具集/ZEEKLOG自动化运营脚本 有兴趣的加文末联系方式,备注自己的ZEEKLOG昵称,拉你进群,互相学习共同进步。 文章目录 * Spring Boot AOP(三) 通知执行链源码解析 * 1. 执行链概述 * 核心概念 * 2. Advisor 链与通知统一处理 * Mermaid 流程图:Advisor 链构建 * 3. MethodInterceptor 执行流程

By Ne0inhk