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

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

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

相关文章

基于stm32的spi从机实验HAL库编程

目录 基于stm32的spi从机实验HAL库编程前言业务场景硬件设计接线配置swd接口配置spi配置DMA配置中断配置系统时钟配置工程生成代码写点从机代码上机现象后记本文使用的测试工程 基于stm32的spi从机实验HAL库编程 前言 在微控制器的世界中&#xff0c;串行外设接口(SPI)是一种…

ICode国际青少年编程竞赛- Python-2级训练场-for循环练习2

ICode国际青少年编程竞赛- Python-2级训练场-for循环练习2 1、 for i in range(5):Dev.step(9 - i * 2)Dev.turnLeft()2、 for i in range(3):Spaceship.step(i 1)Spaceship.turnRight()Spaceship.step(i 1)Spaceship.turnLeft()3、 for i in range(4):Dev.step(10 - i…

接口测试必备技能

接口测试&#xff0c;其实并没有那么可怕&#xff0c;但是作为测试人员也是必不可少的技能。 接口分为&#xff1a;内部接口和外部接口。 内部接口&#xff1a;是浏览器与服务器的接口。这个很容易理解&#xff0c;web开发一般分前端和后端&#xff0c;前端开发人员用html/css…

vue管理系统导航中添加新的iconfont的图标

1.在官网上将需要的图标&#xff0c;加入项目中&#xff0c;下载 2.下载的压缩包中&#xff0c;可以选择这两个&#xff0c;复制到项目目录中 3.如果和之前的iconfont有重复&#xff0c;那么就重新命名 4.将这里的.ttf文件&#xff0c;也重命名为自己的 5.在main文件中导入 6.在…

【Linux 系统】多线程(线程控制、线程互斥与同步、互斥量与条件变量)-- 详解

一、线程概念 线程是进程的一个执行分支&#xff0c;是在进程内部运行的一个执行流。下面将从是什么、为什么、怎么办三个角度来解释线程。 1、什么是线程 上面是一张用户级页表&#xff0c;我们都知道可执行程序在磁盘中无非就是代码或数据&#xff0c;更准确点表述&#xff0…

第十六课 美化表格

美化表格 利用“图表工具/格式”选项卡可分别对图表的图表区、绘图区、图表标题、坐标轴标题、图例项、数据系列等组成元素进行格式设置&#xff0c;如使用系统提供的形状样式快速设置&#xff0c;或单独设置填充颜色、边框颜色和字体等&#xff0c;从而美化图表。【例如&…

AI 绘画神器 Fooocus 本地部署指南:简介、硬件要求、部署步骤、界面介绍

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 随着人工智能技术的飞速发展&#xff0c;AI 绘画逐渐成为创意领域的新宠。Fooocus 作为一款免费开源的 AI 绘画工具&am…

Git之revert的使用

问题场景&#xff1a; 提交代码都是以merge request的形式合并到主分支master的。 由于有一个merge request被误merge了&#xff0c;这期间又有同时merge了其它内容。 如何快速将这个被误merge的request从master上revert呢&#xff1f; 实例演示&#xff1a; 下面是最近的5…

别再观望!2024年必做的项目:视频号无货源

大家好&#xff0c;我是电商花花。 现在做项目&#xff0c;更喜欢的是一个能稳定出单&#xff0c;稳定发展的一个创业项目&#xff0c;一个好的项目就是能长期稳定的发展&#xff0c;如果只追求短平快收益的项目&#xff0c;这样的项目也并不适合我们。 对于越来越火爆的视频…

冒泡排序----深刻理解版本

前面虽然向大家介绍了冒泡排序&#xff0c;但是表达的不是很清楚&#xff0c;这次我带着更深刻的理解向大家介绍以下冒泡排序。 1.冒泡排序 冒泡排序其实是一种排序算法&#xff0c;通过数据之间的相互比较将一堆混乱的数据按照升序或者降序的顺序排列。 2.解题思路 解题思…

数据存储-SQLite

一般使用到数据库存储&#xff0c;涉及到的数据量都较大&#xff0c;采用文件存储也能完成&#xff0c;但是文件操作复杂&#xff0c;效率低&#xff0c;大量结构化数据通常采用关系型数据库存储较为合适。Android中已经嵌入了轻量级的关系型数据库SQLite&#xff0c;直接按照数…

信号处理SCI期刊,中科院2区,专业性强,审稿速度快!

一、期刊名称 Signal Processing 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;信号处理 影响因子&#xff1a;4.4 中科院分区&#xff1a;2区 出版方式&#xff1a;订阅模式/开放出版 版面费&#xff1a;选择开放出版需支付$3300 三、期刊征稿范围 …

信创基础软件之中间件

信创基础软件之中间件 中间件概述 中间件是一种应用于分布式系统的基础软件&#xff0c;位于应用与操作系统、数据库之间&#xff0c;主要用于解决分布式环境下数据传输、数据访问、应用调度、系统构建和系统集成、流程管理等问题&#xff0c;是分布式环境下支撑应用开发、运…

【多客开源】游戏陪玩系统,游戏陪玩源码,游戏陪玩语音社交源码运营版游戏陪玩平台源码/tt语音聊天/声优服务/陪玩系统源码开黑/约玩源码

介绍 我们针对陪玩app源码市场的发展趋势&#xff0c;整合市面上主流陪玩app应用功能&#xff0c;自主开发了多客陪玩系统源码&#xff0c;并可为客户提供全部原生陪玩源码&#xff0c;进行二次开发&#xff0c;打造适用于线上游戏陪玩、语音聊天、心理咨询、情感陪伴等业务场…

26 | 备库为什么会延迟好几个小时?

在官方的 5.6 版本之前,MySQL 只支持单线程复制,由此在主库并发高、TPS 高时就会出现严重的主备延迟问题。 coordinator 就是原来的 sql_thread, 不过现在它不再直接更新数据了,只负责读取中转日志和分发事务。真正更新日志的,变成了 worker 线程。而 work 线程的个数,就是…

Macbook pnpm 安装 node-sass 报错(node-gyp)

换了 Macbook M3 Pro 后安装项目依赖时报错&#xff0c;提示 node-sass 安装出错。 &#xff08;此外&#xff0c;ValueError: invalid mode: rU while trying to load binding.gyp 也是类似原因。只需要确保 node-gyp 运行条件就可以&#xff09; 原因是 node-gyp 运行环境缺…

IntelliJ IDEA安装教程

IntelliJ IDEA-安装 解锁编程新境界&#xff0c;IntelliJ IDEA —— Java开发者梦寐以求的智慧之选&#xff01; 在代码的海洋里航行&#xff0c;你需要的不仅仅是一艘船&#xff0c;而是一位智勇双全的领航员。IntelliJ IDEA&#xff0c;这款被全球数百万程序员热烈追捧的Ja…

JAVA(三)常用类和API

目录 常用类与基础API---String String的内存结构 构造器和常用方法 字符串构建 String与其他结构间的转换 String的常用API 系列1&#xff1a;常用方法 系列2&#xff1a;查找 系列3&#xff1a;字符串截取 系列4&#xff1a;和字符/字符数组相关 系列5&#xff1a;开头…

数据库加密数据模糊匹配查询技术方案

文章目录 前言沙雕方案内存加载解密密文映射表 常规做法实现数据库加密算法参考 分词组合加密&#xff08;推荐&#xff09; 超神方案总结个人简介 前言 在数据安全性和查询效率之间找到平衡是许多数据管理系统所面临的挑战之一。特别是在涉及加密数据的情况下&#xff0c;如何…

vue3+ts--实际开发之--table表格打印或者保存

vue3实现指定区域打印&#xff08;导出&#xff09;功能-主要是解决分页内容分割问题 一、 问题页面效果二、 Print.js相关属性 和使用1. 介绍2. 安装引入3. PrintJS参数配置表 三 、解决关于分页文字或者表格被分割问题&#xff0c;解决后如下&#xff1a;1. 设置一个自定义ta…