C++面向对象学习笔记一
本文阅读下述文章,顺手记录学习《C++面向对象程序设计》✍千处细节、万字总结(建议收藏)_c++面向对象程序设计千处细节-CSDN博客
目录
前言
正文
浅拷贝和深拷贝
向函数传递对象
静态数据成员和静态成员函数
友元
友元函数
1、将非成员函数声明为友元函数
2、将成员函数声明为友元函数
友元类
总结
前言
本文只对面向对象部分知识点有所提及
正文
浅拷贝和深拷贝
首先描述一下如何写拷贝函数
类名::类名(const 类名 &对象名)
{
拷贝构造函数的函数体;
}
相关举例代码如下~~
class Score{
public:Score(int m, int f); //构造函数Score();Score(const Score &p); //拷贝构造函数~Score(); //析构函数void setScore(int m, int f);void showScore();
private:int mid_exam;int fin_exam;
};Score::Score(int m, int f)
{mid_exam = m;fin_exam = f;
}Score::Score(const Score &p)
{mid_exam = p.mid_exam;fin_exam = p.fin_exam;
}调用拷贝构造函数的一般形式为:类名 对象2(对象1);类名 对象2 = 对象1;
Score sc1(98, 87);
Score sc2(sc1); //调用拷贝构造函数
Score sc3 = sc2; //调用拷贝构造函数
浅拷贝,就是由默认的拷贝构造函数所实现的数据成员逐一赋值。通常默认的拷贝构造函数是能够胜任此工作的
但若类中含有指针类型
的数据,则这种按数据成员逐一赋值的方法会产生错误。
class Student{
public:Student(char *name1, float score1);~Student();
private:char *name;float score;
};如下语句会产生错误
Student stu1("白", 89);
Student stu2 = stu1;
上述错误是因为stu1和stu2所指的内存空间相同,在析构函数释放stu1所指的内存后,再释放stu2所指的内存会发生错误,因为此内存空间已被释放。
主要问题是,这样会导致stu1和stu2所指的内存空间一样,需要给stu2重新生成新的空间
解决方法就是重定义拷贝构造函数,为其变量重新生成内存空间。
Student::Student(const Student& stu)
{name = new char[strlen(stu.name) + 1];if (name != 0) {strcpy(name, stu.name);score = stu.score;}
}
向函数传递对象
1、使用对象作为函数参数:对象可以作为参数传递给函数,其方法与传递其他类型的数据相同。在向函数传递对象时,是通过“传值调用”的方法传递给函数的。因此,函数中对对象的任何修改均不影响调用该函数的对象(实参本身)。
2、使用对象指针作为函数参数:对象指针可以作为函数的参数,使用对象指针作为函数参数可以实现传值调用,即在函数调用时使实参对象和形参对象指针变量指向同一内存地址,在函数调用过程中,形参对象指针所指的对象值的改变也同样影响着实参对象的值。
3、使用对象引用作为函数参数:在实际中,使用对象引用作为函数参数非常普遍,大部分程序员喜欢使用对象引用替代对象指针作为函数参数。因为使用对象引用作为函数参数不但具有用对象指针做函数参数的优点,而且用对象引用作函数参数将更简单、更直接。
这里重点是,使用对象作为参数时候,函数中对对象的任何修改都不影响对象本身
其次一般使用对象引用而非对象指针
下面选取文章的代码,主要是要探讨代码运行结果~
#include <iostream>
using namespace std;class Point{
public:int x;int y;Point(int x1, int y1) : x(x1), y(y1) //成员初始化列表{ }int getDistance() {return x * x + y * y;}
};void changePoint1(Point point) //使用对象作为函数参数
{point.x += 1;point.y -= 1;
}void changePoint2(Point *point) //使用对象指针作为函数参数
{point->x += 1;point->y -= 1;
}void changePoint3(Point &point) //使用对象引用作为函数参数
{point.x += 1;point.y -= 1;
}int main()
{Point point[3] = {Point(1, 1), Point(2, 2), Point(3, 3)};Point *p = point;changePoint1(*p);cout << "the distance is " << p[0].getDistance() << endl;p++;changePoint2(p);cout << "the distance is " << p->getDistance() << endl;changePoint3(point[2]);cout << "the distance is " << point[2].getDistance() << endl;return 0;
}
运行结果如下: the distance is 2 the distance is 10 the distance is 20主要探讨第一个,由于是传参,并未对对象本身有影响,所以仍然结果是1+1=2
静态数据成员和静态成员函数
在一个类中,若将一个数据成员说明为static
,则这种成员被称为静态数据成员。与一般的数据成员不同,无论建立多少个类的对象,都只有一个静态数据成员的拷贝。从而实现了同一个类的不同对象之间的数据共享。
定义静态数据成员的格式如下:
static 数据类型 数据成员名;
几点特别的地方
1、静态数据成员的初始化与普通数据成员不同。静态数据成员初始化应在类外单独进行,而且应在定义对象之前进行。一般在main()函数之前、类声明之后的特殊地带为它提供定义和初始化。
2、静态数据成员属于类,所有这个类的对象都有的数据成员(准确地说,是属于类中对象的集合),而不像普通数据成员那样属于某一对象,因此,可以使用“类名::
”访问静态的数据成员。
格式如下:
类名::静态数据成员名
。
3、静态数据成员与静态变量一样,是在编译时创建并初始化。它在该类的任何对象被建立之前就存在。因此,共有的静态数据成员可以在对象定义之前被访问。对象定以后,共有的静态数据成员也可以通过对象进行访问。
其访问格式如下
对象名.静态数据成员名;
对象指针->静态数据成员名;
在类定义中,前面有static说明的成员函数称为静态成员函数。静态成员函数属于整个类,是该类所有对象共享的成员函数,而不属于类中的某个对象。
静态成员函数的作用不是为了对象之间的沟通,而是为了处理静态数据成员。
定义静态成员函数的格式如下:
static 返回类型 静态成员函数名(参数表)
;
下面对静态成员函数的使用再做几点说明:
1、一般情况下,静态函数成员主要用来访问静态成员函数。当它与静态数据成员一起使用时,达到了对同一个类中对象之间共享数据的目的。
2、私有静态成员函数不能被类外部的函数和对象访问。
3、使用静态成员函数的一个原因是,可以用它在建立任何对象之前调用静态成员函数,以处理静态数据成员,这是普通成员函数不能实现的功能
4、编译系统将静态成员函数限定为内部连接,也就是说,与现行文件相连接的其他文件中的同名函数不会与该函数发生冲突,维护了该函数使用的安全性,这是使用静态成员函数的另一个原因。
5、静态成员函数是类的一部分,而不是对象的一部分。如果要在类外调用公有的静态成员函数,使用如下格式较好:类名::静态成员函数名()
主要说明了静态成员函数,主要是为了静态成员服务的,为了能调用静态成员并实现一个类的多个对象直接可以实现静态成员数据共享,由于静态成员函数属于类的一部分,所以可以类外调用,同时为了防止函数名冲突,将其内部链接。
关于静态数据成员和静态成员函数的演示代码
#include <iostream>
using namespace std;class Score{
private:int mid_exam;int fin_exam;static int count; //静态数据成员,用于统计学生人数static float sum; //静态数据成员,用于统计期末累加成绩static float ave; //静态数据成员,用于统计期末平均成绩
public:Score(int m, int f);~Score();static void show_count_sum_ave(); //静态成员函数
};Score::Score(int m, int f)
{mid_exam = m;fin_exam = f;++count;sum += fin_exam;ave = sum / count;
}Score::~Score()
{}/*** 静态成员初始化 ***/
int Score::count = 0;
float Score::sum = 0.0;
float Score::ave = 0.0;void Score::show_count_sum_ave()//静态成员函数
{cout << "学生人数: " << count << endl;cout << "期末累加成绩: " << sum << endl;cout << "期末平均成绩: " << ave << endl;
}int main()
{Score sco[3] = {Score(90, 89), Score(78, 99), Score(89, 88)};sco[2].show_count_sum_ave(); //对象调用~Score::show_count_sum_ave(); //类外调用静态成员函数//结果都一样return 0;
}
由于调用的是静态成员函数,一个对象调用和一个类外调用,最终结果都一样 学生人数: 3 期末累加成绩: 276 期末平均成绩: 92 学生人数: 3 期末累加成绩: 276 期末平均成绩: 92
友元
类的主要特点之一是数据隐藏和封装,即类的私有成员(或保护成员)只能在类定义的范围内使用,也就是说私有成员只能通过它的成员函数来访问。但是,有时为了访问类的私有成员而需要在程序中多次调用成员函数,这样会因为频繁调用带来较大的时间和空间开销,从而降低程序的运行效率。为此,C++提供了友元来对私有或保护成员进行访问。这个是重点,主要可以访问对私有或者保护成员
友元包括友元函数和友元类。
友元函数
友元函数既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数。
友元函数不是当前类的成员函数,但它可以访问该类的所有成员,包括私有成员、保护成员和公有成员。这一点就是上诉,C++提供友元的主要原因,是为了能访问私有或保护成员。
在类中声明友元函数时,需要在其函数名前加上关键字friend。此声明可以放在公有部分,也可以放在保护部分和私有部分。友元函数可以定义在类内部,也可以定义在类外部。
1、将非成员函数声明为友元函数
#include <iostream>
using namespace std;
class Score{
private:int mid_exam;int fin_exam;
public:Score(int m, int f);void showScore();friend int getScore(Score &ob);
};Score::Score(int m, int f)
{mid_exam = m;fin_exam = f;
}int getScore(Score &ob)
{return (int)(0.3 * ob.mid_exam + 0.7 * ob.fin_exam);
}int main()
{Score score(98, 78);cout << "成绩为: " << getScore(score) << endl;return 0;
}
这里我一开始想着,不用友元也可以,然后改了下代码,发现主要问题
如果 getScore(Score &ob)不是Score的友元函数,那么getScore(Score &ob)就不可以访问其中的私有成员mid_exam,fin_exam,从而本身就会报错。
当然我们可以直接在类里写成员函数,直接传出私有成员,或者直接在类里直接写上这个函数就不会需要友元,但仍然需要知道这里的友元主要是为了访问私有成员。但是当一个函数需要访问多个类时,友元函数非常有用,普通的成员函数只能访问其所属的类,但是多个类的友元函数能够访问相关的所有类的数据。
同时因为友元函数不是类的成员,所以它不能直接访问对象的数据成员,也不能通过this指针访问对象的数据成员,它必须通过作为入口参数传递进来的对象名(或对象指针、对象引用)来访问该对象的数据成员。
2、将成员函数声明为友元函数
一个类的成员函数可以作为另一个类的友元,它是友元函数中的一种,称为友元成员函数。友元成员函数不仅可以访问自己所在类对象中的私有成员和公有成员,还可以访问friend声明语句所在类对象中的所有成员,这样能使两个类相互合作、协调工作,完成某一任务。
#include <iostream>
#include <string>
using namespace std;class Score; //对Score类的提前引用说明
class Student{
private:string name;int number;
public:Student(string na, int nu) {name = na;number = nu;}void show(Score &sc);
};class Score{
private:int mid_exam;int fin_exam;
public:Score(int m, int f) {mid_exam = m;fin_exam = f;}friend void Student::show(Score &sc);
};void Student::show(Score &sc) {cout << "姓名:" << name << " 学号:" << number << endl;cout << "期中成绩:" << sc.mid_exam << " 期末成绩:" << sc.fin_exam << endl;
}int main() {Score sc(89, 99);Student st("白", 12467);st.show(sc);return 0;
}
一个类的成员函数作为另一个类的友元函数时,必须先定义这个类。并且在声明友元函数时,需要加上成员函数所在类的类名;就如同上面所说,上述成员函数声明为友元成员函数,也可以利用第一条的非成员函数替代,只要在俩个类都声明友元函数即可,并同时引用俩个类。
友元类
操作是直接friend一个类就行
当一个类被说明为另一个类的友元类时,它所有的成员函数都成为另一个类的友元函数,这就意味着作为友元类中的所有成员函数都可以访问另一个类中的所有成员。
友元关系不具有交换性和传递性。就不存在Y是X友元,Z是Y友元,能得出Z是X友元这种情况,这是不对的。
总结
本文是阅读之后的摘要内容,主要记录了拷贝,传递对象,静态与友元的一些内容。
在此继续感谢佬的付出
推荐学习博客 https://xxetb.xetslk.com/s/4GgGz6
相关文章:
C++面向对象学习笔记一
本文阅读下述文章,顺手记录学习《C面向对象程序设计》✍千处细节、万字总结(建议收藏)_c面向对象程序设计千处细节-CSDN博客 目录 前言 正文 浅拷贝和深拷贝 向函数传递对象 静态数据成员和静态成员函数 友元 友元函数 1、将非成员函数声明…...

C++容器之vector类
目录 1.vector的介绍及使用1.1vector的介绍1.2vector的使用1.2.1 vector的定义1.2.2 vector iterator 的使用1.2.3 vector 空间增长问题1.2.4 vector 增删查改1.2.5vector 迭代器失效问题1.2.6 vector 在OJ中的使用。 2.vector深度剖析及模拟实现2.1 std::vector的核心框架接口…...
什么是MVCC?
MVCC是一种数据库的并发控制策略,就是为了解决多个用户同时访问数据库修改同一数据所造成的问题,如何解决这个问题了? 就是通过创建同一个数据的不同的版本,通过创建时间的不同,最后进行数据合并,其就不用给数据库上锁了,其实数据库的锁,虽然说InnoDB已经非常牛逼了,可以使用行…...
数据结构队列学习
引入 众说周知,在队列的题目中,队头指针(front)和队尾指针(rear)有两种指示方法。 (1)队头指针 ①指向队头元素 ②指向队头元素元素的前一个位置 (2)队尾指针 ①指向队尾元素 ②指向队尾元素的后一个位置 指…...

Javaweb第五次作业
poet数据库sql语言 create table poet(id int unsigned primary key auto_increment comment ID,name varchar(10) not null comment 姓名,gender tinyint unsigned not null comment 性别, 说明: 1 男, 2 女,dynasty varchar(10) not null comment朝代,title varchar(20) not…...

BetterMouse for Mac激活版:鼠标增强软件
BetterMouse for Mac是一款鼠标增强软件,旨在取代笨重的、侵入性的和耗费资源的鼠标驱动程序,如罗技选项。它功能丰富,重量轻,效率优化,而且完全隐私安全,试图满足你在MacOS上使用第三方鼠标的所有需求。 B…...

红米1s 刷入魔趣 (Mokee)ROM(Android 7.1)
目录 背景准备工具硬件(自己准备)软件(我会在文末提供链接) 刷机步骤1. 重启电脑2. 安装驱动3. 刷入TWRP4. 清空数据5. 刷入魔趣6. 开机 结尾下载链接 本文由Jzwalliser原创,发布在CSDN平台上,遵循CC 4.0 B…...
MySQL中的事务隔离级别
事务隔离级别 未提交读(Read uncommitted)是最低的隔离级别。通过名字我们就可以知道,在这种事务隔离级别下,一个事务可以读到另外一个事务未提交的数据。这种隔离级别下会存在幻读、不可重复读和脏读的问题。提交读(Read committed)也可以翻译成读已提交…...
多线程应用实战
文章目录 1、如何实现多线程交替打印字母和数字,打印效果:A1B2C3D4...AutomicBlockingQueueReentrantLockLockSupportSynchronizedWaitNotifyTransferQueueWay 2、实现多个线程顺序打印abc3、实现阻塞队列 1、如何实现多线程交替打印字母和数字ÿ…...
selenium解放双手--记某电力学校的刷课脚本
免责声明:本文仅做技术交流与学习... 重难点: 1-对目标网站的html框架具有很好的了解,定位元素,精准打击. 2-自动化过程中窗口操作的转换. 前置知识: python--selenium模块的操作使用 前端的html代码 验证码自动化操作 Chrome & Chromedriver : Chrome for Testing ava…...

JDK 17有可能代替 JDK 8 吗
不好说,去 Oracle 化是很多公司逐步推进的政策。 JVM 有 OpenJ9。很多公司可能会用 IBM 的版本。 JDK 这东西,能用就不会升级。 JDK 太基础了,决定了后面的很多 jar 的版本。 https://www.ossez.com/t/jdk-17-jdk-8/14102...
代码随想录算法训练营第36期DAY23
DAY23 530二叉搜索树的最小绝对差 /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(null…...
Leetcode 3128. Right Triangles
Leetcode 3128. Right Triangles 1. 解题思路2. 代码实现 题目链接:3128. Right Triangles 1. 解题思路 这一题的话对于任意一个位置,如果该位置为1,假设其所在行中1的个数 r i r_i ri,所在列中1的个数为 c j c_j cj&#…...
力扣经典150题第五十三题:基本计算器
目录 力扣经典150题第五十六题:基本计算器示例提示解题思路 力扣经典150题第五十六题:基本计算器 给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。 注意:不允许使用任何将字符串作为数学表达式计算的内置函数…...

如何为 Nestjs 编写单元测试和 E2E 测试
前言 最近在给一个 nestjs 项目写单元测试(Unit Testing)和 e2e 测试(End-to-End Testing,端到端测试,简称 e2e 测试),这是我第一次给后端项目写测试,发现和之前给前端项目写测试还…...

基于Python的LSTM网络实现单特征预测回归任务(TensorFlow)
单特征:数据集中只包含2列,时间列价格列,仅利用价格来预测价格 目录 一、数据集 二、任务目标 三、代码实现 1、从本地路径中读取数据文件 2、数据归一化 3、创建配置类,将LSTM的各个超参数声明为变量,便于后续…...

Spring - 8 ( 10000 字 Spring 入门级教程 )
一: MyBatis 1.1 引入 MyBatis 我们学习 MySQL 数据库时,已经学习了 JDBC 来操作数据库, 但是 JDBC 操作太复杂了. 我们先来回顾⼀下 JDBC 的操作流程: 创建数据库连接池 DataSource通过 DataSource 获取数据库连接 Connection编写要执行带 ? 占位符…...

鸿蒙内核源码分析(忍者ninja篇) | 都忍者了能不快吗
ninja | 忍者 ninja是一个叫 Evan Martin的谷歌工程师开源的一个自定义的构建系统,最早是用于 chrome的构建,Martin给它取名 ninja(忍者)的原因是因为它strikes quickly(快速出击).这是忍者的特点,可惜Martin不了解中国文化,不然叫小李飞刀更合适些.究竟有多块呢? 用Martin自…...

Linux——守护进程化(独立于用户会话的进程)
目录 前言 一、进程组ID与会话ID 二、setsid() 创建新会话 三、daemon 守护进程 前言 在之前,我们学习过socket编程中的udp通信与tcp通信,但是当时我们服务器启动的时候,都是以前台进程的方式启动的,这样很不优雅,…...

安卓开发--按键跳转页面,按键按下变色
前面已经介绍了一个空白按键工程的建立以及响应方式,可以参考这里:安卓开发–新建工程,新建虚拟手机,按键事件响应。 安卓开发是页面跳转是基础!!!所以本篇博客介绍利用按键实现页面跳转&#…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...

STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...

如何把工业通信协议转换成http websocket
1.现状 工业通信协议多数工作在边缘设备上,比如:PLC、IOT盒子等。上层业务系统需要根据不同的工业协议做对应开发,当设备上用的是modbus从站时,采集设备数据需要开发modbus主站;当设备上用的是西门子PN协议时…...