九、重学C++—类和函数

上一章节:

八、重学C++—动态多态(运行期)-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/147004745?spm=1001.2014.3001.5502

本章节代码:

cpp/cppClassAndFunc.cpp · CuiQingCheng/cppstudy - 码云 - 开源中国https://gitee.com/cuiqingcheng/cppstudy/blob/master/cpp/cppClassAndFunc.cpp

一、引言

        学到这里,会深深被C++的灵活多变所折服,学习C++的过程仿佛遨游在瑰丽多彩的海底世界。类与函数便是其中极为精妙的招式。今天,就让我们一起重新踏上探索 C++ 类与函数的奇妙之旅,揭开它们神秘的面纱。就像古人云:“温故而知新,可以为师矣。” 重学 C++ 的类与函数,也能让我们对编程有全新的感悟。

二、重新认识类:类中成员和特殊类

1、内联函数:代码中的“瞬移大师“

        内联函数,可以是类中的,也可以是普通函数。在常见的函数调用中,程序需要跳转去执行函数代码,执行完再跳转回来,这中间会消耗一些时间和资源。而内联函数呢,编译器会直接将函数代码嵌入到调用它的地方,因此这里常与宏函数对比

例如:

/***  1、内联函数*/
#include <iostream>// 宏函数
# define ADD(a,b)  (a+b)// 内联函数
inline int add(int& a, int &b)
{return a+b;
}class optData{
public:// 类中内联函数inline int add(int& a, int &b){return a+b;}
};int main()
{int n = 5;int m = 13;std::cout << "sum1: " << add(n,m) << std::endl;std::cout << "sum2: " << ADD(n,m) << std::endl;optData opt;std::cout << "sum3: " << opt.add(n,m) << std::endl;return 0;
}

宏函数不推荐使用,宏函数在设计初,就存在以下缺点,(1)、类型不安全,无法进行类型检测(2)、无法调试(3)、无法进行递归调用(4)、可读性差。

使用场景:

(1)、短小且频繁调用的函数:当函数体代码量很少(通常不超过 3 - 5 行 ),且在程序中会被频繁调用时,适合定义为内联函数。

(2)、 模板函数:简短的模板函数在编译时会生成具体的函数实例,更容易被内联化。内联后的模板函数能提供更好的性能

#include <iostream>// 模板内联函数,返回两个数中较大的值
template <typename T>
inline T max(T a, T b) {return a > b? a : b;
}int main()
{int n = 5;int m = 13;int iMax = max(n, m);std::cout << "iMax: " << iMax << std::endl;return 0;
}

优点:

        减少函数调用开销;提高程序执行效率;增强代码可读性;类型安全检查;相比于宏函数方便可调式

2、静态成员:不依赖对象存在,是类中的”常驻大使“

        静态成员变量是类的所有对象共享的一个变量,它就像班级里的公共财产,不管哪个同学都可以使用它

例如:

/***  1、静态成员*/#include <iostream>class Student {
public:Student(std::string name) :m_name(name){++m_totalStudents;}~Student() {--m_totalStudents;}// 静态成员函数static int getTotalStudents() {// ++m_totalStudents; 错误静态成员函数中不能出现普通成员变量,这里仅能操作静态成员变量;return m_totalStudents;}static std::string getSchoolName(){return m_schoolName;}// 普通成员函数int getStudentsCount(){return getTotalStudents();}
private:std::string m_name;// 静态成员变量static std::string m_schoolName;static int m_totalStudents;
};std::string Student::m_schoolName = "上海中学";
int Student::m_totalStudents = 0;int main()
{// 静态成员Student stu1("小明");Student stu2("小红");std::cout << "学生总数:" << Student::getTotalStudents() << " 学校名:"<< Student::getSchoolName()<<std::endl;std::cout << "小明知道的学生总数:" << stu1.getStudentsCount() << std::endl;return 0;
}

注意:

(1)静态成员函数只能操作静态成员变量;

(2)静态成员属于整个类共有的,不依赖于类所创建的对象;

(3)普通成员函数可以修改静态成员变量,因为要注意线程安全;

3、const常量

3.1 、成员变量是常量

一经初始化便不能修改;

3.2、成员函数形参为常量

传入形参在代码段内不能修改;

3.3、成员函数返回值为常量

3.4、成员函数,后接const修饰

不能修改成员变量值;

例如:

#include <iostream>
class Student {
public:Student(std::string name) :m_name(name){++m_totalStudents;}Student(std::string name, std::string strlocal) : m_name(name),m_strLocal(strlocal){++m_totalStudents;}~Student() {--m_totalStudents;}// 静态成员函数static int getTotalStudents() {// ++m_totalStudents; 错误静态成员函数中不能出现普通成员变量,这里仅能操作静态成员变量;return m_totalStudents;}static std::string getSchoolName(){return m_schoolName;}// 普通成员函数int getStudentsCount(){return getTotalStudents();}std::string getSchoolLocation(){return m_strLocal;}void setSex(const std::string strSex){// 传入的形参不能修改;m_strSex = strSex;}const std::string getSex(){return m_strSex;}// 不能修改所有成员变量void getStudetCount(int &count) const{count = m_totalStudents;}private:const std::string m_strLocal{"上海"};std::string m_name;std::string m_strSex;// 静态成员变量static std::string m_schoolName;static int m_totalStudents;
};std::string Student::m_schoolName = "上海中学";
int Student::m_totalStudents = 0;
int main()
{//constStudent stu1("小王");stu1.setSex("男");Student stu2("小丽");stu2.setSex("女");Student stu3("小张", "北京");stu3.setSex("男");int stuCount = 0;stu2.getStudetCount(stuCount);std::cout << "小王学校在哪儿:" << stu1.getSchoolLocation() << " 小王性别:" << stu1.getSex() << std::endl;std::cout << "小丽学校在哪儿:" << stu2.getSchoolLocation() << " 小丽学校学生数量:" << stuCount <<std::endl;std::cout << "小张学校在哪儿:" << stu3.getSchoolLocation() << std::endl;return 0;
}

4、无法实例化对象的类

4.1、抽象类无法实例化对象(略)

4.2、构造函数为私有的

// 构造函数私有
class baseClass {
private:baseClass(){std::cout << "构造函数" <<std::endl;}private:int m_num;
};

5、无法被继承的类final

class NonInheritable2 final {
public:void print() {std::cout << "This is a final class." << std::endl;}
};

6、delete特殊用法(修饰构造函数)

在 C++ 里,在构造函数后面使用 = delete 是 C++11 引入的特性,其作用是显式地删除该构造函数。删除构造函数后,就无法再使用该构造函数来创建类的对象,这在一些场景下很有用,下面详细介绍:

6.1、禁止默认构造函数

当你不希望类拥有默认构造函数(即无参数的构造函数)时,可使用 = delete 将其删除。

#include <iostream>class MyClass {
public:MyClass(int value) : data(value) {}// 删除默认构造函数MyClass() = delete; 
private:int data;
};int main() {// MyClass obj; // 错误,默认构造函数已被删除MyClass obj(42); // 正确,使用带参数的构造函数return 0;
}

在上述代码中,MyClass的默认构造函数被删除,所以不能通过无参数方式构造对象;

6.2、禁止拷贝构造函数和拷贝赋值运算符

若你不希望类的对象被复制,可使用 = delete 删除拷贝构造函数和拷贝赋值运算符。

#include <iostream>class NonCopyable {
public:NonCopyable() = default;// 删除拷贝构造函数NonCopyable(const NonCopyable&) = delete; // 删除拷贝赋值运算符NonCopyable& operator=(const NonCopyable&) = delete; 
};int main() {NonCopyable obj1;// NonCopyable obj2 = obj1; // 错误,拷贝构造函数已被删除// NonCopyable obj3;// obj3 = obj1; // 错误,拷贝赋值运算符已被删除return 0;
}

此代码中,NonCopyable 类的拷贝构造函数和拷贝赋值运算符都被删除,这就阻止了对象的复制操作。

三、友元

友元就像是类的 “好朋友”,虽然不在类的内部,但却能访问类的私有成员。它打破了类的封装性,但有时候为了提高代码的灵活性和效率,这种 “破格” 也是必要的

比如:

#include <iostream>class A {
public:A(int data) : m_privateData(data) {}protected:double m_protectedData{3.88};private:friend void friendFunction(A& a); // 友元函数friend class ClassB; // 友元类private:int m_privateData;
};void friendFunction(A& a) {std::cout << "访问到私有数据:" << a.m_privateData << std::endl;
}class ClassB {
public:void accessClassAData(A& obj) {// 友元类可以访问 ClassA 的私有和受保护成员std::cout << "Accessing private data of Class A: " << obj.m_privateData << std::endl;std::cout << "Accessing protected data of Class A: " << obj.m_protectedData << std::endl;}
};int main()
{// 友元A a(10);friendFunction(a);ClassB b;b.accessClassAData(a);
}

在上述代码中,ClassB 被声明为 A 的友元类,所以 ClassB 的成员函数 accessClassAData 能够访问 A 的私有成员 privateData 和受保护成员 protectedData。

使用场景

(1)、数据共享:当两个类之间存在紧密的关联,需要共享数据时,可以使用友元类。

注意事项

(1)、打破封装性:友元类破坏了类的封装性,因为它允许外部类访问本类的私有和受保护成员。过度使用友元类会导致代码的安全性和可维护性降低,所以应该谨慎使用。

(2)、单向性:友元关系是单向的。

(3)、不具有传递性。

四、名字空间

名字空间就像是编程世界里的 “房间分隔器”。当我们的项目越来越大,代码中的名字(变量名、函数名、类名等)可能会产生冲突,这时候名字空间就能把不同功能的代码隔离开来。

namespace Math {int add(int a, int b) {return a + b;}
}namespace Utility {int add(int a, int b) {return a * b;}
}int main() {std::cout << "Math 名字空间的加法:" << Math::add(3, 5) << std::endl;std::cout << "Utility 名字空间的加法:" << Utility::add(3, 5) << std::endl;return 0;
}

这里定义了 Math 和 Utility 两个名字空间,它们都有 add 函数,但功能不同,通过名字空间的限定,我们可以准确调用到想要的函数。

五、总结

        C++ 的类与函数蕴含着无尽的奥秘,从内联函数的高效执行,到静态成员的独特共享机制,再到各种特殊类和关键字的巧妙运用,以及友元和名字空间的神奇功能,每一处都值得我们细细品味。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/74669.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lua和C的交互

1.C调用lua例子 #include <iostream> #include <lua.hpp>int main() {//用于创建一个新的lua虚拟机lua_State* L luaL_newstate();luaL_openlibs(L);//打开标准库/*if (luaL_dofile(L, "test.lua") ! LUA_OK) {std::cerr << "Lua error: &…

java高并发------守护线程Daemon Thread

文章目录 1.概念2.生命周期与行为2. 应用场景3. 示例代码4. 注意事项 1.概念 Daemon &#xff1a; 滴门 在Java中&#xff0c;线程分为两类&#xff1a;用户线程(User Thread)和守护线程(Daemon Thread)。 守护线程是后台线程&#xff0c;主要服务于用户线程&#xff0c;当所…

Docker存储策略深度解析:临时文件 vs 持久化存储选型指南

Docker存储策略深度解析&#xff1a;临时文件 vs 持久化存储选型指南 一、存储类型全景对比二、临时存储适用场景与风险2.1 最佳使用案例2.2 风险警示 三、持久化存储技术选型3.1 Volume核心优势Volume管理命令&#xff1a; 3.2 Bind Mount适用边界挂载模式对比&#xff1a; 四…

【Linux网络#18】:深入理解select多路转接:传统I/O复用的基石

&#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;Linux—登神长阶 目录 一、前言&#xff1a;&#x1f525; I/O 多路转接 为什么需要I/O多路转接&#xff1f; 二、I/O 多路转接之 select 1. 初识 select2. select 函数原型2.1 关于 fd_set 结…

高级:微服务架构面试题全攻略

一、引言 在现代软件开发中&#xff0c;微服务架构被广泛应用于构建复杂、可扩展的应用程序。面试官通过相关问题&#xff0c;考察候选人对微服务架构的理解、拆分原则的掌握、服务治理的能力以及API网关的运用等。本文将深入剖析微服务架构相关的面试题&#xff0c;结合实际开…

使用MQTTX软件连接阿里云

使用MQTTX软件连接阿里云 MQTTX软件阿里云配置MQTTX软件设置 MQTTX软件 阿里云配置 ESP8266连接阿里云这篇文章里有详细的创建过程&#xff0c;这里就不再重复了&#xff0c;需要的可以点击了解一下。 MQTTX软件设置 打开软件之后&#xff0c;首先点击添加进行创建。 在阿…

【HFP】蓝牙Hands-Free Profile(HFP)核心技术解析

蓝牙 Hands-Free Profile&#xff08;HFP&#xff09;作为车载通信和蓝牙耳机的核心协议&#xff0c;定义了设备间语音交互的标准化流程&#xff0c;并持续推动着无线语音交互体验的革新。自2002年首次纳入蓝牙核心规范以来&#xff0c;HFP历经多次版本迭代&#xff08;最新为v…

轻量化大模型微调工具XTuner指令微调实战(下篇)

接着上篇文章《轻量化大模型微调工具XTuner指令微调实战&#xff08;上篇&#xff09;》来接着写教程。 一、模型转换 模型训练后会自动保存成 PTH 模型&#xff08;例如 iter_500.pth&#xff09;&#xff0c;我们需要利用 xtuner convert pth_to_hf 将其转换为 HuggingFace…

pyTorch框架使用CNN进行手写数字识别

目录 1.导包 2.torchvision数据处理的方法 3.下载加载手写数字的训练数据集 4.下载加载手写数字的测试数据集 5. 将训练数据与测试数据 转换成dataloader 6.转成迭代器取数据 7.创建模型 8. 把model拷到GPU上面去 9. 定义损失函数 10. 定义优化器 11. 定义训练…

强化学习课程:stanford_cs234 学习笔记(3)introduction to RL

文章目录 前言7 markov 实践7.1 markov 过程再叙7.2 markov 奖励过程 MRP&#xff08;markov reward process&#xff09;7.3 markov 价值函数与贝尔曼方程7.4 markov 决策过程MDP&#xff08;markov decision process&#xff09;的 状态价值函数7.4.1 状态价值函数7.4.2 状态…

操作系统 4.5-文件使用磁盘的实现

通过文件进行磁盘操作入口 // 在fs/read_write.c中 int sys_write(int fd, const char* buf, int count) {struct file *file current->filp[fd];struct m_inode *inode file->inode;if (S_ISREG(inode->i_mode))return file_write(inode, file, buf, count); } 进程…

libreoffice-help-common` 的版本(`24.8.5`)与官方源要求的版本(`24.2.7`)不一致

出现此错误的原因主要是软件包依赖冲突&#xff0c;具体分析如下&#xff1a; ### 主要原因 1. **软件源版本不匹配&#xff08;国内和官方服务器版本有差距&#xff09; 系统中可能启用了第三方软件源&#xff08;如 PPA 或 backports 源&#xff09;&#xff0c;导致 lib…

使用Geotools中的原始方法来操作PostGIS空间数据库

目录 前言 一、原生PostGIS连接介绍 1、连接参数说明 2、创建DataStore 二、工程实战 1、Maven Pom.xml定义 2、空间数据库表 3、读取空间表的数据 三、总结 前言 在当今数字化与信息化飞速发展的时代&#xff0c;空间数据的处理与分析已成为众多领域不可或缺的一环。从…

讯飞语音合成(流式版)语音专业版高质量的分析

一、引言 在现代的 Web 应用开发中&#xff0c;语音合成技术为用户提供了更加便捷和人性化的交互体验。讯飞语音合成&#xff08;流式版&#xff09;以其高效、稳定的性能&#xff0c;成为了众多开发者的首选。本文将详细介绍在 Home.vue 文件中实现讯飞语音合成&#xff08;流…

走进未来的交互世界:下一代HMI设计趋势解析

在科技日新月异的今天&#xff0c;人机交互界面&#xff08;HMI&#xff09;设计正以前所未有的速度发展&#xff0c;不断引领着未来的交互世界。从简单的按钮和图标&#xff0c;到如今的智能助手和虚拟现实&#xff0c;HMI设计不仅改变了我们的生活方式&#xff0c;还深刻影响…

洛谷题单3-P1217 [USACO1.5] 回文质数 Prime Palindromes-python-流程图重构

题目描述 因为 151 151 151 既是一个质数又是一个回文数&#xff08;从左到右和从右到左是看一样的&#xff09;&#xff0c;所以 151 151 151 是回文质数。 写一个程序来找出范围 [ a , b ] ( 5 ≤ a < b ≤ 100 , 000 , 000 ) [a,b] (5 \le a < b \le 100,000,000…

学习笔记,DbContext context 对象是保存了所有用户对象吗

DbContext 并不会将所有用户对象保存在内存中&#xff1a; DbContext 是 Entity Framework Core (EF Core) 的数据库上下文&#xff0c;它是一个数据库访问的抽象层它实际上是与数据库的一个连接会话&#xff0c;而不是数据的内存缓存当您通过 _context.Users 查询数据时&…

本地命令行启动服务并连接MySQL8

启动服务命令 net start mysql8 关闭服务命令 net stop mysql8 本地连接MySQL数据库mysql -u [用户名] -p[密码] 这里&#xff0c;我遇到了个问题 —— 启动、关闭服务时&#xff0c;显示 “发生系统错误 5。拒绝访问。 ” 解法1&#xff1a;在 Windows 上以管理员身份打开…

数据蒸馏:Dataset Distillation by Matching Training Trajectories 论文翻译和理解

一、TL&#xff1b;DR 数据集蒸馏的任务是合成一个较小的数据集&#xff0c;使得在该合成数据集上训练的模型能够达到在完整数据集上训练的模型相同的测试准确率&#xff0c;号称优于coreset的选择方法本文中&#xff0c;对于给定的网络&#xff0c;我们在蒸馏数据上对其进行几…

【spring cloud Netflix】Ribbon组件

1.基本概念 SpringCloud Ribbon是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。简单的说&#xff0c;Ribbon 是 Netflix 发布的开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法&#xff0c;将 Netflix 的中间层服务连接在一 起。Ribbon 的客户端组件提供…