剖析【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.rhkb.cn/news/339636.html

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

相关文章

【Java面试】七、SpringMvc的执行流程、SpringBoot自动装配原理

文章目录 1、SpringMVC的执行流程1.1 视图阶段1.2 前后端分离阶段 2、SpringBoot自动配置原理3、框架常用的注解3.1 Spring的注解3.2 SpringMvc的注解3.3 SpringBoot的注解 4、面试 1、SpringMVC的执行流程 1.1 视图阶段 旧项目中&#xff0c;未前后端分离时&#xff0c;用到…

《mysql轻松学习·二》

1、创建数据表 contacts&#xff1a;数据表名 auto_increament&#xff1a;自动增长 primary key&#xff1a;主键 engineInnoDB default charsetutf8; 默认字符集utf8&#xff0c;不写就默认utf8 对数据表的操作&#xff1a; alter table 数据表名 add sex varchar(1); //添…

【C语言】字符串左旋(三种方法)

&#xff08;方法3只给出思路参考&#xff09; 问题 描述&#xff1a; 实现一个函数&#xff0c;可以左旋字符串中的k个字符。 例如&#xff1a; ABCD左旋一个字符得到BCDA ABCD左旋两个字符得到CDAB 分析 我们先来理解一下&#xff0c;什么叫“左旋”&#xff1f;其实是这…

d2-crud-plus 使用小技巧(六)—— 表单下拉选择 行样式 溢出时显示异常优化

问题 vue2 elementUI d2-crud-plus&#xff0c;数据类型为select时&#xff0c;行样式显示为tag样式&#xff0c;但是如果选择内容过长就会出现下面这种bug&#xff0c;显然用户体验不够友好。 期望 代码 js export const crudOptions (vm) > {return {...columns:…

QT 如何在 QListWidget 的选项中插入自定义组件

有时我们需要 QListWidget 完成更复杂的操作&#xff0c;而不仅限于添加文本或者图标&#xff0c;那么就会使用到 setItemWidget 函数&#xff0c;但是这也会伴生一个问题&#xff0c;插入自定义组件后&#xff0c;QListWidget 对选项点击事件的获取会收到阻塞&#xff0c;因…

QT 使用信号和槽,让QLabel的内容实时与QLineEdit同步,类似vue框架的双向绑定

在窗口里放置一个单行文本编辑器&#xff08;QLineEdit&#xff09;和一个标签控件&#xff08;QLabel&#xff09;&#xff0c;实现的效果就是当编辑器的内容被编辑时&#xff0c;标 签控件同步显 示编辑控件里的内容 1&#xff09;当 lineEdit 控件被用户编辑时&#xff0c;它…

重生之 SpringBoot3 入门保姆级学习(17、整合SSM)

重生之 SpringBoot3 入门保姆级学习&#xff08;17、整合SSM&#xff09; 4、数据访问4.1 整合 ssm 4、数据访问 4.1 整合 ssm pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" …

coze扣子自定义插件使用方式1

1&#xff0c;模型中的工具描述 2&#xff0c;大模型调用&#xff0c;触发接口&#xff1a;

谨以此文章记录我的蓝桥杯备赛过程

以国优秀结束了蓝桥杯cb组 鄙人来自电信学院&#xff0c;非科班出身&#xff0c;在寒假&#xff0c;大约2024年2月份&#xff0c;跟着黑马程序员将c基础语法学完了&#xff0c;因为过年&#xff0c;事情较多&#xff0c;没在学了。 最初就是抱着拿省三的态度去打这个比赛的&a…

linux指令-高阶指令用法

前言 linux操作系统的环境变量的使用基础需要先了解 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、环境变量是什么&#xff1f; 在Linux系统中&#xff0c;环境变量是动态命名的值&#xff0c;这些值可以由shell&#xff08;如bash&#xff09;…

现代密码学-基础

安全业务 保密业务&#xff1a;数据加密 认证业务&#xff1a;保证通信真实性 完整性业务&#xff1a;保证所接收的消息未经复制、插入、篡改、重排或重放 不可否认业务&#xff1a;防止通信双方的某一方对所发消息的否认 访问控制&#xff1a;防止对网络资源的非授权访问&…

Java应用中的短信发送解决方案:RocketMQ实践指南

在当今的数字化时代&#xff0c;短信作为一种即时的通讯方式&#xff0c;被广泛应用于各种业务场景中&#xff0c;如用户身份验证、订单状态更新、营销推广等。对于Java应用来说&#xff0c;集成一个高效、可靠的短信发送服务是至关重要的。Apache RocketMQ 作为一款高性能、低…

《猎杀:对决》是适合什么样的人玩 Mac电脑怎么玩《猎杀:对决》

《猎杀&#xff1a;对决》是一款集合了生存、竞技和恐怖元素的多人在线游戏&#xff0c;自推出以来受到了广大玩家的热爱。本文将详细探讨《猎杀&#xff1a;对决》适合什么样的人玩以及Mac电脑怎么玩《猎杀&#xff1a;对决》。本文将一一解析&#xff0c;帮助你了解这款游戏是…

程序员的五大职业素养,你知道吗?

程序员职业生涯的挑战与机遇 在当今这个科技日新月异的时代&#xff0c;程序员作为技术行业的中坚力量&#xff0c;其职业生涯无疑充满了无数挑战与机遇。技术的快速迭代要求他们必须不断学习新知识、掌握新技能&#xff0c;以跟上时代的步伐。同时&#xff0c;云计算、人工智…

RFID防盗门:守护您的商品资产安全!

在新零售运营管理中&#xff0c;防盗是至关重要的一环。根据美国零售联合会发布的年度零售安全调查&#xff0c;2022年美国零售商损失了创纪录的1121亿美元。其中年度损失最大因素是由外部盗窃导致库存损失和员工内部盗窃造成的。 然而传统零售业商品资产盘点往往依赖人工排查&…

从零开始学习Linux(9)----文件系统

1.前言 1.铺垫 a.文件内容属性 b.访问文件之前&#xff0c;都得先打开&#xff0c;修改文件&#xff0c;都是通过执行代码的方式完成修改&#xff0c;文件必须被加载到内存中 c.谁打开文件&#xff1f;进程在打开文件 d.一个进程可以打开多少个文件呢&#xff1f;可以打开多个…

GSEA的算法只考虑排序吗

其实这个问题很好回答&#xff0c;只需要运行如下代码&#xff0c;如下的基因列表是顺序是完全相同&#xff0c;并且我们只是做了最基础的变换 library(clusterProfiler) library(org.Hs.eg.db)data(geneList, package"DOSE")ego1 <- gseGO(geneList geneLi…

【图像处理与机器视觉】灰度变化与空间滤波

基础 空间域与变换域 空间域&#xff1a;认为是图像本身&#xff0c;对于空间域的操作就是对图像中的像素直接进行修改 变换域&#xff1a;变换系数处理&#xff0c;不直接对于图像的像素进行处理 邻域 图像中某点的邻域被认为是包含该点的小区域&#xff0c;也被称为窗口 …

Chrome 调试技巧

1. alert 在最早的时候&#xff0c;javascript 程序员调试代码都是通过 alert 进行&#xff0c;但 alert 会让整个程序被打断&#xff0c;并且还有一个很大的缺点&#xff0c;调试完成之后&#xff0c;如果忘记将 alert 删除 or 注释掉&#xff0c;导致别人访问该页面时会莫名…

动画技术在AI绘画中的革新作用

引言&#xff1a; 随着人工智能技术的飞速发展&#xff0c;AI绘画作为其应用领域之一&#xff0c;已经引起了艺术和技术界的广泛关注。动画技术&#xff0c;作为视觉艺术的重要组成部分&#xff0c;与AI绘画的结合不仅为传统绘画带来了新的活力&#xff0c;也推动了创意表达和艺…