c++类与对象

文章目录

  • 前言
  • 一、
    • 1、类的引入
    • 2、类的定义
    • 3、类的访问限定符及封装
    • 4、类的实例化
    • 5、类对象模型
    • 6、this指针
    • 7、封装


前言

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。


一、

1、类的引入

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数。

struct Stack
{void Init(int n){a = (int*)malloc(sizeof(int) * n);if (nullptr == a){perror("malloc申请空间失败\n");return;}capacity = n;size = 0;}void Push(int x){if ((size == capacity) && (size != 0)){a = (int*)realloc(a,sizeof(int) * (2*capacity));}a[size] = x;size++;}int* a;int size;int capacity;
};int main()
{Stack s;s.Init(4);s.Push(1);s.Push(2);s.Push(3);s.Push(4);s.Push(5);return 0;
}

2、类的定义

虽然在c++中可以使用struct来定义类,但是在C++中更喜欢用class来代替。
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。

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

类的两种定义方式

(1). 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

class Stack
{
public:void Init(int n = 4){a = (int*)malloc(sizeof(int) * n);if (nullptr == a){perror("malloc申请空间失败\n");return;}capacity = n;size = 0;}void Push(int x){if ((size == capacity) && (size != 0)){a = (int*)realloc(a,sizeof(int) * (2*capacity));}a[size] = x;size++;}
private:int* a;int size;int capacity;
};int main()
{Stack s;s.Init(4);s.Push(1);s.Push(2);s.Push(3);s.Push(4);s.Push(5);return 0;
}

(2). 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::。因为类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
需要注意的是在定义类成员函数时要使用Stack::,即告诉编译器Init方法不是一个全局方法,是在Stack类中的方法。
在这里插入图片描述

3、类的访问限定符及封装

在上面定义类的成员函数时,我们可以发现在前面加上了public:修饰,如果不加的话就会显示该类的成员函数不可以访问。这时因为类的访问限定问题。例如如果我们创建了一个类,该类有些重要的函数我们不希望在其他地方被调用,此时我们就可以使用类的访问限定符来修饰该函数。用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
访问限定符分为三类
(1). public (公有)
(2). protected (保护)
(3). private (私有)

访问限定符说明

  1. public修饰的成员在类外可以直接被访问
  2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到 } 即类结束。
  5. class的默认访问权限为private,struct为public(因为struct要兼容C)
    注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
    所以我们就可以理解为什么前面写的代码中需要在类的成员函数前面加上public修饰,因为类中的默认访问权限都为private,即在外面不能访问类的成员函数,所以要将访问权限改为public,这样才能在外面访问到这些成员函数。
    在这里插入图片描述

4、类的实例化

用类类型创建对象的过程,称为类的实例化
(1). 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没
有分配实际的内存空间来存储它。
(2). 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量。
可以理解为类的定义就是设计出一个楼房的设计图,此时只有一个设计图,该设计图不占用土地,即类的定义不占用空间,类的实例化就是根据这个设计图来盖出来一栋楼房,此时该楼房占用了土地。一个类可以实例化多个对象就是根据这一个设计图可以盖出来很多楼房。

class Date
{
public://成员函数void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private://成员变量/类的属性int _year;int _month;int _day;
};
int main()
{//直接使用类名来访问里面的属性或方法是错误的//Date._year;   //因为此时没有给该类开辟空间// 类对象实例化 -- 开空间Date d1;Date d2;//d1._year = 200;  //因为该Date类的属性都为private,所以不可以在外面修改d1.Init(2023, 9, 8);return 0;
}

5、类对象模型

一个类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?
可以看到使用sizeof()来计算类的大小只是计算了类的成员变量的大小,并没有计算类的成员函数的大小。
在这里插入图片描述
为什么需要这样存储呢?这是因为每个对象中成员变量的值是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类实例化多个对象时,每个对象中都会保存一份函数代码,相同代码保存多次,浪费空间。所以在类实例化为对象时,每个对象的成员变量不一样,所以独立存储,但是每个对象调用的成员函数是一样的,所以将成员函数放到共享公共区域(代码段)。
下面的图片中可以看到当类中没有成员变量时,该类也占用了1个字节空间,这1字节空间并不存储有效数据,而是占位用,标识该对象被实例化定义了出来。因为如果不标识的话,如果调用A2中的f2方法就没法调用了,因为根本没有空间,不知道在哪里调用。
在这里插入图片描述
结论
一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐。注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

6、this指针

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;Date d2;d1.Init(2023, 9, 8);d2.Init(2021, 6, 6);d1.Print();d2.Print();return 0;
}

在这里插入图片描述
通过上面的代码和运行结果我们可以看到不同的对象调用Print()方法打印出来的数据是不一样的,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对呢?

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

this指针的特性
(1). this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
(2). 只能在“成员函数”的内部使用。
(3). this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
(4). this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
在这里插入图片描述
那么this指针存在哪里呢?
答:栈里面,因为this指针实际就是类成员函数的形参,而当调用函数时,函数的形参都存在栈里面,所以this指针也在栈里面,this指针本质就是一个类指针,只不过该指针不需要我们自己定义,而是编译器自动给我们增加的一个隐藏指针参数,我们只需要知道this指针的存在,然后使用它即可。

空指针问题
在下面的代码中,对空指针调用func()函数可以正常执行,而对空指针调用Init和Print函数会报空指针异常,这是因为在Init函数中对this指针进行了解引用,而this指针此时是一个nullptr空指针,所以对空指针解引用会报错。而ptr->func()和(*ptr).func()不报错是因为没有对该空指针进行解引用,编译器解引用时会先判断右边要访问的内容在不在这个对象里面,如果在这个对象里面才会解引用,而不在这个对象里面就不会进行解引用,而func()函数的本质不在对象里面,所以没有解引用。

class Date
class Date
{
public://成员函数//void Init(Date* this, int year, int month, int day)void Init(int year, int month, int day){this->_year = year;this->_month = month;this->_day = day;}//void Print(Date* this)void Print(){cout << this->_year << " " << this->_month << " " << this->_day << endl;}void func(){cout << "func()" << endl;}private://成员变量/类的属性int _year;int _month;int _day;
};
int main()
{Date d1;Date d2;d1.Init(2023, 9, 8);d2.Init(2021, 6, 6);d1.Print();d2.Print();Date* ptr = nullptr;ptr->func();  //正常运行(*ptr).func();  //正常运行ptr->Init(2023, 3, 4);  //报错return 0;
}

7、封装

面向对象的三大特性:封装、继承、多态
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来
和对象进行交互。
例如在c语言中我们实现栈操作时,数据和方法是分离的,即在使用时,我们先需要创建一个Stack结构体变量,然后给该变量初始化,然后调用一系列方法对栈进行操作,并且这些方法什么位置都可以调用,可以在任何时候使用int top = StackTop(&st)来获取栈的栈顶元素,这样是不安全的。

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>typedef int STDataType;typedef struct Stack
{STDataType* data;int top;int capacity;
}Stack;void StackInit(Stack* ps);void StackPush(Stack* ps, STDataType x);void StackPop(Stack* ps);bool StackIsEmpty(Stack* ps);STDataType StackTop(Stack* ps);
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"void StackInit(Stack* ps)
{assert(ps);ps->data = NULL;ps->top = 0;ps->capacity = 0;
}void CheckCapacity(Stack* ps)
{assert(ps);int newCapacity = ps->capacity = 0 ? 4 : ps->capacity * 2;if (ps->top == ps->capacity){STDataType* tmp = (STDataType*)realloc(ps->data, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}ps->data = tmp;ps->capacity = newCapacity;}
}void StackPush(Stack* ps, STDataType x)
{assert(ps);CheckCapacity(ps);ps->data[ps->top] = x;ps->top++;
}void StackPop(Stack* ps)
{assert(ps);assert(!StackIsEmpty(ps));ps->top--;
}bool StackIsEmpty(Stack* ps)
{assert(ps);if (ps->top == 0){return true;}return false;
}STDataType StackTop(Stack* ps)
{assert(ps);assert(!StackIsEmpty(ps));return ps->data[ps->top - 1];
}
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"int main()
{Stack st;StackInit(&st);StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);StackPush(&st, 5);StackPush(&st, 6);printf("%d\n", StackTop(&st));StackPop(&st);printf("%d\n", StackTop(&st));return 0;
}

而在c++中栈可以这样实现。

#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>typedef int STDataType;typedef struct Stack
{STDataType* data;int top;int capacity;
}Stack;void StackInit(Stack* ps);void StackPush(Stack* ps, STDataType x);void StackPop(Stack* ps);bool StackIsEmpty(Stack* ps);STDataType StackTop(Stack* ps);
#include"Stack.h"void StackInit(Stack* ps)
{assert(ps);ps->data = NULL;ps->top = 0;ps->capacity = 0;
}void CheckCapacity(Stack* ps)
{assert(ps);int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;if (ps->top == ps->capacity){STDataType* tmp = (STDataType*)realloc(ps->data, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}ps->data = tmp;ps->capacity = newCapacity;}
}void StackPush(Stack* ps, STDataType x)
{assert(ps);CheckCapacity(ps);ps->data[ps->top] = x;ps->top++;
}void StackPop(Stack* ps)
{assert(ps);assert(!StackIsEmpty(ps));ps->top--;
}bool StackIsEmpty(Stack* ps)
{assert(ps);if (ps->top == 0){return true;}return false;
}STDataType StackTop(Stack* ps)
{assert(ps);assert(!StackIsEmpty(ps));return ps->data[ps->top - 1];
}
#include"Stack.h"int main()
{Stack st;StackInit(&st);StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);StackPush(&st, 5);StackPush(&st, 6);printf("%d\n", StackTop(&st));StackPop(&st);printf("%d\n", StackTop(&st));return 0;
}

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

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

相关文章

MySQL聚簇索引与非聚簇索引

分析&回答 当数据库一条记录里包含多个字段时&#xff0c;一棵B树就只能存储主键&#xff0c;如果检索的是非主键字段&#xff0c;则主键索引失去作用&#xff0c;变成顺序查找了。这时应该在第二个要检索的列上建立第二套索引。这个索引由独立的B树来组织。有两种常见的方…

2023国赛数学建模B题思路分析 - 多波束测线问题

# 1 赛题 B 题 多波束测线问题 单波束测深是利用声波在水中的传播特性来测量水体深度的技术。声波在均匀介质中作匀 速直线传播&#xff0c; 在不同界面上产生反射&#xff0c; 利用这一原理&#xff0c;从测量船换能器垂直向海底发射声波信 号&#xff0c;并记录从声波发射到…

vue基于Echarts、百度地图MapVGL实现可视化大屏数据展示

一、布局 常见的大屏数据展示布局&#xff0c;一般会将地图作为整个屏幕的背景&#xff0c;在地图上以九宫格布局展示各类数据图表。实现这一效果可以使地图的z-index1,在地图上的图表等z-index>1,下面会详细描述这种设计该如何实现&#xff1a; <div style"width…

嵌入式开发笔试面试

C语言部分&#xff1a; 1.gcc的四步编译过程 1.预处理 展开头文件&#xff0c;删除注释、空行等无用内容&#xff0c;替换宏定义。 gcc -E hello.c -o hello.i 2.编译 检查语法错误&#xff0c;如果有错则报错&#xff0c;没有错误则生成汇编文件。 gcc -S hello.i -o h…

C++ vector模拟实现

目录 使用insert时迭代器失效使用erase时迭代器失效使用memcpy浅拷贝的问题调用最匹配的函数可能出现的问题模拟实现vector 使用insert时迭代器失效 在模拟vector插入的时候会遇到扩容后pos失效的问题&#xff0c;需要更新pos vector():_start(nullptr), _finish(nullptr), _e…

解耦只是一个巧合?

本文分享一篇在IJCAI2023看到的文章&#xff1a;Overlooked Implications of the Reconstruction Loss for VAE Disentanglement 首先回顾下VAE&#xff0c;其loss函数有两项&#xff0c;一项是重构误差&#xff0c;另一项是正则项&#xff1a; L r e c ( x , x ^ ) E q ϕ (…

QT(9.1)对话框与事件处理

作业&#xff1a; 1. 完善登录框 点击登录按钮后&#xff0c;判断账号&#xff08;admin&#xff09;和密码&#xff08;123456&#xff09;是否一致&#xff0c;如果匹配失败&#xff0c;则弹出错误对话框&#xff0c;文本内容“账号密码不匹配&#xff0c;是否重新登录”&…

23062C++QT day2

封装一个结构体&#xff0c;结构体中包含一个私有数组&#xff0c;用来存放学生的成绩&#xff0c;包含一个私有变量&#xff0c;用来记录学生个数&#xff0c; 提供一个公有成员函数&#xff0c;void setNum(int num)用于设置学生个数 提供一个公有成员函数&#xff1a;void…

FPGA实战小项目3

基于FPGA的波形发生器 基于FPGA的波形发生器 基于FPGA的beep音乐播放器设计 基于FPGA的beep音乐播放器设计 基于FPGA的cordic算法实现DDS sin和cosine波形的产生 基于FPGA的cordic算法实现DDS sin和cosine波形的产生

无需公网IP,实现远程访问群晖Drive并挂载为电脑磁盘的方法

文章目录 前言1.群晖Synology Drive套件的安装1.1 安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用 2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用 3. 结语 前言 群晖作为专业的数据存储中心&…

利用MQ实现mysql与elasticsearch数据同步

流程 1.声明exchange、queue、RoutingKey 2. 在hotel-admin中进行增删改&#xff08;SQL&#xff09;&#xff0c;完成消息发送 3. 在hotel-demo中完成消息监听&#xff0c;并更新elasticsearch数据 4. 测试同步 1.引入依赖 <!--amqp--> <dependency><groupId&…

欧洲云巨头OVHcloud收购边缘计算专家 gridscale

边缘计算社区近日获悉&#xff0c;欧洲云巨头OVHcloud已进入全面收购德国公司 gridscale 的谈判&#xff0c;该公司是一家专门从事超融合基础设施的软件提供商。 此次战略收购将标志着 OVHcloud 的另一个重要里程碑&#xff0c;使该集团能够显着加速其地理部署&#xff0c;并进…

lv3 嵌入式开发-6 linux shell脚本编程(概念、变量、语句)

1 Shell脚本概述 1.1Shell脚本概述 Shell脚本是利用 shell 的功能所写的一个程序。这个程序是使用纯文本文件&#xff0c;将一些 shell 的语法与命令&#xff08;含外部命令&#xff09;写在里面&#xff0c;搭配正则表达式、管道命令与数据流重定向等功能 1.2Shell脚本编写流…

[JAVA] byte与int的类型转换案例剖析

总结&#xff1a; ①没有byte的字面值&#xff0c;赋值时需要强制转换类型 ②涉及运算&#xff0c;系统自动进行类型升级&#xff0c;由此用final修饰&#xff0c;代表这是一个不会更改值的常量&#xff0c;通过编译 感受&#xff1a;还是用int吧&#xff0c;自动类型转换太复…

Java程序员所需Javascript知识

它是一种脚本语言&#xff0c;可以用来更改页面内容&#xff0c;控制多媒体&#xff0c;制作图像、动画等等 js 代码位置 <script>// js 代码 </script>引入 js 脚本&#xff0c;在js脚本中写js代码 <script src"js脚本路径"></script>注…

滑动窗口实例7(串联所有单词的子串)

题目&#xff1a; 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c;…

OpenCV之形态学操作

形态学操作包含以下操作&#xff1a; 腐蚀 (Erosion)膨胀 (Dilation)开运算 (Opening)闭运算 (Closing)形态梯度 (Morphological Gradient)顶帽 (Top Hat)黑帽(Black Hat) 其中腐蚀和膨胀操作是最基本的操作&#xff0c;其他操作由这两个操作变换而来。 腐蚀 用一个结构元素…

简述视频智能分析EasyCVR视频汇聚平台如何通过“AI+视频融合”技术规避八大特殊作业风险

视频智能分析EasyCVR视频汇聚平台可以根据不同的场景需求&#xff0c;让平台在内网、专网、VPN、广域网、互联网等各种环境下进行音视频的采集、接入与多端分发。在视频能力上&#xff0c;视频云存储平台EasyCVR可实现视频实时直播、云端录像、视频云存储、视频存储磁盘阵列、录…

noetic 怎么下载robotiq_modbus_tcp 从而使用robotiq二指夹爪

1&#xff0c;会科学上网 2&#xff0c; # Modbus TCP rosdep install robotiq_modbus_tcp3&#xff0c;报错 4&#xff0c;去robotiq的功能包里面找两个大爷&#xff08;tmd&#xff09; 分别修改package.xml 本来是python &#xff0c; 然后分别修改成python3 然后参考其他…

aws-msk-托管kafka集群的简单使用(VPC内部访问:无验证和SASL认证)

1.使用控制台创建即可 根据实例类型创建需要至少15分以上&#xff0c;可以提前创建好ec2实例和Secrets Manager,一会会使用到 2. 创建Secrets Manager &#xff08;使用无认证时请跳过&#xff09; 官方文档&#xff1a;https://docs.aws.amazon.com/zh_cn/msk/latest/deve…