类与对象(上篇)

前言

在之前我们学的C++入门主要是为现在学习类与对象打基础,今天我们才算真正开始学习C++了。因为类与对象的知识点比较多,所以我们将它分为三部分讲解,今天我们学习类与对象的上篇。

一、面向过程和面向对象的初步认识

1、面向过程

面向过程顾名知义,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。例如:C语言。

2、面向对象

面向对象,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。例如:C++,java(注意:C++兼容C,所以C++并不是纯面向对象的语言,是支持面向对象和面向过程的“混编”)。

3、举个例子

外卖系统分别用面向过程与面向对象是怎样实现的?如下图所示:

在这里插入图片描述
tip: 现在大家先对概念理解一下,随着以后的学习会慢慢理解面向对象的思想。

二、类的引入

在C语言中,数据与方法是分离的。而在C++中,数据与方法没有分离。所以在C++中struct被升级成了类——结构体内不仅可以定义变量,也可以定义函数。

代码示例:写一个简易的栈类

#include<iostream>
#include<stdlib.h>//展开命名空间
using namespace std;
//栈类
typedef int DataType;//栈的元素类型
struct Stack
{//成员函数//栈的初始化void Init(int capacity = 4){//在堆区开辟capacity个栈空间_array = (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr == _array){perror("malloc fail");return;}//初始化栈的容量与栈顶位置_capacity = capacity;_top = 0;//指向栈顶位置的下一个}//入栈void push(const DataType& x){//判断是否扩容if (_top == _capacity){//扩容}//入栈_array[_top] = x;_top++;}//访问栈顶元素DataType Top(){return _array[_top - 1];}//销毁void Destroy(){free(_array);_array = nullptr;_capacity = 0;_top = 0;}//成员变量DataType* _array;//指向堆区开辟的数组int _capacity;//栈的容量int _top;//栈顶位置
};int main()
{//C++兼容C,struct以前的用法都可以继续使用struct Stack s1;//C++类名就是类型,所以可以直接写StackStack s2;s2.Init();//缺省参数——没有传实参,使用缺省值s2.push(1);s2.push(1);cout << s2.Top() << endl;s2.Destroy();return 0;
}

tip:

①C++将struct升级成了类,不仅可以定义变量,还可以定义函数。

②C++中类名就是类型,在C里面struct 类名组合在一起才是类型。因为C++兼容C所以两种写法都可以,struct以前的用法都可以继续使用。

③我们发现在类中变量成员在声明前面可以使用,这是因为类域是一个整体,所以变量写在后面,也不用声明。

虽然struct被升级成了类,但是在C++中更喜欢用class来代替。

三、类的定义

1、类定义的代码示例

class className
{//类体:由由成员变量和成员函数组成
}; //注意与struct一样后面要有分号

解读:

①class是定义类的关键字,className为类名,{}中为类的主体,注意类定义结束时后面分号不能省略。

②类体中的内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或成员函数。

2、类的两种定义方法

①声明和定义全部放在类体中 ,需要注意:成员函数如果在类中定义,默认为内联函数,最后由编译器决定。

class Person
{//函数的声明与实现都在类中void showInfo(){cout << _name << " " << _age << endl;}//成员函数char* _name;int _age;
};

②类声明放在.h文件中,成员函数定义放在.cpp文件中。如下图:

在这里插入图片描述
tip:类成员函数定义时,注意要成员函数名前面要类名::,表明它是那个类的。

总结:对于这两种方法,平时我博客讲解的时候为了方便使用方法1定义类,但是建议大家在以后写项目和工作时使用方法2。

3、成员变量的命名习惯

为了避免成员变量与成员函数的参数同名,我们一般可以①成员变量加前缀_;②成员变量加前缀my_;③成员变量加后缀_等方法。

代码示例:

class Date
{
public:void Init(int year){//因为成员变量加了前缀_,所以这里我们能很好的区分该语句是给对象的_year赋值_year = year;}
private:int _year;
};

四、类的访问限定符与封装

1、类的访问限定符

(1)引入访问限定符

我们先来看两段代码:

代码1:能运行吗?

struct Person
{char _name[10];int _age;
};int main()
{//定义对象p1Person p1;//直接修改对象p1的年龄p1._age = 18;return 0;
}

代码2:能运行吗?

class Person
{char _name[10];int _age;
};int main()
{//定义对象p1Person p1;//直接修改对象p1的年龄p1._age = 18;return 0;
}

答案是:代码1能运行,代码2不能运行,出现语法错误,这是为什么呢?这就是我们接下来要讲解的类的访问限定符。

(2)访问限定符

C++实现封装的方式:用类将对象的属性与方法结合在一起,让对象更加完善,通过访问权限选择性的将接口提供给外部的用户使用。

在这里插入图片描述

(3)访问限定符说明

①public修饰的成员在类外可以直接被访问。

②protected和private修饰的成员在类外不能直接被访问。(在C++初阶protected和private类似,在后面进阶讲继承的时候才能体现他们的区别)

③访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止。

④如果后面没有访问限定符,作用域就到“}”即类的结束。

⑤class的默认访问权限为private,struct为public(因为struct要兼容C)。
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。

(4)问题:C++中struct和class的区别

C++需要兼容C,所以C++中struct可以当作结构体使用。另外C++中struct还可以用来定义类。和class定义类一样,区别是struct定义的类默认访问权限是public,class定义的类默认权限是private。注意:在继承和模板参数列表位置,struct和class也有区别,后序再给大家讲解。

2、封装

(1)面向对象的三大特性

面向对象的三大特性:封装、继承、多态。

(2)什么是封装

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交换。

在我们生活中有许多封装的实例,例如:你家的房子,就是一个封装。如果不封装的话,那谁都可以进你家了。

(3)封装的本质

封装的本质是一种管理,让用户更方便使用类。

在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制那些方法可以在类外直接被使用。

在这里插入图片描述

五、类的作用域

1、类域

①类定义了一个新的作用域,类的所有成员都在类的作用域中。

②在类体外定义成员时,需要使用::作用域操作符指明成员属于哪个类域。

③类域是一个整体,成员变量不是一定写在成员函数后面的。

代码示例:

#include<iostream>
using namespace std;class Person
{
public:void PrintPersonInfo();
private:char _name[20];char _gender[3];int _age;
};//这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{cout << _name << " " << _gender << " " << _age << endl;
}

2、简单总结我们已经学过的域

①我们已经学习了四种域:局部域、全局域、命名空间域、类域。

②同一个域不能定义同名变量,不同域可以定义同名变量。

③域都会影响访问,但只有局部域和全局域影响生命周期。

④编译器访问变量规则:一般默认先在局部找,找不到再去全局找(都找不到则报错);特殊类方法先局部找,找不到去类域找,最后再去全局找。

⑤命名空间域与全局域平行,但是如果不展开就不会访问。

⑥::作用域操作符,指定访问某个域的变量。指定方式:域名::变量名。

六、类的实例化

1、定义

用类类型创建对象的过程,称为类的实例化。

2、实例化的说明

①类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。

②一个类可以实例化出多个对象,实例化出的对象才占用实际的物理空间,存储类的成员变量。

类就像是设计图,类实例化出对象就像现实中使用建筑设计图建造出的房子。 图纸并没有实体,同样类也只是一个设计,只有实例化出的对象才能实际存储数据,占用物理空间。

在这里插入图片描述
代码示例:

#include<iostream>
using namespace std;class Person
{
public:void PrintPersonInfo(){cout << _name << " " << _sex << " " << _age << endl;}
public:char* _name;char* _sex;int _age;
};int main()
{//下面语句是否正确?//Person::_age = 18;//错误,因为类没有实例化,并没有开辟空间。只有类实例化出的对象才有具体的年龄。//tip:类的对象要整体实例化才可以。//类实例化对象/对象定义Person man;//tip:只有类实例化,开辟了空间,才能存储数据man._name = (char*)"zhangsan";man._sex = (char*)"男";man._age = 18;man.PrintPersonInfo();return 0;
}

七、类对象模型

1、如何计算类的大小

问题: 类中既有成员变量,也有成员函数,那么一个类的对象中包含什么?如何计算一个类的大小?

我们先来一段代码示例,用编译器运行计算看类A的大小是多少。

#include<iostream>
using namespace std;class A
{
public://成员函数void f(){}
private://成员变量int _a;char _ch;
};int main()
{//实例化对象A a;//打印类对象的大小cout << sizeof(a) << endl;return 0;
}

运算结果:类A的大小

在这里插入图片描述
解读:

①类对象存储: 类对象只保存类的成员变量,不保存类的成员函数。(为什么会这样呢?详细讲解在后面类对象存储猜测)

②类的大小计算: 与C语言计算结构体的方式一样,需要注意内存对齐。

③回顾内存对齐的规则:

1、结构体的第一个成员,对齐到结构体变量在内存中存放位置的0偏移量。

2、从第二个成员开始,每个成员变量都要对齐到(一个对齐数)的整数倍。

  • 对齐数=编译器默认的一个对齐数与一个结构体成员自身大小的较小值。
  • VS默认对齐数为8;Linux gcc没有默认对齐数,对齐数就是结构体成员自身大小。


3、结构体总大小,必须是所有成员变量的对齐数中最大对齐数的整数倍。

4、如果是嵌套结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

④内存对齐的意义:

1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定的数据,否则抛出硬件异常(例:int——>对齐到4的整数倍)。

2、性能原因:计算机读取数据时,并不是想访问哪个字节就访问哪个字节,而是从结构体的对齐边界开始按照访问倍数去访问。假设CPU一次访问4个字节(具体与硬件有关),对齐访问_a只要访问一次,不对齐_a要访问两次,如下图所示:

在这里插入图片描述
如图可知,内存对齐是拿空间换时间。

⑤类A的内存大小为8,如图所示:

在这里插入图片描述

2、类对象的存储猜测

①对象中包含类的各个成员:

在这里插入图片描述
缺陷: 不同对象中成员变量不同,但是调用同一个函数,如果按照此方式存储,当一个类创建多个对象时,**每个对象都会保存一份代码,相同代码保存多次,浪费空间。

②只保存成员变量,成员函数存放在公共的代码段:

在这里插入图片描述
tip:关于上述两种存储方式,计算机按照方式二来存储。

3、空类与仅有成员函数的类大小

代码示例:

#include<iostream>
using namespace std;//类中仅有成员函数
class A1
{
public:void f(){}
};//类中什么都没有——空类
class A2
{};int main()
{//输出A1,A2的大小cout << sizeof(A1) << endl;cout << sizeof(A2) << endl;return 0;
}

运行结果:

在这里插入图片描述
总结:

①没有成员变量的类对象,需要一个字节,是为了占位(不存储有效数据),表示对象存在。

②一个类的大小,实际就是该类中成员变量之和,还需要注意内存对齐。

八、this指针

1、this指针的引出

我们来定义一个日期类Data:

#include<iostream>
using namespace std;class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year; int _month;int _day; 
};
int main()
{Date d1, d2;d1.Init(2022, 1, 11);d2.Init(2022, 1, 12);d1.Print();d2.Print();return 0;
}

运行结果:

在这里插入图片描述

问题: d1与d2调用同一个函数print,为什么打印结果不一样呢?(在类的存储模型我们知道成员函数与对象无关,它存储在公共区域。)

答案是: C++中引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数”添加了一个隐藏的this指针参数,让该this指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。如下图所示:

在这里插入图片描述

2、this指针的特点

(1)语法规定:this指针不能在形参和实参显示传递,但是可以在函数内部显示使用

①调用成员函数时,不能显示传递对象的地址给this,因为编译器会自动传递了,不需要用户传递。

tip: 既然编译器会自动传递对象地址,为什么不通过类来调用函数——因为通过类来调用函数,不会传this指针;通过对象调用函数,虽然不再对象里找函数,但是会传this指针。

②定义成员函数时,也不能显示定义this指针,因为编译器会自动定义。

tip: this指针是一个关键字,指向当前对象地址。

③this指针可以在函数体内部显示使用,如下代码所示:

void Print(){//语法规定:this指针不能在形参和实参显示传递,但是可以在函数内部显示使用//函数体中所有成员变量都要通过this指针访问//1、在函数体中,访问成员变量,你不写他会自动添加thiscout << _year << "-" << _month << "-" << _day << endl;//2、在函数体内可以自己显示使用this指针cout << this->_year << "-" << this->_month << "-" << this->_day << endl;}

tip: 在函数内部,你访问对象的成员变量你不写this指针它会自动添加,你也可以显示使用。

(2)this指针的类型:类类型* const,即成员函数中,不能给this指针赋值

tip:左定值,右定向。

const在*的左边,则指针指向的变量的值不能通过指针改变;在 * 的右边,则指针的指向不能改变。

代码示例:

int main()
{int a = 10;//右定向int* const pa = &a;int b = 9;//pa = &b;//报错,左值不可修改,即指针指向不能改变*pa = b;//可以修改指向变量的值return 0;
}

3、面试题

问题1: this指针存在哪里?

答案是:this指针本质上是“成员函数”的形参,所以this指针与普通参数一样存在函数调用的栈帧里面。如下图汇编代码:

在这里插入图片描述
tip: 在VS集成开发环境下,对this指针进行了优化,对象地址是放在ecx寄存器,ecx存储this指针的值。

问题2: this指针可以为空吗?

我们先来看两段代码,判断A、编译报错 B、运行崩溃 C、正常运行

代码1:

class A
{
public:void Print(){cout << "Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}

代码2:

class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA();return 0;
}

答案是: 代码1:C、运行成功;代码2:B、运行崩溃。解析如下图所示:

在这里插入图片描述

4、this指针的好处

在这里插入图片描述

tip: C就像手动挡需要自己控制变速箱,C++有了封装,引入this指针就像自动挡电脑程序控制变速箱。简单来说,就是更简单,不易出错了

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

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

相关文章

工作记录---为什么双11当天不能申请退款?(有趣~)

为什么&#xff1f; 服务降级了 服务降级&#xff1a; 当服务器压力剧增的情况下&#xff0c;根据实际业务情况及流量&#xff0c;对一些服务和页面有策略的不处理或换种简单的方式处理&#xff0c;从而释放服务器资源以保证核心交易正常运作或高效运作。 分布式系统的降级…

OpenCvSharp从入门到实践-(01)认识OpenCvSharp开发环境搭建

目录 一、OpenCV 二、OpenCvSharp 三、OpenCvSharp开发环境搭建 四、下载 五、其他 一、OpenCV OpenCV是基于Apache2.0许可&#xff08;开源&#xff09;发行的跨平台计算机视觉和机器学习函数库&#xff0c;支持Windows、Linux、Android和Mac OS操作系统。OpenCV由一系…

C语言中的指针(上)

目录 一、基本概念 1.变量的存储空间 2.定义指针 3.引用与解引用 二、指针的算术运算、类型以及通用指针 1.指针的算数运算 2.指针类型以及通用型指针 三、指向指针的指针&#xff08;pointers to pointers&#xff09; 四、函数传值以及传引用 1.局部变量 2.从存储地…

Failed to execute org.scala-tools:maven-scala-plugin:2.15.2解决

原因也不是很清楚&#xff0c;查看一个博主文章(net.alchim31.maven:scala-maven-plugin&#xff1a;maven依赖无法下载或无法编译)得到的解决方案&#xff1a; 在idea的terminal执行以下语句即可实现maven对scala代码的编译&#xff1a; mvn clean scala:compile compile pac…

代码随想录算法训练营第四十一天【动态规划part03】 | 343. 整数拆分、96.不同的二叉搜索树

343. 整数拆分 题目链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 求解思路&#xff1a; 动规五部曲 确定dp数组及其下标含义&#xff1a;dp[i] 拆分i&#xff0c;可以得到的最大乘积为dp[i]确定递推公式&#xff1a;从1开始遍…

测试Bard和ChatGPT关于双休的法规和推理

Bard是试验品&#xff0c;chatgpt是3.5版的。 首先带着问题&#xff0c;借助网络搜索&#xff0c;从政府官方网站等权威网站进行确认&#xff0c;已知正确答案的情况下&#xff0c;再来印证两个大语言模型的优劣。 想要了解的问题是&#xff0c;在中国&#xff0c;跟法定工作…

linux rsyslog综合实战1

本次我们通过rsyslog服务将A节点服务器上的单个日志(Path:/var/log/245-1.log)实时同步到B节点服务器目录下(Path:/opt/rsyslog/245) 1.rsyslog架构 2.环境信息 环境信息 HostnameIpAddressOS versionModuleNotersyslog1192.168.10.245CentOS Linux release 7.9.2009 (Core)rs…

原理Redis-Dict字典

Dict 1) Dict组成2) Dict的扩容3) Dict的收缩4) Dict的rehash5) 总结 1) Dict组成 Redis是一个键值型&#xff08;Key-Value Pair&#xff09;的数据库&#xff0c;可以根据键实现快速的增删改查。而键与值的映射关系正是通过Dict来实现的。 Dict由三部分组成&#xff0c;分别…

ES6有何新特性?(下篇)

目录 函数参数的默认值设置 rest参数 扩展运算符 Symbol 迭代器 生成器 Promise Class 数值扩展 对象方法扩展 模块化 大家好呀&#xff01;今天这篇文章继续为大家介绍ES6的新特性&#xff0c;上上上篇文章介绍了一部分&#xff0c;这篇文章会将剩下的部分新增的特…

【CHI】Ordering保序

本节介绍CHI协议所包含的支持系统保序需求的机制&#xff0c;包括&#xff1a; • Multi-copy atomicity • Completion response and ordering • Completion acknowledgment • Transaction ordering 一、 Multi-copy atomicity CHI协议中所使用的memory model要求为mu…

怎么在echarts图上左右滑动切换数据区间

说在前面 不管前端还是后端&#xff0c;大家或多或少都了解使用过echarts图表吧&#xff0c;很多时候我们只是需要展示指定区间的数据&#xff0c;但有时我们希望在图表上能够轻松地切换数据的展示区间&#xff0c;以便更清晰地观察特定时间段或区域的变化。在本文中&#xff0…

电子学会C/C++编程等级考试2022年06月(一级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:倒序输出 依次输入4个整数a、b、c、d,将他们倒序输出,即依次输出d、c、b、a这4个数。 时间限制:1000 内存限制:65536输入 一行4个整数a、b、c、d,以空格分隔。 0 < a,b,c,d < 108输出 一行4个整数d、c、b、a,整数之…

听GPT 讲Rust源代码--src/bootstrap

图片来自 使用rust的image库进行图片压缩[1] File: rust/src/bootstrap/build.rs 在Rust源代码中&#xff0c;rust/src/bootstrap/build.rs这个文件是一个构建脚本。构建脚本是一个在编译Rust编译器本身时运行的程序&#xff0c;它用于初始化和配置Rust编译器的构建过程。build…

沸点 | Ultipa 图数据库金融应用场景优秀案例首批入选,金融街论坛年会发布

为推进图数据库在金融行业的创新应用试点&#xff0c;近日&#xff0c;在2023金融街论坛年会“全球金融科技中心网络年会暨ZIBS北京论坛”上&#xff0c;北京前沿金融监管科技研究院发布了基于国际标准组织——国际关联数据基准委员会&#xff08;LDBC&#xff09;的《图数据库…

c语言:矩阵交换

题目&#xff1a; 代码和思路&#xff1a; #define _CRT_SECURE_NO_WARNINGS #include<stdio.h>int main() {int n 0;int m 0;int arr[10][10] { 0 }; // 输入行和列scanf("%d%d", &n, &m);int i 0;int j 0;//读取数组for (i 0; i < n; i)…

python爬虫SHA案例:某直播大数据分析平台

声明&#xff1a; 该文章为学习使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;违者后果自负&#xff0c;由此产生的一切后果均与作者无关 一、找出需要加密的参数 js运行 atob(‘aHR0cDovL3d3dy5oaDEwMjQuY29tLyMvc2VhcmNoL3NlYXJjaA’) 拿到网址&#xff0c;F12打…

Linux本地WBO创作白板部署与远程访问

文章目录 前言1. 部署WBO白板2. 本地访问WBO白板3. Linux 安装cpolar4. 配置WBO公网访问地址5. 公网远程访问WBO白板6. 固定WBO白板公网地址 前言 WBO在线协作白板是一个自由和开源的在线协作白板&#xff0c;允许多个用户同时在一个虚拟的大型白板上画图。该白板对所有线上用…

将word中的表格无变形的弄进excel中

在上篇文章中记录了将excel表拷贝到word中来&#xff1a; 记录将excel表无变形的弄进word里面来-CSDN博客 本篇记录&#xff1a;将word中的表格无变形的弄进excel中。 1.按F12&#xff0c;“另存为...”&#xff0c;保存类型&#xff1a;“单个文件页面”&#xff0c;保存。…

CXL崛起:2024启航,2025年开启新时代

在2019年&#xff0c;Intel主导联合多家阿里巴巴、Facebook(也就是改名后Meta)、谷歌、Dell、华为、思科、微软、HPE最初的八巨头&#xff0c;发布了新的互联协议CXL&#xff0c;全称Comupte Express Link。由于在服务器领域享有绝对领导地位&#xff0c;Intel一经号令&#xf…

力扣-路径总和问题

路径总和 --简单 112. 路径总和 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 f…