剖析【C++】——类和对象(下篇)——超详解——小白篇

目录

1.再谈构造函数

1.1 构造函数体赋值

1.2 初始化列表

1.3 explicit 关键字

2. Static成员

2.1 概念

2.2 特性

3. 友元

3.1 友元函数

3.2 友元类

3.3总结:

4. 内部类

1.概念

2.特性

示例代码:

代码分析

3.总结

5.再次理解类和对象

1. 抽象现实生活中的实体

2. 用类描述实体

3. 实例化对象

4. 总结类和对象的关系


1.再谈构造函数

1.1 构造函数体赋值

构造函数是用来初始化对象的特殊函数。当我们创建一个对象时,编译器会自动调用构造函数来给对象的成员变量赋值。

简单理解:当你买一个新手机,打开包装,这就像调用了构造函数。手机里的默认应用程序就像成员变量的初始值。这些默认应用虽然是预装的,但我们可以再安装新的应用,这相当于构造函数体内的赋值操作。

示例代码:

class MyClass {
public:int x;int y;MyClass(int a, int b) : x(a), y(b) {}  // 初始化列表
};

在上面的代码中,当我们创建一个 MyClass 对象时,比如 MyClass obj(10);,构造函数 MyClass(int value) 会被调用,x 会被赋值为 10。但是这个过程叫赋值,不叫初始化,因为我们可以在构造函数内多次赋值。

总结:构造函数体内的赋值可以多次进行,而初始化只能进行一次。

1.2 初始化列表

初始化列表 是另一种在创建对象时给成员变量赋初值的方法。这种方法更高效,因为它在对象创建时就直接赋值,而不是先创建对象然后再赋值。

简单理解:假设你买了一辆新车,初始化列表就像你在购车前已经决定好车的颜色、型号等,而不是买车后再去喷漆改装。

示例代码:

class MyClass {
public:int x;int y;MyClass(int a, int b) : x(a), y(b) {}  // 初始化列表
};

在上面的代码中,: x(a), y(b) 就是初始化列表,它在构造函数体执行之前完成成员变量的初始化。

注意事项

  1. 每个成员变量在初始化列表中只能出现一次。
  2. 以下成员必须在初始化列表中初始化:
    • 引用类型成员变量
    • const 成员变量
    • 自定义类型成员变量(如果该类没有默认构造函数)

示例代码:

class MyClass {
public:const int x;int &y;MyClass(int a, int &b) : x(a), y(b) {}  // 必须在初始化列表中初始化
};

在这段代码中,const int xint &y 必须在初始化列表中进行初始化,因为 const 成员变量和引用类型成员变量在对象创建时就需要确定其初始值。

总结:尽量使用初始化列表,因为它对自定义类型成员变量更高效。

1.3 explicit 关键字

构造函数不仅可以用来创建对象,还可以用来进行类型转换。对于只有一个参数的构造函数,如果不使用 explicit 关键字,编译器会自动进行隐式类型转换。

简单理解:假设有一个银行系统,你有一个账户类。如果允许隐式转换,就像允许陌生人随意把你的银行账号转换成他们的账户,这很危险。所以我们需要 explicit 关键字来禁止这种转换。

示例代码:

class MyClass {
public:int x;explicit MyClass(int value) : x(value) {}  // 使用 explicit 关键字
};void func(MyClass obj) {// do something
}int main() {MyClass obj1(10);  // 正常调用构造函数// MyClass obj2 = 10;  // 错误,explicit 禁止隐式转换// func(20);  // 错误,explicit 禁止隐式转换return 0;
}

在上面的代码中,explicit 关键字禁止了构造函数的隐式转换,这样可以避免潜在的错误和提高代码的可读性。

总结:使用 explicit 关键字可以防止构造函数被用于隐式转换,确保代码的安全性和可读性。

2. Static成员

2.1 概念

在C++中,声明为static的类成员称为类的静态成员。静态成员分为静态成员变量和静态成员函数。

  • 静态成员变量:用static修饰的成员变量。
  • 静态成员函数:用static修饰的成员函数。

简单理解:静态成员就像学校里的公共设施,比如学校的大门(静态成员变量)和学校的公告栏(静态成员函数),它们是所有学生(类的对象)共享的,而不是某个学生独有的。

初始化:静态成员变量必须在类外进行初始化。

面试题:实现一个类,计算程序中创建了多少个类对象。

示例代码

#include <iostream>
using namespace std;class MyClass {
public:static int count;  // 静态成员变量声明MyClass() {count++;  // 每创建一个对象,count加1}// 静态成员函数static int getCount() {return count;}
};// 静态成员变量在类外定义并初始化
int MyClass::count = 0;int main() {MyClass obj1;MyClass obj2;MyClass obj3;cout << "Created objects: " << MyClass::getCount() << endl;return 0;
}

在这段代码中,MyClass类的静态成员变量count记录了创建的对象数量,每创建一个对象,count就加1。静态成员函数getCount返回当前的对象数量。

2.2 特性

  1. 静态成员为所有类对象共享:静态成员变量存放在静态区,不属于某个具体的对象。
  2. 静态成员变量必须在类外定义:定义时不添加static关键字,类中只是声明。
  3. 访问静态成员:静态成员可以通过类名::静态成员对象.静态成员访问。
  4. 静态成员函数没有this指针:不能访问任何非静态成员。
  5. 静态成员受访问限定符限制:静态成员同样受publicprotectedprivate访问限定符的限制。

示例代码

class MyClass {
public:static int staticVar;  // 静态成员变量声明int nonStaticVar;      // 非静态成员变量static void staticFunc() {// 静态成员函数不能访问非静态成员// cout << nonStaticVar;  // 错误cout << staticVar;  // 正确}void nonStaticFunc() {cout << staticVar;  // 非静态成员函数可以访问静态成员cout << nonStaticVar;  // 非静态成员函数可以访问非静态成员}
};// 静态成员变量定义
int MyClass::staticVar = 0;

问题解答

1.静态成员函数可以调用非静态成员函数吗? 不可以。静态成员函数没有this指针,所以不能访问类的非静态成员(包括非静态成员函数)。

示例代码

class MyClass {
public:static void staticFunc() {// nonStaticFunc();  // 错误,静态成员函数不能调用非静态成员函数}void nonStaticFunc() {cout << "Non-static member function called." << endl;}
};

2.非静态成员函数可以调用类的静态成员函数吗? 可以。非静态成员函数可以访问类的所有成员,包括静态成员。

示例代码

class MyClass {
public:static void staticFunc() {cout << "Static member function called." << endl;}void nonStaticFunc() {staticFunc();  // 非静态成员函数可以调用静态成员函数}
};

3. 友元

友元提供了一种特殊的机制,可以让一个类允许另一个类或函数访问其私有或受保护的成员。虽然友元增加了便利性,但也会破坏封装性,增加代码耦合度,因此使用时需谨慎。

3.1 友元函数

问题描述:在重载 operator<< 时,无法将其重载为成员函数,因为 cout 作为输出流对象和 this 指针会竞争第一个参数的位置。为了使 cout 成为第一个参数,我们需要将 operator<< 重载为全局函数。但全局函数不能直接访问类的私有成员,这时就需要友元函数来解决这个问题。

友元函数的特点

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

示例代码

#include <iostream>
using namespace std;class MyClass {
private:int value;public:MyClass(int v) : value(v) {}// 声明友元函数friend ostream& operator<<(ostream& os, const MyClass& obj);
};// 定义友元函数
ostream& operator<<(ostream& os, const MyClass& obj) {os << obj.value;return os;
}int main() {MyClass obj(42);cout << obj << endl;  // 输出: 42return 0;
}

在上面的代码中,operator<< 被定义为友元函数,因此它可以访问 MyClass 类的私有成员 value

3.2 友元类

友元类是一种类的所有成员函数都可以访问另一个类的私有和保护成员的机制。

特点

  1. 友元关系是单向的,不具有交换性。例如,如果 A 类是 B 类的友元,那么 B 类可以访问 A 类的私有成员,但反过来 A 类不能访问 B 类的私有成员。
  2. 友元关系不能传递。如果 BA 的友元,而 CB 的友元,这并不意味着 CA 的友元。
  3. 友元关系不能继承。

示例代码

#include <iostream>
using namespace std;class Date;class Time {
private:int hour;int minute;public:Time(int h, int m) : hour(h), minute(m) {}// 声明Date类为友元类friend class Date;
};class Date {
private:int day;int month;int year;public:Date(int d, int m, int y) : day(d), month(m), year(y) {}void displayTime(const Time& t) {// 访问Time类的私有成员cout << "Time: " << t.hour << ":" << t.minute << endl;}
};int main() {Time t(10, 30);Date d(1, 5, 2023);d.displayTime(t);  // 输出: Time: 10:30return 0;
}

在上面的代码中,Date 类被声明为 Time 类的友元类,因此 Date 类的成员函数可以访问 Time 类的私有成员。

3.3总结

  • 友元函数和友元类允许访问私有和保护成员,但要谨慎使用,因为这会增加代码的耦合性。
  • 友元关系是单向的,不可传递。
  • 使用友元可以解决一些特殊情况下的访问权限问题,如重载运算符等。

4. 内部类

1.概念

内部类 是指定义在另一个类内部的类。它是一个独立的类,不属于外部类,不能通过外部类的对象访问其成员。

简单理解:就像一家大公司的部门(内部类)和公司(外部类),部门是独立的,但仍然是公司的一部分,外部类对内部类没有特别的访问权限。

注意事项

  • 内部类就像是外部类的友元类,内部类可以通过外部类的对象参数访问外部类的所有成员。
  • 外部类对内部类没有友元访问权限,不能访问内部类的私有成员。

2.特性

  1. 内部类的位置:内部类可以定义在外部类的 publicprotectedprivate 区域。
  2. 访问外部类的静态成员:内部类可以直接访问外部类的静态成员,而不需要外部类的对象或类名。
  3. 大小计算sizeof(外部类) 只计算外部类的大小,与内部类无关。

示例代码:

#include <iostream>
using namespace std;class OuterClass {
private:int outerVar;static int outerStaticVar;public:OuterClass(int val) : outerVar(val) {}// 定义内部类class InnerClass {public:void displayOuter(OuterClass& outer) {// 访问外部类的非静态成员需要外部类的对象cout << "Outer class non-static member: " << outer.outerVar << endl;}void displayOuterStatic() {// 直接访问外部类的静态成员cout << "Outer class static member: " << outerStaticVar << endl;}};// 外部类中的成员函数可以创建内部类的对象void createInner() {InnerClass inner;inner.displayOuter(*this);inner.displayOuterStatic();}
};// 定义并初始化外部类的静态成员
int OuterClass::outerStaticVar = 10;int main() {OuterClass outer(5);outer.createInner();// 外部类对象不能直接访问内部类成员// outer.InnerClass inner;  // 错误// 创建内部类对象OuterClass::InnerClass inner;inner.displayOuter(outer);inner.displayOuterStatic();return 0;
}

代码分析

  1. 内部类的位置InnerClass 定义在 OuterClasspublic 区域内,可以访问外部类的静态和非静态成员。
  2. 访问外部类的静态成员InnerClass 中的 displayOuterStatic 函数直接访问 OuterClass 的静态成员 outerStaticVar
  3. 访问外部类的非静态成员displayOuter 函数通过 OuterClass 的对象参数 outer 访问其非静态成员 outerVar

3.总结

  • 内部类 是独立的类,可以定义在外部类的任何访问区域。
  • 内部类可以直接访问外部类的静态成员,不需要外部类的对象或类名。
  • 内部类可以通过外部类的对象参数访问外部类的非静态成员。
  • 外部类不能访问内部类的私有成员,内部类也不会影响外部类的大小计算。

5.再次理解类和对象

1. 抽象现实生活中的实体

在现实生活中,计算机无法直接认识物理世界中的实体,如洗衣机。为了让计算机理解这些实体,我们需要通过面向对象的语言(如C++)对它们进行抽象和描述。

简单理解:假设你想让计算机认识洗衣机。首先,你需要在头脑中抽象出洗衣机的属性和功能。比如,洗衣机有颜色、品牌、容量等属性,还有启动、停止、洗涤等功能。

2. 用类描述实体

一旦你在人为思想层面对洗衣机有了清晰的认识,就需要用某种编程语言(如C++)将这种认识转化为计算机能理解的格式。我们使用“类”来描述洗衣机。

示例代码

class WashingMachine {
public:// 属性string color;string brand;int capacity;// 方法void start() {cout << "Washing machine started." << endl;}void stop() {cout << "Washing machine stopped." << endl;}void wash() {cout << "Washing machine is washing clothes." << endl;}
};

在上面的代码中,我们定义了一个类 WashingMachine,它包含了洗衣机的属性(颜色、品牌、容量)和方法(启动、停止、洗涤)。

3. 实例化对象

定义了类之后,计算机还不能理解洗衣机是什么。我们需要通过类来实例化具体的洗衣机对象。

示例代码

int main() {// 实例化一个洗衣机对象WashingMachine myWasher;// 给属性赋值myWasher.color = "White";myWasher.brand = "LG";myWasher.capacity = 7;// 调用方法myWasher.start();myWasher.wash();myWasher.stop();return 0;
}

在这段代码中,我们创建了一个 WashingMachine 对象 myWasher,并为其属性赋值,然后调用其方法来模拟洗衣机的行为。

4. 总结类和对象的关系

是对某一类实体的抽象和描述。类定义了这些实体具有的属性和方法,形成了一种新的自定义类型。

对象 是类的实例,是具体的实体。通过实例化类,我们创建对象,然后可以使用这些对象来模拟现实中的实体。

现实生活中的模拟

  • 抽象:你在人为思想层面对洗衣机进行认识,确定它的属性和功能。
  • :用C++类来描述洗衣机的属性和功能,将这种描述输入计算机中。
  • 实例化:通过类实例化具体的洗衣机对象,计算机才真正理解和模拟洗衣机的行为。

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

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

相关文章

MySQL各种锁

目录 1. 从粒度上区分锁 1.1 全局锁&#xff08;第一粒度&#xff09; 1.2 表级锁&#xff08;第二粒度&#xff09; 1.3 行锁&#xff08;第三最小粒度&#xff09; 2 从模式上区分锁 2.1 什么是乐观锁 2.2 什么是悲观锁 2.3 意向共享锁和意向排他锁 2.4 临键锁和记录…

autocad背景色、引线文字大小

一、改变背景 在命令行输入op&#xff0c;回车&#xff0c;弹出配置对话框&#xff1a; 二、改变引线文字大小 选中引线&#xff0c;右键选择【特性】&#xff0c;在文字选项卡中设置文字高度&#xff1a;

Linux学习笔记:日志文件的编写

日志文件Log.hpp 日志文件的作用简单的日志文件编写 日志文件的作用 日志文件可以很好的帮我们显示出程序运行的信息,例如,进程pid,运行时间,运行状况等,通过日志记录程序的执行路径、变量值、函数调用等&#xff0c;可以帮助我们快速定位和修复代码中的错误。 简单的日志文件…

磁盘配额的具体操作

磁盘配额&#xff1a; linux的磁盘空间有两个方面&#xff1a;第一个是物理空间&#xff0c;也就是磁盘的容量 第二个inode号耗尽&#xff0c;也无法写入 linux根分区&#xff1a;根分区的空间完全耗尽&#xff0c;服务程序崩溃&#xff0c;系统也无法启动了。 为了防止有人…

JavaWeb 请求响应路径调试

在使用mvc时&#xff0c;或许会遇到请求的页面响应不了&#xff0c;这种情况要对站下径。 站点根目录 启动服务器时&#xff0c;通常要知道哪个是站点根目录。相应在网页端的url的跟站点通常为http://localhost:8080/ &#xff0c;前端解析时用的是站点根目录。 <form act…

【嵌入式DIY实例】-OLED显示网络时钟

OLED显示网络时钟 文章目录 OLED显示网络时钟1、硬件准备与接线2、代码实现在上一个ESP8266 NodeMCU文章中,我们用DS3231 RTC芯片和SSD1306 OLED制作了一个简单的实时时钟,时间和日期显示在SSD1306屏幕上,并且可以通过两个按钮进行设置。 在本中,我们将使用ESP 8266 NodeMC…

【Gradle】Gradle的本地安装和使用

目录 1、Gradle 的安装 2、集成 IntelliJ IDEA 3、使用 Gradle Gradle 完全兼容 Maven 和 Ivy 仓库&#xff0c;你可以从中检索依赖也可以发布你的文件到仓库中&#xff0c;Gradle 提供转换器能把 Maven 的构建逻辑转换成 Gradle 的构建脚本。 1、Gradle 的安装 Gradle 的…

Python 将Word、Excel、PDF、PPT文档转为OFD文档

OFD&#xff08;Open Fixed-layout Document &#xff09;是我国自主制定的一种开放版式文件格式标准。OFD文档具有不易被篡改、格式独立、版式固定等特点&#xff0c;目前常用于政府公文、金融、电子发票等领域。 如果想要通过Python将Office文档&#xff08;如Word、Excel或…

Android 车载 Audio 中 有关系统按键无声的问题排查小结

本文简单记录一下&#xff0c;车载中系统按键音的问题排查从 App --> FrameWork --> HAL层 的问题排查。 通过日志分析&#xff1a; AudioStreamOutSink 这个有数据写入到 HAL 中&#xff08;方式一&#xff09; 查看 dump 文件。&#xff08;方式二&#xff09; 先 …

SAP 物料的与压缩库存数据不一致

在测试系统中进行501和561增加库存的时候,系统提示物料的预压缩库存数据不一致的报错。 如下图所示: 首先想到的就是搜SAP的NOTES,找到了相关的两个note:293356 note:2197042 表示:数据库表 ACDOCA 和 ACDOCA_M_EXTRACT 之间的差异。 经过查询发现是由于物料分类账中的…

Docker 图形化界面管理工具 Portainer | 让你更轻松的管理 Docker

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 Portainer 是一个 Docker 图形化管理工具&#xff0c;可以通过 Web UI 轻松的管理容器、镜像、网络、卷。与 Dockge 相比功能更加的完善&#xff0c;同时上手难度也更大一些 Portainer 分为社区版和商业版…

你是否正确地编写了 Git 提交信息?

介绍 在版本控制方面&#xff0c;Git 是一个非常有效的工具。然而&#xff0c;像任何其他工具一样&#xff0c;你必须正确使用它才能充分发挥其作用。你需要考虑不同的方面。本文着重介绍如何按照传统提交规范&#xff08;Conventional Commits specification&#xff09;编写…

线上商城API接口再次升级||电商API接口对接线上商城搭建

功能更新 商城对接【蚂蚁搬客】应用 API接口产品上传及订单状态修改 01 商城对接API应用 ▼ 使用场景 适用于多个电商平台&#xff08;如淘宝、京东、天猫、1688、苏宁、唯品会、当当等&#xff09;的产品搬家&#xff0c;包括产品标题、主图等信息&#xff0c;轻松完成商…

NFTScan 获 Google Cloud 战略支持!

近日&#xff0c;NFT 数据基础设施服务商 NFTScan 获得全球领先云计算服务提供商 Google Cloud 战略支持。未来&#xff0c;双方将在链上数据和区块链领域展开战略合作&#xff0c;高效联动&#xff0c;共同探索区块链技术的更多可能性&#xff0c;为用户和行业带来更多惊喜与成…

束测后台实操文档2-OpenWrt

束测后台实操文档1-PVE、PBS 上面文&#xff0c;把proxmox装好并添加好PBS上的镜像存储空间后&#xff0c;还原已经做好的镜像基本上就可以在已有的镜像下开展工作了。 调试的PVE环境一般两个网口&#xff0c;一个外网wan&#xff0c;一个子网lan&#xff0c;虚拟机一般在lan…

【UnityShader入门精要学习笔记】第十六章 Unity中的渲染优化技术 (上)

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 我的GitHub仓库 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 移动平台上…

【Python】解决Python报错:AttributeError: ‘module‘ object has no attribute ‘xxx‘

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

flink 操作mongodb的例子

简述 该例子为从一个Collection获取数据然后插入到另外一个Collection中。 Flink的基本处理过程可以清晰地分为以下几个阶段&#xff1a; 数据源&#xff08;Source&#xff09;&#xff1a; Flink可以从多种数据源中读取数据&#xff0c;如Kafka、RabbitMQ、HDFS等。Flink会…

21 厂商考证介绍(华为 华三 锐键 深信服)+AI 解析

一 认识考证体系 二 明确考证的大致方向 锐键 职业资格证书等级介绍 职业资格证书是由国家职业资格鉴定机构或相关行业主管部门颁发的&#xff0c;用于证明一个人在特定职业领域具备一定技能和知识水平的证明文件。职业资格证书的等级分为初级、中级、高级、技师、高级技师、…

【Python】解决Python报错:AttributeError: ‘NoneType‘ object has no attribute ‘xxx‘

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…