C++ 进阶:类相关特性的深入探讨

⭐在对C++ 中类的6个默认成员函数有了初步了解之后,现在我们进行对类相关特性的深入探讨!

🔥🔥🔥【C++】类的默认成员函数:深入剖析与应用(上)

               【C++】类的默认成员函数:深入剖析与应用(下)


目录

💯前言

💯再谈构造函数

(一)构造函数体赋值

(二)初始化列表

(三)explicit关键字

💯static成员

(一)静态成员变量

(二)静态成员函数

💯友元

(一)友元函数

(二)友元类

💯内部类

(一)定义与访问

(二)内部类的用途

💯总结


💯前言

在 C++ 编程中,类是构建复杂程序的基石。🌟它提供了一种将数据和操作数据的方法进行封装的机制,使得程序更加模块化、可维护和可扩展。在之前对类的学习中,我们已经了解了一些基本概念,如构造函数、拷贝构造函数和析构函数等。然而,类还有许多其他重要的特性,这些特性对于深入理解和掌握 C++ 编程至关重要。🎦本文将进一步探讨构造函数的更多细节,以及 Static 成员、友元、内部类等特性,并再次深入理解封装的概念。

 


💯再谈构造函数

 构造函数在 C++ 类中扮演着至关重要的角色。它主要用于对象的初始化操作。当创建一个类的对象时,构造函数会被自动调用


(一)构造函数体赋值

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。

class Date {
public:Date(int year, int month, int day) {_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};

❓问题如下: 

虽然在构造函数中通过赋值操作给对象的成员变量赋予了初始值,但严格意义上来说这只是在构造函数体中进行的赋初值操作,而不是真正的初始化。

真正的初始化是在对象创建时就确定下来,并且只能进行一次。而在构造函数体内部,可以进行多次赋值操作,这就与初始化的概念有所不同。

 🔵现在我们引出一个概念,来解决这一问题——初始化列表 


(二)初始化列表

 

为了实现真正的初始化,可以使用初始化列表。

初始化列表是在构造函数的参数列表之后,函数体之前,以冒号开头,后面跟着一系列成员变量的初始化表达式。

👇代码如下 :

class Date {
public:Date(int year, int month, int day) : _year(year), _month(month), _day(day) {// 构造函数体,可以进行其他操作,但这里不能再对成员变量进行初始化}
private:int _year;int _month;int _day;
};

 ❗注意:

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    🌠引用成员变量
    🌠const成员变量
    🌠自定义类型成员(且该类没有默认构造函数时)
    #include <iostream>// 自定义类 CustomClass 的定义
    class CustomClass {
    public:// 构造函数,接收一个整数值进行初始化CustomClass(int val) : value(val) {std::cout << "CustomClass constructor called with value: " << value << std::endl;}
    private:int value;
    };// 主类 MyClass 的定义
    class MyClass {
    public:int& ref; // 引用成员变量const int constVal; // const 成员变量CustomClass custom; // 自定义类型成员变量// 构造函数,接收一个引用、一个整数和一个整数作为参数MyClass(int& r, int v, int customVal) : ref(r), constVal(v), custom(customVal) {std::cout << "MyClass constructor called." << std::endl;}
    };int main() {int num = 10;// 创建 MyClass 对象,传入相应参数MyClass obj(num, 20, 30);return 0;
    }

    MyClass包含了引用成员变量、const成员变量和一个自定义类型的成员变量。在构造函数中,必须使用初始化列表来正确初始化这些特殊类型的成员变量。


  3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,✅对于自定义类型成员变量,一定会先使用初始化列表初始化
    #include <iostream>class CustomType {
    public:CustomType(int val) : data(val) {std::cout << "CustomType constructor called with value: " << data << std::endl;}
    private:int data;
    };class MyClass {
    public:MyClass(int customVal) : customMember(customVal) {std::cout << "MyClass constructor called." << std::endl;}
    private:CustomType customMember;
    };int main() {MyClass obj(42);return 0;
    }

    在这个例子中,MyClass有一个自定义类型CustomType的成员变量customMember。当创建MyClass的对象时,即使在MyClass的构造函数中没有显式地写出初始化列表,但实际上编译器会先尝试使用初始化列表来初始化customMember,这就调用了CustomType的构造函数并输出相应信息。如果CustomType没有默认构造函数,那么就必须在MyClass的构造函数中显式地使用初始化列表来正确初始化customMember


  4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

    #include <iostream>class MyClass {
    public:MyClass(int a, int b, int c) : c_member(c), b_member(b), a_member(a) {std::cout << "Constructor called." << std::endl;}void printMembers() {std::cout << "a_member: " << a_member << std::endl;std::cout << "b_member: " << b_member << std::endl;std::cout << "c_member: " << c_member << std::endl;}private:int a_member;int b_member;int c_member;
    };int main() {MyClass obj(1, 2, 3);obj.printMembers();return 0;
    }


    在这个例子中,构造函数的初始化列表中成员变量的初始化顺序看起来是 c_memberb_membera_member💐但实际上成员变量的初始化顺序是由它们在类中的声明顺序决定的即先初始化 a_member,再初始化 b_member,最后初始化 c_member如果在初始化列表中打乱顺序,初始化的结果仍然是按照声明顺序进行的。


(三)explicit关键字

构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用。 

接收单个参数的构造函数具体表现🌞: 

  1. 构造函数只有一个参数
  2. 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
  3. 全缺省构造函数 
#include <iostream>class Date
{
public:// 1. 单参构造函数,没有使用 explicit 修饰,具有类型转换作用// explicit 修饰构造函数,禁止类型转换---explicit 去掉之后,代码可以通过编译explicit Date(int year): _year(year){}/*// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用 explicit 修饰,具有类型转换作用// explicit 修饰构造函数,禁止类型转换explicit Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}*/Date& operator=(const Date& d){if (this!= &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}private:int _year;int _month;int _day;
};void Test()
{Date d1(2022);// 用一个整形变量给日期类型对象赋值// 实际编译器背后会用 2023 构造一个无名对象,最后用无名对象给 d1 对象进行赋值// 将 1 屏蔽掉,2 放开时则编译失败,因为 explicit 修饰构造函数,禁止了单参构造函数类型转换的作用
}

 上述代码可读性不是很好,用explicit修饰构造函数,将会禁止构造函数的隐式转换。

 有一个接受单个整数参数的构造函数Date(int year),用于初始化日期对象的年份部分。这个构造函数使用了explicit关键字修饰,这意味着它不能进行隐式类型转换。如果没有这个关键字,编译器可能会在某些情况下自动使用这个构造函数进行隐式的类型转换。

 


💯static成员

 

(一)静态成员变量

😃静态成员变量是属于类的变量,而不是属于某个具体的对象。它在整个类的所有对象之间共享。

  1.定义与初始化

  • 静态成员变量需要在类内声明,但不能在类内初始化(除了一些特殊的静态常量整数类型可以在类内初始化)。例如:
    class MyClass {
    public:static int staticVar;
    };
    int MyClass::staticVar = 0; // 在类外初始化

  2.访问方式

  • 可以通过类名直接访问静态成员变量,也可以通过对象访问,但通常建议通过类名访问,以体现其类属性。例如:
    MyClass obj;
    MyClass::staticVar = 10; // 通过类名访问
    obj.staticVar = 20; // 通过对象访问也是允许的,但不规范

(二)静态成员函数

🍎静态成员函数也是属于类的函数,它不依赖于具体的对象实例。

1.特点

  • 它只能访问静态成员变量和其他静态成员函数,因为它没有this指针指向具体的对象。例如:
    class MyClass {
    public:static int staticVar;static void staticFunction() {staticVar++; // 可以访问静态成员变量// 不能访问非静态成员变量,因为没有this指针}
    };

2.调用方式

  • 与静态成员变量类似,可以通过类名直接调用静态成员函数。例如:MyClass::staticFunction();

 


💯友元

友元机制允许一个类或函数访问另一个类的私有成员。它打破了类的封装性,但在某些特定情况下是非常有用的。

(一)友元函数

1.定义

  • 友元函数是在一个类中声明为友元的普通函数。它不是类的成员函数,但可以访问该类的私有成员。例如:
    class MyClass {
    private:int privateVar;
    public:friend void friendFunction(MyClass obj);
    };
    void friendFunction(MyClass obj) {// 可以访问obj的privateVarstd::cout << obj.privateVar << std::endl;
    }

❗说明:

  1. 友元函数可访问类的私有和保护成员,但不是类的成员函数
  2. 友元函数不能用const修饰
  3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  4. 一个函数可以是多个类的友元函数
  5. 友元函数的调用与普通函数的调用原理相同

(二)友元类

1.定义

  • 友元类是在一个类中声明为友元的另一个类。友元类的所有成员函数都可以访问声明它为友元的类的私有成员。例如:
    class MyClass {
    private:int privateVar;
    public:friend class FriendClass;
    };
    class FriendClass {
    public:void accessPrivate(MyClass obj) {// 可以访问obj的privateVarstd::cout << obj.privateVar << std::endl;}
    };

2.使用场景

  • 当两个类之间存在紧密的合作关系,需要相互访问对方的私有成员时,可以使用友元类。例如,一个图形绘制类和一个图形变换类可能需要相互访问对方的私有数据来实现复杂的图形操作。

💯内部类

🍄内部类是定义在另一个类内部的类。它具有一些特殊的性质和用途。

(一)定义与访问

1.定义

  • 内部类可以在一个外部类的任何部分定义,包括私有部分、保护部分和公共部分。例如:
    class OuterClass {
    private:int outerVar;class InnerClass {public:void innerFunction() {// 可以访问OuterClass的成员吗?这取决于具体情况}};
    };

2.访问

  • 外部类可以通过创建内部类的对象来访问内部类的成员。内部类也可以访问外部类的成员,但需要注意访问权限。如果内部类定义在外部类的公共部分,那么它可以像普通类一样被外部访问和使用。如果定义在私有部分,只有外部类的成员函数可以创建内部类的对象并访问其的成员。例如:
    OuterClass outerObj;
    OuterClass::InnerClass innerObj; // 创建内部类对象(如果InnerClass是公共的)
    outerObj.innerObj.innerFunction(); // 通过外部区
    outerObj.innerObj.innerFunction(); // 通过外部类对象访问内部类对象的成员(如果InnerClass是公共的且有合适的访问路径)

(二)内部类的用途

1.隐藏实现细节

  • 内部类可以将一些与外部类相关但又不想暴露给外部的实现细节封装起来。例如,一个复杂的容器类可能使用内部类来实现其内部的数据结构,如链表节点类可以作为容器类的内部类。

2.实现辅助功能

  • 可以利用内部类来实现一些辅助功能,这些功能与外部类紧密相关但又不适合作为外部类的直接成员函数。例如,一个文件读取类可能有一个内部类用于处理文件的缓冲和读取位置等细节。

💯总结

C++ 中类的这些特性为我们提供了强大的编程工具,让我们能够更好地组织和管理代码。希望本文能够帮助你更深入地理解 C++ 类的相关特性,提升你的编程能力。🚩🚩🚩


以后我将深入研究继承、多态、模板等特性,并将默认成员函数与这些特性结合,以解决更复杂编程问题!欢迎关注我👉【A Charmer】

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

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

相关文章

python实战项目46:selenium爬取百度新闻

python实战项目46:selenium爬取百度新闻 一、项目简介二、完整代码一、项目简介 思路是首先使用selenium打开百度新闻页面,然后实现翻页操作,获取每条新闻的标题和链接。接下来的问题是,在遍历标题和链接,对每一个链接发送请求时,发现会弹出百度安全验证,本文的思路是使…

浪潮云启操作系统(InLinux)bcache缓存实践:理解OpenStack环境下虚拟机卷、Ceph OSD、bcache设备之间的映射关系

前言 在OpenStack平台上&#xff0c;采用bcache加速ceph分布式存储的方案被广泛用于企业和云环境。一方面&#xff0c;Ceph作为分布式存储系统&#xff0c;与虚拟机存储卷紧密结合&#xff0c;可以提供高可用和高性能的存储服务。另一方面&#xff0c;bcache作为混合存储方案&…

新版idea菜单栏展开与合并

新版idea把菜单栏合并了看着很是不习惯&#xff0c;找了半天原来在这里展开 ① 点击文件 -> 设置 ② 点击外观与行为 -> 外观 -> 合并主菜单和窗口标题 然后确定&#xff0c;重启即可

HTML作业

作业 复现下面的图片 复现结果 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><form action"#"method"get"enctype"text/plain"><…

演示:基于WPF的DrawingVisual开发的高刷新率示波器

一、目的&#xff1a;分享一个基于WPF的DrawingVisual开发的高刷新率示波器 二、效果演示 特此说明&#xff1a;由于Gif录制工具帧率不够&#xff0c;渲染60帧用了4.6秒&#xff0c;平均帧率在12Hz左右&#xff0c;所以展示效果不好&#xff0c;想要看好些的效果可以看文章下面…

【目标检测2024】DetCLIP

算法介绍 CLIP&#xff08;Contrastive Language-Image Pre-Training&#xff09;模型是一种多模态预训练神经网络&#xff0c;由OpenAI在2021年发布&#xff0c;是从自然语言监督中学习的一种有效且可扩展的方法。CLIP在预训练期间学习执行广泛的任务&#xff0c;包括OCR&…

DORA 机器人中间件学习教程(6)——激光点云预处理

文章目录 1 移植思路2 代码输入输出说明3 编写CmakeList.txt文件4 编写yml文件5 编译并启动节点参考资料 在DORA中通过驱动获取激光雷达数据后&#xff0c;激光点云预处理部分代码是参考了autoware官方代码并对其进行裁剪得到的&#xff0c;点云预处理主要包含三个节点&#xf…

32.第二阶段x86游戏实战2-遍历技能2(技能二叉树基址)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

[数据集][目标检测]电力场景输电线路巡检检测数据集VOC+YOLO格式8667张50类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;8667 标注数量(xml文件个数)&#xff1a;8667 标注数量(txt文件个数)&#xff1a;8667 标注…

双碳目标下储能产业新趋势与架构

0.引言 储能技术涉及能量的存储和利用&#xff0c;对电力系统平衡至关重要。它允许电力在需求时被储存和释放&#xff0c;对电力生产和消费方式产生重大影响。随着全球应对气候变化&#xff0c;风能和太阳能成为主要能源&#xff0c;但其不稳定性需要储能技术来提高可靠性。储…

在做题中学习(65):Z字形变换

6. Z 字形变换 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a;模拟 思路&#xff1a;把原字符串从上到下依次读取到新字符串中&#xff0c;就需要看看Z字形变换时字符变化的规律。 以行数h4时为例&#xff1a; 对于第一行和最后一行&#xff1a; 每一个字符的下标…

Java笔试06

在Java中&#xff0c;异常可以分为两大类&#xff1a;编译时异常&#xff08;编译时检查异常&#xff09;和运行时异常&#xff08;非编译时检查异常&#xff09;。 编译时异常&#xff08;Checked Exceptions&#xff09;是指在编译时期必须被捕获或声明抛出的异常。这些异常…

基于springboot家乡特色推荐系统

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 系统展示 【2024最新】基于JavaSpringBootVueMySQL的&#xff0c;前后端分离。 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;…

Q宠大乐斗批量好友添加器(基于python实现)

效果如下: 只要有自动化测试的浏览器和插件就能批量添加等级相近的陌生人为好友,过程迅速,分两个py文件 第一个是主程序: import tkinter as tk import re from tkinter import scrolledtext, font, ttk, messagebox, filedialog from selenium import webdriver from se…

10_实现readonly

在某些时候&#xff0c;我们希望定义一些数据是只读的&#xff0c;不允许被修改&#xff0c;从而实现对数据的保护&#xff0c;即为 readonly 只读本质上也是对数据对象的代理&#xff0c;我们同样可以基于之前实现的 createReactiveObject 函数来实现&#xff0c;可以为此函数…

Unable to open nested entry ‘********.jar‘ 问题解决

今天把现网版本的task的jar拖回来然后用7-zip打开拖了一个jar进去替换mysql-connector-java-5.1.47.jar 为 mysql-connector-java-5.1.27.jar 启动微服务的时候就报错下面的 Exception in thread "main" java.lang.IllegalStateException: Failed to get nested ar…

OS管理和进程的学习

1.冯诺依曼体系结构 1.1 输入设备&#xff1a;键盘&#xff0c;鼠标&#xff0c;键盘&#xff0c;网卡&#xff08;网络接受&#xff09;&#xff0c;磁盘... 输出设备&#xff1a;显示器&#xff0c;磁盘&#xff0c;网卡&#xff08;网络发送&#xff09; .... 存储器&…

CTFHUB技能树之SQL——字符型注入

开启靶场&#xff0c;打开链接&#xff1a; 直接指明是SQL字符型注入&#xff0c;但还是来判断一下 &#xff08;1&#xff09;检查是否存在注入点 1 and 11# 返回正确 1 and 12# 返回错误 说明存在SQL字符型注入 &#xff08;2&#xff09;猜字段数 1 order by 2# 1 order…

Shell重定向输入输出

我的后端学习大纲 我的Linux学习大纲 重定向介绍 标准输入介绍 从键盘读取用户输入的数据&#xff0c;然后再把数据拿到Shell程序中使用&#xff1b; 标准输出介绍 Shell程序产生的数据&#xff0c;这些数据一般都是呈现到显示器上供用户浏览查看; 默认输入输出文件 每个…

QT的文件操作类 QFile

QFile 是 Qt 框架中用于文件处理的一个类。它提供了读取和写入文件的功能&#xff0c;支持文本和二进制文 件。 QFile 继承自 QIODevice &#xff0c;因此它可以像其他IO设备一样使用。 主要功能 文件读写&#xff1a; QFile 支持打开文件进行读取或写入操作文件信息&#x…