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通信,但是当时我们服务器启动的时候,都是以前台进程的方式启动的,这样很不优雅,…...
安卓开发--按键跳转页面,按键按下变色
前面已经介绍了一个空白按键工程的建立以及响应方式,可以参考这里:安卓开发–新建工程,新建虚拟手机,按键事件响应。 安卓开发是页面跳转是基础!!!所以本篇博客介绍利用按键实现页面跳转&#…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
