初识C++ · 类和对象(上)

目录

1.面向过程和面向对象初步认识

2.类的引入

3.类的定义

4.类的访问限定符及封装

4.1 访问限定符

4.2 封装

5.类的作用域

6.类的实例化

7.类的对象大小的计算

8.类成员函数的this指针


1.面向过程和面向对象初步认识

C语言是一门面向过程的语言,注重的是解决问题的过程,举个例子——洗衣服,洗衣服的过程是:拿衣服——放进洗衣机——放洗衣液——放水——打开洗衣机——开始工作——结束——晾衣服,注重整个过程,C++是一门面向对象的语言,注重的是解决问题时候涉及的对象,比如洗衣服的时候涉及到了衣服,洗衣机,水,洗衣液等对象,注重的是对象交互来解决某个问题。


2.类的引入

在C语言中的结构体里面只能包含变量,但是在C++里面觉得结构体里面只能放变量不太方便,于是规定函数也可以放在里面:

struct St
{int a;int b;int c;int Add(int x,int y){return x + y;}
};
int main()
{struct St s1;cout << s1.Add(1, 2);return 0;
}

而在中C++更喜欢使用class来定义类,那么这样有有什么好处呢?
比如我们写个栈的初始化,C语言的写法是定义好之后创建函数传参传栈的指针进去,再进行初始化,C++不用:

class Stack
{void StackInit(){arr = nullptr;size = capacity = 0;}int* arr;int size;int capacity;};

如此,直接进行初始化就可以了。


3.类的定义

类的定义使用的关键字是class,定义的时候和结构体一样,花括号后面都有一个分号,class后面跟的就是类名:

class ClassName
{//...
};

其中,类体(花括号)里面的变量叫做类的属性或者是成员变量,类体里面的函数叫做类的方法或者成员函数

类中定义函数的时候有两种方法:
一是定义和声明放在一起,如:

class ClassName
{int Sub(int ,int ){return 1;}//...
};

二是声明和定义放在对应的文件里面,如:

//Stack.hclass Stack
{
public:void Print();private:int* arr;int size;int capacity;};
//Stack.cppvoid Stack::Print()
{cout << "Print" << endl;
}int main()
{Stack s1;s1.Print();return 0;
}

这里有两个需要注意的地方,一个是头文件里面的public和private,一个是.cpp里面的函数定义。

当定义和声明分离的时候,定义函数的时候要在函数名前面加上类名和两个冒号,这是代表去这个类域里面访问这个函数。

定义变量的时候:class Stack s1 和 Stack s1都是可以的,那么方便起见我们就不写class,反正有没有都是可以的。

一般建议的还是第二种,声明和定义分开的形式,这样代码可读性更大,简洁度更高,其次就是函数命名的一些建议

class Date
{
public: void Init(int year,int month,int minute){_year = year;_month = month;_minute = minute;}
private:int _year;int _month;int _minute;
};

命名类里面的变量的时候大多数情况下加一个下划线_,这是因为如上情况的时候避免混淆,初始化的时候,如果变量也是year,那么就是year = year,代码的可读性就非常差,那有人说把参数的year换成y不就行了?

实际上参数的命名最好要有比较明显的实际意义,取名为y这谁知道参数对应的含义是什么呢?


4.类的访问限定符及封装

4.1 访问限定符

上文还有一个注意事项没有介绍,就是public和private的介绍,这是C++中定义类的访问限定符,顾名思义,就是访问变量的时候访问是否会受到限制。

那么这有什么意义呢?

就好比文物展览,如果没有保安,没有文物警戒线等,文物是不大能保存完整的,这时候“访问限定符”就起作用了,可以隔绝一部分没有素质的人。

代码也是如此,C语言中结构体定义好之后就是谁都可以访问它,所以比较考验程序员的素质,C++不一样,C++加了访问限定符,有些变量就不能被随意访问,就起到了一定的保护作用。

默认的是用private修饰,也就是默认是私有的,不让访问。

访问限定符一共有3个,private,public,protected:

关于访问限定符的说明:
1 private和protected在外部不能直接被访问,public可以直接被访问

2 访问限定符的作用域是从该限定符开始一直到下一个限定符出现中的区域

3 class默认限定符为private,struct默认限定符为public(因为要兼容C语言)

4.2 封装

面向对象有三大特性:封装,继承,多态

封装就是利用了访问限定符,数据和使用数据的方法的结合,达到隐藏对象的具体细节和属性,但是可以通过接口的实现和外界进行交互。

封装其实就是管理,让使用者使用该使用的,不该使用的就是不使用,比如电脑,电脑的内部CPU,GPU怎么工作的我们是不用关心,我们关心的是我们使用电脑可以完成什么样的工作,这就是一种封装。


5.类的作用域

类定义好了之后就是单独创建了一个域,这个域叫做类域,和命名空间一样,我们使用里面的成员的时候我们需要指定一下是哪个地方的,比如:

class calculator
{
public :int Add(int ,int);
};int calculator::Add(int x, int y)
{return x + y;
}
int main()
{calculator c1;cout << c1.Add(2,4) << endl;return 0;
}

目前我们学了 局部域全局域命名空间域类域,要注意区分。


6.类的实例化

类就是一个模型,对象就是根据这个模型创建出来的,所以类和对象常常放在一起,类的实例化我们就可以理解为:我们有一张设计图纸,我们根据设计图纸创建了一些东西,这个过程就叫做实例化

只有实例化出来的对象才可以"存放东西",因为类是不能存放数据的,图纸怎么存放数据?是吧。

class calculator
{
public :int Add(int ,int);
private:int _a;
};int main()
{calculator _a = 1;return 0;
}

如果直接给数据就会报错了。

那么提个问题:

类里面的成员变量是声明还是定义?

答案是声明,也就是说在类里面告诉了你有这个东西,但是我还没有给它开辟空间,这也是实例化之前不能放数据的一个理由。

那么什么时候是定义呢?

实例化的时候我们开辟了空间,这个时候就是定义,因为我们开辟了空间,可以用来存放数据了。


7.类的对象大小的计算

我们来看下面五个类:

class C1
{
public:int Add();
};
class C2
{
private:int _a;
};
class C3
{};
class C4
{char c;
};
class C5
{
public:int Add();char c;
};

试问三个类的大小是多大?

第一个类有函数,第二个类有一个int,第三个类是空类。

问题在于:函数算不算进去?

因为类是结构体演化而来的,所以计算的是套用结构体的内存对齐。

空类的内存是1,这是因为空类里面没有东西,为了存这个类的地址,所以必须开辟一个字节用来保存它的地址,但是不存储数据。

C2 和 C3对比就知道,一个是4字节,一个是1字节,因为一个是int,一个是char。

但是C1和C3对比,都是一个字节,但是函数的内存不可能是一字节,所以C1实际上也是空类,这里结合C5和C1也知道函数是不占内存空间的。

所以计算类的大小的时候函数不算进大小,其次就是空类的大小是1,这个1是用来存储类的地址的,其余就是结构体的内存对齐了。


8.类成员函数的this指针

class Cl
{
public:int Add(int ,int);
private:int _a;char _c;
};
int Cl::Add(int x,int y)
{return x + y;
}
int main()
{Cl c1;int ret1 = c1.Add(1, 3);Cl c2;int ret2 = c2.Add(2, 5);return 0;
}

先来看这样一段代码,提问:调用的函数是一个函数吗?还是说调用的是不同对象中的函数?

如果调用的是不同的函数,也就是内存为函数开辟了两个函数栈帧,那么汇编代码中的反汇编call对应的函数的地址应该是不同的,那么:

在汇编代码中,对应的地址是一样的,说明调用的函数是一个,这也可以解释前面的为什么空类里面有一个函数的时候对应的大小却是1,因为函数放在的区域是公共代码段,所以计算大小的时候函数的大小不算在里面。

那么问题来了,函数只有一个,系统是怎么使用函数的,一个函数怎么应用不同的参数呢?

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

void Cl::Print(int x)
{cout << x << endl;
}
void Cl::Print(Cl* this,int x)
{cout << this->x << endl;
}

就像如此,通过指针来实现各种操作,也就达到了一个函数多个对象使用的目的。

但是this指针实现的时候都是透明的,我们不需要手动参与,编译器会自己实现。

下面来看两个关于this指针的题目:

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

问:编译结果是否正常?

第一个代码的编译结果是正常的,虽然类指针是空指针,但是访问的不是成员变量,那么实际上的空指针就不会用上,那么this指针是空指针是没问题的,没用到空指针程序就不会崩溃。

第二个代码就会崩溃,因为this指针接收了传过来的空指针,我们又用到了这个空指针访问成员变量,程序就会崩溃了。

	void Print(){cout << this << endl;//cout << _a << endl;}

this指针打印出来就是空的。


感谢阅读!

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

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

相关文章

vue+springboot多角色登录

①前端编写 将Homeview修改为manager Manager&#xff1a; <template><div><el-container><!-- 侧边栏 --><el-aside :width"asideWidth" style"min-height: 100vh; background-color: #001529"><div style"h…

百度文库验证码识别

一、前言 百度出了如图所示的验证码&#xff0c;需要拖动滑块&#xff0c;与如图所示的曲线轨迹进行重合。经过不断研究&#xff0c;终于解决了这个问题。我把识别代码分享给大家。 下面是使用selenium进行验证的&#xff0c;这样可以看到轨迹滑动的过程&#xff0c;如果需要…

亚马逊店铺引流:海外云手机的利用方法

在电商业务蓬勃发展的当下&#xff0c;亚马逊已经成为全球最大的电商平台之一&#xff0c;拥有庞大的用户群和交易量。在激烈的市场竞争中&#xff0c;如何有效地吸引流量成为亚马逊店铺经营者所关注的重点。海外云手机作为一项新兴技术工具&#xff0c;为亚马逊店铺的流量引导…

页面转word的那些事

背景 有些时候需要将页面内容或者是页面的数据通过word进行下载&#xff0c;以方便客户进行二次编辑&#xff0c;而不是直接导出图片或者是pdf。 想在页面端点击下载成word&#xff0c;那必然需要服务端来进行读写文件&#xff0c;无论是你后端编辑好的内容流&#xff0c;还是…

从头开发一个RISC-V的操作系统(五)汇编语言编程

文章目录 前提RISC-V汇编语言入门RISC-V汇编指令总览汇编指令操作对象汇编指令编码格式add指令介绍无符号数 练习参考链接 目标&#xff1a;通过这一个系列课程的学习&#xff0c;开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提 这个系列的大部分文章和知识来自于…

VMware Intel i5-10400 安装Mac 14 Sonoma

目录 安装完后的效果安装前的准备创建虚拟机创建虚拟机&#xff0c;选择典型安装。选择ISO文件选择系统类型命名虚拟机设置磁盘完成 配置虚拟机文件修改配置文件 第一次运行虚拟机选择语言选择磁盘工具格式磁盘安装macOS Sonoma 其他问题登录Apple帐户 &#xff1a; MOBILEME_C…

单点登录系统设计

一、介绍 token鉴权最佳的实践场景就是在单点登录系统上。 在企业发展初期&#xff0c;使用的后台管理系统还比较少&#xff0c;一个或者两个。 以电商系统为例&#xff0c;在起步阶段&#xff0c;可能只有一个商城下单系统和一个后端管理产品和库存的系统。 随着业务量越来…

药店药品进销存管理系统软件可以对有效期管理查询以及对批号库存管理

药店药品进销存管理系统软件可以对有效期管理查询以及对批号库存管理 一、前言 以下软件操作教程以&#xff0c;佳易王药店药品进销存管理软件为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 软件可以对药品有效期进行管理查询&#xff0c;可以…

【C++进阶】哈希表(哈希函数、哈希冲突、开散列、闭散列)

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;c大冒险 总有光环在陨落&#xff0c;总有新星在闪烁 引言&#xff1a; 我们之前…

【Frida】【Android】 10_爬虫之WebSocket协议分析

&#x1f6eb; 系列文章导航 【Frida】【Android】01_手把手教你环境搭建 https://blog.csdn.net/kinghzking/article/details/136986950【Frida】【Android】02_JAVA层HOOK https://blog.csdn.net/kinghzking/article/details/137008446【Frida】【Android】03_RPC https://bl…

实现第一个动态链接库 游戏插件 成功在主程序中运行 dll 中定义的类

devc 5.11编译环境 dll编译环境设置参考 Dev c C语言实现第一个 dll 动态链接库 创建与调用-CSDN博客 插件 DLL代码和主程序代码如下 注意 dll 代码中的class 类名需要 和主程序 相同 其中使用了函数指针和强制类型转换 函数指针教程参考 以动态库链接库 .dll 探索结构体…

HBase详解(2)

HBase 结构 HRegion 概述 在HBase中&#xff0c;会从行键方向上对表来进行切分&#xff0c;切分出来的每一个结构称之为是一个HRegion 切分之后&#xff0c;每一个HRegion会交给某一个HRegionServer来进行管理。HRegionServer是HBase的从节点&#xff0c;每一个HRegionServ…

elementPlus el-table动态列扩展及二维表格

1、循环列数据源&#xff0c;动态生成列 <template><div><el-table ref"table" :data"pageData.tableData" stripe style"width: 100%"><el-table-column v-for"column in pageData.columns" :key"column.p…

尚硅谷html5+css3(1)html相关知识

1.基本标签&#xff1a; <h1>最大的标题字号 <h2>二号标题字号 <p>换行 2.根标签<html> 包括<head>和<body> <html><head><title>title</title><body>body</body></head> </html> 3…

162 Linux C++ 通讯架构实战16,UDP/TCP协议的优缺点,使用环境对比。UDP 服务器开发

UDP/TCP协议的优缺点 TCP :面向连接的&#xff0c;可靠数据包传输。对于不稳定的网络层&#xff0c;采取完全弥补的通信方式。丢包重传 优点&#xff1a;稳定&#xff0c;数据流量稳定&#xff0c;速度稳定&#xff0c;顺序稳定 缺点&#xff1a;传输速度慢&…

青藏铁路双寨物流基地扩能改造工程接触网第一杆成功组立

4月2日凌晨&#xff0c;随着吊钩缓缓落下&#xff0c;在中铁电气化局北京电化公司现场作业人员的紧张操作下&#xff0c;青藏铁路双寨物流基地扩能改造工程首根接触网支柱稳稳落在基础上&#xff0c;标志着双寨物流基地扩能改造进入全面施工阶段。 双寨物流基地扩能改造工程包含…

Part1.Transformer架构

构成&#xff1a; 【手把手教你用Pytorch代码实现Transformer模型&#xff01;从零解读(Pytorch版本&#xff09;-哔哩哔哩】 https://b23.tv/o283hzU

JavaScript逆向爬虫——使用Python模拟执行JavaScript

使用Python模拟执行JavaScript 通过一些调试&#xff0c;我们发现加密参数token是由encrypt方法产生的。如果里面的逻辑相对简单的话&#xff0c;那么我们可以用Python完全重写一遍。但是现实情况往往不是这样的&#xff0c;一般来说&#xff0c;一些加密相关的方法通常会引用…

摄像头校准漫反射板提高识别物体

摄像头校准漫反射板是一种用于摄像头校准的重要工具。在摄像头成像过程中&#xff0c;由于各种因素的影响&#xff0c;如光线、角度、镜头畸变等&#xff0c;会导致摄像头成像出现偏差。为了消除这些偏差&#xff0c;提高摄像头的成像质量&#xff0c;需要使用摄像头校准漫反射…

从头开发一个RISC-V的操作系统(四)嵌入式开发介绍

文章目录 前提嵌入式开发交叉编译GDB调试&#xff0c;QEMU&#xff0c;MAKEFILE练习 目标&#xff1a;通过这一个系列课程的学习&#xff0c;开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提 这个系列的大部分文章和知识来自于&#xff1a;[完结] 循序渐进&#x…