C++类和对象【下】

发布时间:2026/10/26 1:30:53

C++类和对象【下】 目录4.3拷贝构造函数4.4赋值运算符重载运算符重载什么是运算符重载如何定义运算符重载定义运算符重载时的注意事项5.const成员函数6.初始化列表7.类型转换8.static成员8.1static修饰成员变量8.2static修饰成员函数9.友元10.内部类11.匿名对象续上4.3拷贝构造函数如果⼀个构造函数的第一个参数是自身类类型的引用且任何额外的参数都有默认值则此构造函数也叫做拷贝构造函数也就是说拷贝构造是一个特殊的构造函数。拷贝构造的特点1.拷贝构造是构造函数的重载2.拷贝构造的第一个参数必须是自生类类型的引用因为在传参过程中其实实际上也是一个拷贝而类类型就会调用拷贝构造去初始化形参这样就会陷入无限递归3.拷贝构造函数也可以多个参数但是第一个参数必须是类类型对象的引用后面的参数必须有缺省值class Date { Date(const Date d, int day 0 /* ...其他参数(有默认值) */)// 拷贝构造 { // ... } };4.若未显式定义拷贝构造则编译器会默认生成一个拷贝构造且该拷贝是对内置类型的变量是浅拷贝对自定义类型变量则会去调用它的拷贝构造浅拷贝一个字节一个字节的拷贝class Date { public: Date(int year 0, int month 0, int day 0) :_year(year), _month(month), _day(day) {} Date(const Date d) { _year d._year; _month d._month; _day d._day; } private: int _year; int _month; int _day; }; class news { public: news(const Date val, const char* _str) { d val; weather new char[strlen(_str) 1]; strcpy(weather, _str); } private: Date d; char* weather; }; int main() { Date d(2026, 4, 28); news n1(d, 晴); news n2(n1);// 未实现news拷贝构造,weather直接浅拷贝,Date类型调用Date类类型的拷贝构造 return 0; }可以看到n1和n2的weather指向同一处地址这样导致改变其中一处的weather会导致另一处的更改这就是浅拷贝的坏处所以这时候就要自己来实现拷贝构造(实现深拷贝)也就是说若类成员变量指向了堆上的资源就要实现深拷贝同时因为申请了堆上的空间就要实现析构函数所以需要我们自己实现析构函数的类都需要写拷贝构造否则不需要news(const news n)// 拷贝构造 { d n.d; weather new char[strlen(n.weather) 1]; strcpy(weather, n.weather); } ~news()// 析构函数 { delete[] weather; }4.4赋值运算符重载运算符重载什么是运算符重载当运算符被用于类类型的对象时C允许我们通过运算符重载的形式指定新的含义。C规定类类型对象使用运算符时必须转换成调用对应运算符重载若没有对应的运算符重载则会编译报错就比如我们在日期类要让日期 d 加上1我们想要的是d中的“day”加1但是编译器并不知道所以就有了运算符重载让我们自己去实现这些定义如何定义运算符重载1.运算符重载是具有特殊名字的函数他的名字是由operator和后面要定义的运算符共同构成。和其他函数⼀样它也具有其返回类型和参数列表以及函数体2.如果一个重载运算符函数是成员函数则它的第⼀个运算对象默认传给隐式的this指针因此运算符重载作为成员函数时参数比运算对象少一个3.重载运算符函数的参数个数和该运算符作用的运算对象数量⼀样多。一元运算符有⼀个参数二元运算符有两个参数并且二元运算符的左操作数为它的第一个参数右操作数为第二个参数4.运算符重载以后其优先级和结合性与对应的内置类型运算符保持⼀致以赋值运算符为例定义运算符重载时的注意事项1.不能通过连接语法中没有的符号来创建新的操作符比如operator2.(.*) (::) (sizeof) ( ? : ) ( . )该五个运算符不能实现函数重载用小括号做了区分3.重载操作符至少有⼀个类类型参数且不能通过运算符重载改变内置类型对象的含义比如int operator(int a, int b) { return a - b; }4.⼀个类需要重载哪些运算符是看哪些运算符重载后有意义比如Date类重载operator-就有意 义日期减日期 天数但是重载operator就没有意义日期加日期 5.重载运算符时有前置和后置运算符重载函数名都是operator就无法很好的区分。 所以C规定后置重载时增加⼀个int形参跟前置构成函数重载方便区分。Date operator()// 前置 { // ... } Date operator(int)// 后置 { // ... }6.重载二元运算符 或 时推荐重载为全局函数搭配友元因为重载为成员函数this指针抢占了第一个参数的位置调用时就不符合习惯可读性也差了(这里仅做演示这里用了友元的知识代码细节不必考虑后面会细讲)重载为成员函数重载为全局函数至此有了以上理论基础就能够实现赋值运算符重载了Date operator(const Date d) { _year d._year; _month d._month; _day d._day; return *this; }当然默认生成的赋值运算符重载依旧是浅拷贝想日期类就不用我们自己生成5.const成员函数我们之前已经说过每一个成员函数都会有一个默认不显示的隐藏参数——this指针我们也知道如果不想实参在函数中被修改我们就可以用const来修饰那如果我们不想让成员函数去修改this指针指向的对象的数据我们该如何让const去修饰这个隐藏的参数呢答案是将const放到成员函数参数列表的后面让const修饰成员函数例如int operator-(const Date d) const { // ... }被const修饰的成员函数称之为const成员函数表明在该成员函数中不能对类的任何成员进行修改this指针Date* const this ----- const Date* const this6.初始化列表之前我们实现构造函数时初始化成员变量主要使用函数体内赋值构造函数初始化还有⼀种方式就是初始化列表初始化列表的使用方式是以⼀个冒号开始接着是⼀个以逗号分隔的数据成员列表每个成员变量后面跟⼀个放在括号中的初始值或表达式class Date { public: //Date(int year, int month, int day)// 构造函数 //{ // _year year; // _month month; // _day day; //} Date(int year, int month, int day)// 初始化列表 :_year(year), _month(month), _day(day) {} private: int _year; int _month; int _day; }在使用初始化列表时要注意以下事项1.每个成员变量在初始化列表中只能出现一次语法理解上初始化列表可以认为是每个成员变量定义初始化的地方2.引用成员变量const成员变量没有默认构造的类类型变量必须放在初始化列表位置进行初始化否则会编译报错class Date// 无默认构造的类 { public: Date(int year, int month, int day) :_year(year), _month(month), _day(day) {} Date(const Date d) { _year d._year; _month d._month; _day d._day; } private: int _year; int _month; int _day; } class news { public: news(const Date val, char val_char, int val_int) :d(val) , a(val_char) , b(val_int) {} private: Date d; const char a; int b; };3.C11支持在成员变量声明的位置给缺省值这个缺省值主要是给没有显示在初始化列表初始化的成员使用的4.初始化列表中按照成员变量在类中声明顺序进行初始化跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持⼀致我们在初始化类成员变量时尽量使用初始化列表初始化因为你不在初始化列表初始化的成员也都会走初始化列表如果这个变量在声明时给了缺省值则会用缺省值进行初始化如果没有缺省值对于内置类型的初始化情况就取决于编译器了而对于自定义类型则会调用他的默认构造如果没有则会报错总结7.类型转换C中支持内置类型隐式类型转化为类类型对象前提是有对应的内置类型参数的构造函数类类型的对象之间也可以隐式转换需要相应的构造函数支持class A { public: A(int val) :_a(val) {} int GetVal() const { return _a; } private: int _a; }; class B { public: B(const A a) :b(a.GetVal()) {} private: int b; }; int main() { A a(1); B b(a); return 0; }如果我们不想再支持隐式类型转换就在构造函数前加上explicit就行8.static成员用static修饰的变量或成员叫做静态成员变量或静态成员函数非静态的成员函数可以访问任意的静态成员变量和静态成员函数突破类域就可以访问静态成员可以通过“ 类名::静态成员 ”或者“ 对象.静态成员 ”来访问静态成员变量和静态成员函数静态成员也是类的成员受访问限定符的限制8.1static修饰成员变量用static修饰的成员变量叫做静态成员变量静态成员变量是要在类外进行初始化静态成员变量为所有类对象所共享不属于某个具体的对象不存在对象中存放在静态区namespace zhangsan { class string { public: // ... static size_t npos;// 类中声明 void test() { cout npos endl; } private: char* _str; size_t _size; size_t _capacity; }; size_t string::npos -1;// 类外进行初始化 }注不能再声明处给缺省值因为缺省值是给走初始化列表的成员变量进行初始化的而静态成员变量不走初始化列表8.2static修饰成员函数用static修饰成员函数叫做静态成员函数静态成员函数中没有this指针静态成员函数可以访问其他静态成员变量但不能访问非静态的因为没有this指针9.友元友元提供了一种突破类访问限定符封装的方式。友元分为友元函数和友元类在函数声明或者类 声明的前面加“ friend ”并且把友元声明放到⼀个类的里面。外部友元函数可访问类的私有和保护成员友元函数仅仅是⼀种声明他不是类的成员函数友元函数可以在类定义的任何地方声明不受类访问限定符限制⼀个函数可以是多个类的友元函数// 前置声明 class B; class A { // 友元声明 friend void func(const A aa, const B bb); private: int _a1 1; int _a2 2; }; class B { // 友元声明 friend void func(const A aa, const B bb); private: int _b1 3; int _b2 4; }; void func(const A aa, const B bb) { cout aa._a1 endl; cout bb._b1 endl; }友元类中的成员函数都可以是另⼀个类的友元函数都可以访问另⼀个类中的私有和保护成员class A { public: // 友元声明 A() { // ... } friend class B; private: int _a1; int _a2; }; class B { public: B() { // ... } void func1(const A aa) { cout aa._a1 endl; cout _b1 endl; } void func2(const A aa) { cout aa._a2 endl; cout _b2 endl; } private: int _b1; int _b2; };我们在上面实现 或 运算符重载时由于友元函数的特性我们就也用了友元函数这样一来就符合了调用时的习惯也提高了代码的可读性友元类的关系是单向的不具有交换性比如A类是B类的友元但是B类不是A类的友元友元类关系不能传递如果A是B的友元B是C的友元但是A不是C的友元注友元虽然提供了便利但是友元会增加耦合度破坏了封装所以友元不宜多用10.内部类如果⼀个类定义在另⼀个类的内部这个内部类就叫做内部类内部类是⼀个独立的类他只是受外部类类域限制和访问限定符限制所以外部类定义的对象中不包含内部类内部类默认是外部类的友元类class A { private: static int _k; int _h 1; public: class B // B默认就是A的友元 { public: void foo(const A a) { cout _k endl; cout a._h endl; } int _b1; }; }; int A::_k 1; int main() { A a; A::B b; b.foo(a); return 0; }内部类本质也是⼀种封装当A类跟B类紧密关联A类实现出来主要就是给B类使用那么可以考 虑把A类设计为B的内部类如果放到private/protected位置那么A类就是B类的专属内部类其 他地方都用不了就比如下面这一道题求123...n_牛客题霸_牛客网https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId13tqId11200tPage3rp3ru/ta/coding-interviewsqru/ta/coding-interviews/question-rankingclass Solution { class Sum { public: Sum() { sum i; i; } }; public: int Sum_Solution(int n) { Sum arr[n]; return sum; } static size_t sum; static size_t i; }; size_t Solution::sum 0; size_t Solution::i 1;这种解法就很清楚地体现内部类的用法11.匿名对象用类型(实参)定义出来的对象叫做匿名对象相比之前我们定义的类型对象名(实参)定义出来的叫有名对象class A { public: A(const int val) :a(val) {} void func() { cout a endl; } private: int a; }; int main() { A(1).func();// 匿名对象 A a(2);// 有名对象 a.func(); return 0; }匿名对象生命周期只在当前一行一般临时定义一个对象当前用一下即可就可以定义匿名对象好了以上就是类和对象的全部内容了感谢阅读希望能够帮到你也欢迎大家指错及补充
延伸阅读

更多相关文章

2026/10/26 1:25:53

Design Compiler:使用HPC Megaswitch

拓扑模式的Design Compiler NXT在2019版本针对ARM的高性能处理器内核综合引入了一个功能,用户可以使用set_hpc_options命令针对内核应用推荐的设置。这些设置包括:针对特定内核的应用变量设置、针对特定内核的解组策略选择、针对特定内核的路径分组方式。…

2026/10/26 1:25:53

【C++】现代C++的constexpr特性

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 1:25:53

国产AI工具生态全景:从开发到部署的实践指南

1. 国产AI工具生态现状全景2026年的中国AI工具市场已经形成了完整的金字塔结构。底层是基础大模型提供商,中间层是垂直领域解决方案,顶层则是面向终端用户的轻量化应用。与三年前相比,最显著的变化是工具链的国产化率从2023年的37%提升至86%&…

2026/10/26 2:45:57

Android显示系统架构与性能优化实战

1. Android显示系统架构概述Android显示系统是连接应用层与硬件层的核心桥梁,其设计哲学遵循"分层解耦"原则。从应用开发者视角看,最常接触的是View体系;而从系统层面看,WindowManagerService(WMS)和SurfaceFlinger构成…

2026/10/26 2:45:57

Linux系统日志分析与常用命令实战指南

1. Linux系统日志概述与核心价值日志系统是Linux运维工程师的"黑匣子",记录了系统运行的每一个关键事件。我在实际运维工作中发现,90%以上的故障排查都需要依赖日志分析。不同于Windows系统将日志存储在专用事件查看器中,Linux采用…

2026/10/26 2:45:57

教育数据分析:动态分层评价体系与黑马识别

1. 项目概述:京北中学期中排名分析的价值与意义每次期中考试后的排名公布,都是检验教学成果与学习成效的重要时刻。2026年京北中学的期中排名尤其值得关注,因为这所百年名校正在经历教学改革的关键时期。作为长期跟踪教育数据的从业者&#x…

2026/10/26 2:45:57

GY-AS7262/AS7263六通道光谱模块选型与调参避坑

简介:GY-AS7262 7263 配套资料包面向嵌入式开发、电子制造与传感器应用学习者,围绕 AS7262/AS7263 光谱传感模块的 Arduino 上手与二次开发整理。资源包含15个文件,约1.5MB,以3份PDF数据手册与原理图、3个ino示例程序、2份md说明、…

2026/10/26 2:45:57

AI测试助手:提升系统工程效率的智能解决方案

1. 项目概述:AI测试助手如何赋能系统工程师在系统工程的复杂世界里,每个决策都可能引发连锁反应。三年前我在一次数据中心迁移项目中,因为一个未被发现的兼容性问题导致整个系统宕机8小时,那次教训让我意识到:传统测试…

2026/10/26 2:40:57

UNet图像分割实战:PyTorch实现、训练链路与损失函数选型

先说个我自己的经历:去年帮人做一个细胞显微图像的分割练手项目,需求很朴素——把视野里每一张细胞膜轮廓都描出来,输出一张和原图同尺寸的二值掩膜。我第一反应就是 UNet,从拉代码到跑出第一版能看的结果,前后不到十分…

2026/10/26 0:20:20

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/26 1:53:47

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/26 0:20:17

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/26 0:00:48

Nested Learning:解决AI灾难性遗忘的分层记忆架构

1. 为什么AI需要"记忆"能力?在传统深度学习模型中,我们常遇到一个根本性问题:模型在处理新任务时,往往会完全遗忘之前学到的知识。这种现象被业界称为"灾难性遗忘"(Catastrophic Forgetting)。想象一下&#…

2026/10/26 0:00:48

企业级大模型应用:OCR预处理、Dify编排与DeepSeek落地实战

1. 这不是“用大模型”,而是重构工作流的起点“大模型的应用和工具”——这八个字现在被贴在无数技术分享、招聘JD、内部立项书的标题栏里,但绝大多数人点进去后看到的,是零散的API调用示例、几个开源UI截图、或者一句“接入LLM就能降本增效”…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑