03 类和对象 1

目录

  1. 面向过程和面向对象初步认识
  2. 类的引入
  3. 类的定义
  4. 类的访问限定符及封装
  5. 类的作用域
  6. 类的实例化
  7. 类的对象大小的计算
  8. 类成员函数的this指针

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

c语言是面向过程的,关注的是过程,分析求解问题的步骤,通过函数调用逐步解决问题

如洗衣服的过程,面向过程会按照步骤一步步来
在这里插入图片描述
面向对象关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成
洗衣服总共有4个对象,人、衣服、洗衣粉、洗衣机
过程:人将衣服放入,倒入洗衣粉,启动洗衣机,完成甩干
整个过程主要是这4个对象之间交互完成,不需要关注具体如何洗衣服

2. 类的引入

c语言结构体中只能定义变量,c++中不仅可以定义变量,也可以定义函数,之前的栈以c++的方式定义

typedef int DataType;
struct Stack
{
 void Init(size_t capacity)
 {
 _array = (DataType*)malloc(sizeof(DataType) * capacity);
 if (nullptr == _array)
 {
 perror("malloc申请空间失败");
 return;
 }
 _capacity = capacity;
 _size = 0;
 }
 void Push(const DataType& data)
 {
 // 扩容
 _array[_size] = data;
 ++_size;
 }
 DataType Top()
 {
 return _array[_size - 1];
 }
 void Destroy()
 {
 if (_array)
 {
 free(_array);
 _array = nullptr;
 _capacity = 0;
 _size = 0;
 }
 }
 DataType* _array;
 size_t _capacity;
 size_t _size;
};
int main()
{
 Stack s;
 s.Init(10);
 s.Push(1);
 s.Push(2);
 s.Push(3);
 cout << s.Top() << endl;
 s.Destroy();
 return 0;
}

在c++中,结构体struct关键字用class代替

3. 类的定义

class classname
{
//类体: 由成员函数和成员变量组成

}; //一定要注意后面的分号

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

类体中内容称为类的成员:类中的变量称为类的属性和成员变量,类中的函数称为类的方法或者成员函数

类的两种定义方式:
1.声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当做内联函数处理
在这里插入图片描述

2.类声明放在头文件,成员函数定义在.cpp中,注意:成员函数名前需要加类名
在这里插入图片描述

一般情况下采用第二种方法

成员变量命名规则:
类的成员变量前面会加_或m来和成员函数的形参做区分

// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
 void Init(int year)
 {
 // 这里的year到底是成员变量,还是函数形参?
 year = year;
 }
private:
 int year;
};
// 所以一般都建议这样
class Date
{
public:
 void Init(int year)
 {
 _year = year;
 }
private:
 int _year;
};
// 或者这样
class Date
{
public:
 void Init(int year)
 {
 mYear = year;
 }
private:
 int mYear;
};
// 其他方式也可以的,主要看公司要求。一般都是加个前缀或者后缀标识区分就行。

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

4.1 访问限定符

c++实现封装的方式:用类将对象的属性和方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用
在这里插入图片描述【访问限定符说明】
1.public修饰的成员在类外可以直接被访问
2.protected和private修饰的成员在类外不能直接被访问(此外protected和private是类似的)
3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止
4.如果后面没有访问限定符,作用域就到}类结束
5.class的默认访问权限为private,struct为public(因为struct要兼容c)

访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

问题:c++中struct和class的区别是什么?
解答:c++需要兼容c语言,所以c++中struct可以当成结构体使用。另外c++中struct还可以用来定义类。和class定义类一样,区别是struct定义的类默认访问权限是public,class默认访问权限是private。注意:继承和模板参数列表位置,struct和class也有区别,后续介绍

4.2 封装

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

在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?
封装:将数据和操作数据的方法有机结合,隐藏对象属性和实现细节,只对外公开接口来和对象进行交互

封装本质上是一种管理,让用户更方便使用类。例如电脑提供的开关机键、显示器、键盘输入等就是封装的功能,但实际上真正工作的是CPU、显存内存等元件

在这里插入图片描述
在这里插入图片描述
对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部设计等,只需要通过鼠标和键盘交互。所以计算机出厂时,在外部套上壳子,将内部细节隐藏起来,提供各种接口操作交互

c++语言实现封装,通过将数据和操作方法结合,访问权限来隐藏内部实现细节,控制可以在类外直接调用的方法

5. 类的作用域

类定义了一个新的作用域,所有成员都在类作用域中,类外定义成员时,使用::作用域操作符说明成员属于哪个类域

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

6. 类的实例化

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

1.类是对对象描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有书籍分配内存空间,比如:填写的个人信息表格,就可以看做一个类

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

int main()
{
 Person._age = 100;   // 编译失败:error C2059: 语法错误:“.”
 return 0;
}

直接访问类的成员时错误的,没有空间,必须实例化对象才有具体的年龄

3.做个比方,类实例化对象就像建筑设计图,类就是设计图,只设计出需要什么东西,但没有实际的建筑,只是一个设计,实例化的对象才存储数据,占用物理空间

在这里插入图片描述

7. 类对象模型

7.1 如何计算类对象的大小

class A
{
public:
void PrintA()
{
   cout<<_a<<endl;
}
private:
char _a;
};

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

7.2 类对象的存储方式

//有函数有变量
class A
{
public:
	void fun();
private:
	int _a;
};
//只有函数
class A1
{
public:
	void fun();
};
//空类
class A2
{

};
	cout <<sizeof(A)<<"\r\n";
	cout << sizeof(A1) << "\r\n";
	cout << sizeof(A2) << "\r\n";

输出上面三个类的大小:
在这里插入图片描述
空类大小也是1,给1个字节来标识

结论:一个类的大小,实际是这个类中成员变量的和,内存对齐原则。由于成员函数可能被重复调用,所以放在公共代码区

7.3 结构体内存对齐规则

1.第一个成员在与结构体偏移量为0的地址处
2.其他成员变量对齐到某个数字(对齐数)的整数倍地址处
注意: 对齐数=编译器默认的一个对齐数与该成员大小的较小值
VS中默认对齐数为8
3.结构体总大小: 最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有对齐数(含嵌套结构体的对齐数)的整数倍

8. this指针

8.1 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, d2;
 d1.Init(2022,1,11);
 d2.Init(2022, 1, 12);
 d1.Print();
 d2.Print();
 return 0;
}

对于上述类,有个疑问
Date类有Init和Print两个成员函数,没有关于不同对象的区分,d1调用Init函数时,如何直到设置d1对象,不是设置d2对象?

c++中通过引入this指针解决,给每个非静态的成员函数增加了一个隐藏的指针参数,指向该对象,函数体所有成员变量的操作,都是指针去访问,只不过由编译器自动完成

8.2 this指针的特性

1.this指针的类型:类类型*const,不能给this赋值
2.只能在“成员函数”内部使用
3.this指针本质是“成员函数”的形参,对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针
4.this指针时“成员函数”第一个隐含的形参,一般情况由编译器传递,vs中通过ecx寄存器传递

在这里插入图片描述

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
 void Print()
 {
 cout << "Print()" << endl;
 }
private:
 int _a;
};
int main()
{
 A* p = nullptr;
 p->Print();
 return 0;
}
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{ 
public:
    void PrintA() 
   {
        cout<<_a<<endl;
   }
private:
 int _a;
};
int main()
{
    A* p = nullptr;
    p->PrintA();
    return 0;
}

第一个正常运行,第二个会崩溃,第一个对printa的调用并没有访问到成员变量,所以空指针也不会报错。第二个访问了变量_a,上面说了变量的访问都是用this指针完成,所以崩溃

8.3 c++语言实现stack对比

c语言实现

typedef int DataType;
typedef struct Stack
{
	DataType* array;
	int capacity;
	int size;
}Stack;
void StackInit(Stack * ps)
{
	assert(ps);
	ps->array = (DataType*)malloc(sizeof(DataType) * 3);
	if (NULL == ps->array)
	{
		assert(0);
		return;
	}
	ps->capacity = 3;
 ps->size = 0;
}
void StackDestroy(Stack* ps)
{
 assert(ps);
 if (ps->array)
 {
 free(ps->array);
 ps->array = NULL;
 ps->capacity = 0;
 ps->size = 0;
 }
}
void CheckCapacity(Stack* ps)
{
 if (ps->size == ps->capacity)
 {
 int newcapacity = ps->capacity * 2;
 DataType* temp = (DataType*)realloc(ps->array, 
newcapacity*sizeof(DataType));
 if (temp == NULL)
 {
 perror("realloc申请空间失败!!!");
 return;
 }
 ps->array = temp;
 ps->capacity = newcapacity;
 }
}
void StackPush(Stack* ps, DataType data)
{
 assert(ps);
 CheckCapacity(ps);
 ps->array[ps->size] = data;
 ps->size++;
}
int StackEmpty(Stack* ps)
{
 assert(ps);
 return 0 == ps->size;
}
void StackPop(Stack* ps)
{
 if (StackEmpty(ps))
 return;
 ps->size--;
}
DataType StackTop(Stack* ps)
{
 assert(!StackEmpty(ps));
 return ps->array[ps->size - 1];
}
int StackSize(Stack* ps)
{
 assert(ps);
 return ps->size;
}
int main()
{
 Stack s;
 StackInit(&s);
 StackPush(&s, 1);
 StackPush(&s, 2);
 StackPush(&s, 3);
 StackPush(&s, 4);
 printf("%d\n", StackTop(&s));
 printf("%d\n", StackSize(&s));
 StackPop(&s);
 StackPop(&s);
 printf("%d\n", StackTop(&s));
 printf("%d\n", StackSize(&s));
 StackDestroy(&s);
 return 0;
}

可以看到,c语言实现时,stack相关函数有以下共性:

  • 每个函数第一个参数都是stack*
  • 函数必须要对第一个参数检测,因为该参数可能为空
  • 函数中都是通过stack*操作栈的
  • 调用时必须传递stack结构体变量地址

结构体只能定义存放数据结构,操作数据的方法不能放在结构体中,数据和操作方法是分开的,涉及大量指针操作时稍不注意就会出错

c++实现

typedef int DataType;
class Stack
{
public:
 void Init()
 {
 _array = (DataType*)malloc(sizeof(DataType) * 3);
 if (NULL == _array)
 {
 perror("malloc申请空间失败!!!");
 return;
 }
 _capacity = 3;
 _size = 0;
 }
 void Push(DataType data)
 {
 CheckCapacity();
 _array[_size] = data;
 _size++;
 }
 void Pop()
 {
 if (Empty())
 return;
 _size--;
 }
 DataType Top(){ return _array[_size - 1];}
 int Empty() { return 0 == _size;}
 int Size(){ return _size;}
 void Destroy()
 {
 if (_array)
 {
 free(_array);
 _array = NULL;
 _capacity = 0;
 _size = 0;
 }
 }
private:
 void CheckCapacity()
 {
 if (_size == _capacity)
 {
 int newcapacity = _capacity * 2;
 DataType* temp = (DataType*)realloc(_array, newcapacity *
sizeof(DataType));
 if (temp == NULL)
 {
 perror("realloc申请空间失败!!!");
 return;
 }
 _array = temp;
 _capacity = newcapacity;
 }
 }
private:
 DataType* _array;
 int _capacity;
 int _size;
};
int main()
{
 Stack s;
 s.Init();
s.Push(1);
 s.Push(2);
 s.Push(3);
 s.Push(4);
 
 printf("%d\n", s.Top());
 printf("%d\n", s.Size());
 s.Pop();
 s.Pop();
 printf("%d\n", s.Top());
 printf("%d\n", s.Size());
 s.Destroy();
 return 0;
}

c++通过类可以将数据和操作方法完美结合,通过访问权限控制哪些方法可以被调用,封装。更符合对一件事物的认知,每个方法不用传递stack参数,c++中stack是编译器维护的,c语言需要自己维护

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

免费邮件群发工具有哪些?群发邮件的软件?

免费邮件群发软件盘点&#xff01;EDM免费邮件营销软件哪个好&#xff1f; 电子邮件营销已成为许多企业和个人推广的重要手段。而免费邮件群发工具则成为了这些用户节约成本、提高效率的首选。蜂邮将为您详细介绍几款备受欢迎的免费邮件群发工具&#xff0c;帮助您在众多选择中…

从kafka如何保证数据一致性看通常数据一致性设计

一、前言 在数据库系统中有个概念叫事务&#xff0c;事务的作用是为了保证数据的一致性&#xff0c;意思是要么数据成功&#xff0c;要么数据失败&#xff0c;不存在数据操作了一半的情况&#xff0c;这就是数据的一致性。在很多系统或者组件中&#xff0c;很多场景都需要保证…

JVM-JVM调优基础(理论)

申明&#xff1a;文章内容是本人学习极客时间课程所写&#xff0c;作为笔记进行记录&#xff0c;文字和图片基本来源于课程资料&#xff0c;在某些地方会插入一点自己的理解&#xff0c;未用于商业用途&#xff0c;侵删。 原资料地址&#xff1a;课程资料 JVM参数 标准参数 …

- 工程实践 - 《QPS百万级的有状态服务实践》02 - 冷启动和热更新

本文属于专栏《构建工业级QPS百万级服务》 继续上篇《QPS百万级的有状态服务实践》01 - 存储选型实践。如图1架构&#xff0c;我们已经解决了数据生产的问题。 图1 但是我们的服务已经在运行了&#xff0c;并实时处理大量的请求&#xff0c;我们如何把内存中的数据版本更新呢。…

网页中实现打开qq添加群聊

网页中实现打开qq添加群聊 效果 登录qq群管理后台 1. https://qun.qq.com/#/handy-tool/join-group 2 . 复制生成的链接 代码 在html中使用的话就直接粘贴到对应的内容里面就行 如果是添加点击事件的话&#xff1a; <div click"joinQQGroup">添加群聊</…

Vue23 数据监测总结

代码 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>总结数据监视</title><style>button{margin-top: 10px;}</style><!-- 引入Vue --><script type"text/javascript" src"…

mqtt 协议中的 QoS等级介绍

一、QoS等级 MQTT设计了一套保证消息稳定传输的机制&#xff0c;包括消息应答、存储和重传。在这套机制下&#xff0c;提供了三种不同层次QoS&#xff08;Quality of Service&#xff09;&#xff1a; QoS0&#xff0c;At most once&#xff0c;至多一次&#xff1b;QoS1&…

OpenAI全新发布的Sora,到底意味着什么?

16日凌晨&#xff0c;OpenAI发布了文本视频的工具&#xff08;text-do-video&#xff09;Sora&#xff0c;整个世界再次被震撼。 Sora的出现&#xff0c;到底意味着什么&#xff1f; 目录 Sora的背景与概述Sora是什么&#xff1f;能为我们做些什么&#xff1f;存在的一些问题 文…

C++ 里设置Expose on Spawn csv 通过 UStruct 导入为 DataTable

一.蓝图里面暴露的设置如下&#xff1a; C 中写法如下&#xff1a; 属性如下&#xff1a; 关卡蓝图中Spawn时会有 参数接口 二. 创建UObject类&#xff0c;并在C中声明结构体。继承FTableRowBase 在Excel里&#xff0c;创建对应csv文件 如果在头文件修改了属性&#xff0c;使用…

情人节官宣频发,白敬亭宋轶等多对情侣陷情风。

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 情人节甜蜜满溢&#xff0c;娱乐圈情侣们争相晒幸福。2024年&…

Rust Vs Go:从头构建一个web服务

Go 和 Rust 之间的许多比较都强调它们在语法和初始学习曲线上的差异。然而&#xff0c;最终的决定性因素是重要项目的易用性。 “Rust 与 Go”争论 Rust vs Go 是一个不断出现的话题&#xff0c;并且已经有很多关于它的文章。部分原因是开发人员正在寻找信息来帮助他们决定下…

CogCopyRegionTool

关于visionpro工具操作原理文章甚少&#xff0c;以下是本人自己查阅visionpro官方文档完成的&#xff1a; “复制区域”工具允许您对单个图像或两个独立的图像执行多个复制操作&#xff1a; 将输入图像的一部分复制到新的输出图像。 1、 将输入图像的一部分复制到现有的目标…

一杯咖啡一根烟,一个bug改一天,让程序员崩溃的43个瞬间

一杯咖啡一根烟&#xff0c;一个bug改一天 新年刚刚开始&#xff0c;我估计大家都还处于打发时间的状态吧&#xff01;让我们来谈谈一些轻松的内容&#xff0c;调整一下心情&#xff0c;希望所有在座的朋友&#xff0c;在2024年能够bug多多&#xff0c;收入多多&#xff0c;美女…

Apache DolphinScheduler中ZooKeeperCDH不兼容问题的解决方案

背景 看到Apache DolphinScheduler社区群有很多用户反馈和讨论这块问题&#xff0c;针对不兼容的问题&#xff0c;不仅需要自己重新编译各一个新包&#xff0c;而且因为默认是使用zk-3.8的配置&#xff0c;所以会出现不兼容问题。使用zk-3.4配置即可适配3.4.x 解决办法&#…

北京地区MySQL培训课程:深度解析查询语句中的WHERE条件设置

MySQL如果在查询时想要获取满足的条件的记录&#xff0c;就需要使用WHERE子句&#xff0c;WHERE子句用于在 MySQL 中过滤查询结果&#xff0c;只返回满足条件的数据记录。 语法格式&#xff1a; SELECT column1, column2, ...FROM table_name WHERE condition; SELECT 列名,…

【Linux】Framebuffer 应用

# 前置知识 LCD 操作原理 在 Linux 系统中通过 Framebuffer 驱动程序来控制 LCD。 Frame 是帧的意思&#xff0c; buffer 是缓冲的意思&#xff0c;这意味着 Framebuffer 就是一块内存&#xff0c;里面保存着一帧图像。 Framebuffer 中保存着一帧图像的每一个像素颜色值&…

才气系统与逻辑系统道装实现的比较

才气系统与逻辑系统道装实现的比较 道装道装思想简介烛火流形学习引擎&#xff0c;流形学习的引入王船山信息熵&#xff0c;简称王船山熵&#xff1b;凝聚态数学可计算函数科学方法道装由来琴语言简介逻辑与才气的逐层比较表格&#xff08;王船山熵&#xff09; 道装 道装思想…

LeetCode 0589.N 叉树的前序遍历:深度优先搜索(DFS)

【LetMeFly】589.N 叉树的前序遍历&#xff1a;深度优先搜索(DFS) 力扣题目链接&#xff1a;https://leetcode.cn/problems/n-ary-tree-preorder-traversal/ 给定一个 n 叉树的根节点 root &#xff0c;返回 其节点值的 前序遍历 。 n 叉树 在输入中按层序遍历进行序列化表…

防火墙 iptables(二)--------------SNAT与DNAT

一、SNAT ①SNAT 应用环境: 局域网主机共享单个公网IP地址接入Internet (私有IP不能在Internet中正常路由) ②SNAT原理: 源地址转换&#xff0c;根据指定条件修改数据包的源IP地址&#xff0c;通常被叫做源映射 数据包从内网发送到公网时&#xff0c;SNAT会把数据包的源IP由…

【Web】CVE-2022-22947 SpringCloud Gateway SpEL漏洞学习

目录 简介 Actuator操作Gateway接口列表 复现流程 漏洞复现 简单原理 简介 Spring Boot Actuator 和 Spring Cloud Gateway 是 Spring 生态系统中的两个关键组件&#xff0c;它们在微服务架构中扮演着不同的角色&#xff0c;下面简要介绍它们之间的关系&#xff1a; Spri…