【类和对象】类的作用域 | 类的实例化 | 类对象模型 | this指针

目录

5.类的作用域

6.类的实例化

6.1成员的声明和定义 

6.2实例化出的对象大小

7.类对象模型❗❗

7.1如何计算类对象的大小

7.2类对象的存储方式猜测

7.3结构体内存对齐规则

7.3.1内存对齐

7.3.2大小端  

8.this指针

8.1this指针的引出

8.2this指针的特性

C和C++实现栈的对比


  • 类,对象,成员变量,变量,成员函数的关系

5.类的作用域

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

  • 类域能解决一定的命名冲突问题
  • 在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
//声明.h
class Person
{
public:void PrintPersonInfo();
private:char _name[20];char _gender[3];int  _age;
};//定义.cpp
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{cout << _name << " " << _gender << " " << _age << endl;
}

6.类的实例化

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

  • 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员。
  • 定义出一个类并没有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个类,来描述具体学生信息。
  •  一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
  • 类是没有空间的,只有类的实例化出的对象才有具体的空间。

举例:

  • 类就像谜语一样,对谜底来进行描述,谜底就是谜语的一个实例。
  • 谜语:"年纪不大,胡子一把,主人来了,就喊妈妈" 谜底:山羊

举例:做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。

 

6.1成员的声明和定义 

  • 无论是成员变量/成员函数在类中,只要还未被实例化成对象,那么它们都是一种声明,因为它们只是类,并不存在空间。
  • 定义并不是给值,而是为类开辟空间,也就是实例化。
  • 定义的时候可以初始化,但是只初始化并不代表定义。
#include<stdlib.h>
#include<iostream>
using namespace std;
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 s;//定义了s.Init(1,1,1);
❌	Date::_year++;//不能因为它只是声明,并没有开辟空间
❌  Date._month = 100;  // 编译失败:error C2059: 语法错误:“.” return 0;
}

6.2实例化出的对象大小

 移步☞类对象的存储方式。

7.类对象模型❗❗

《深度探索C++对象模型》这本书中对对象模型的描述如下:

有两个概念可以解释C++对象模型:

  • 语言中直接支持面向对象程序设计的部分。
  • 对于各种支持的底层实现机制。

对象模型是指在面向对象编程中,为了描述和处理现实世界中的实体、事物或概念所构建的抽象模型。它通常由类、对象、属性、方法等元素组成,用于描述实体之间的关系、结构和行为。


❓❓问题:类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?类对象的存储方式?如何计算一个类的大小? 下面我们来一起探讨。

7.1如何计算类对象的大小

  • 类的实例化=对象
  • sizeof(类名)
  • sizeof(对象)
  • 二者计算都是类实例化的大小,也就是类对象的大小。
  • 一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐
  • 注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象
  • 没有成员变量的类对象大小1字节,标识对象实例化,定义出来存在。
class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;char _i;
};

❓计算下面类对象的大小: sizeof(A1) : ______ sizeof(A2) : ______ sizeof(A3) : ______

#include<stdlib.h>
#include<iostream>
using namespace std;
// 类中既有成员变量,又有成员函数
//内存对齐存储
class A1 {
public:void f1() {}
private:int _a;char _i;
};// 类中仅有成员函数
//标识对象实例化,定义出来存在
class A2 {
public:void f2() {}
};// 类中什么都没有---空类
class A3
{};
int main()
{cout << sizeof(A1) << endl;cout << sizeof(A2) << endl;cout << sizeof(A3) << endl;return 0;
}

7.2类对象的存储方式猜测

类对象的存储方式有两种:

  • 对象中包含类的各个成员(不用)
  • 代码只保存一份,在对象中保存存放代码的地址(以后)
  • 只保存成员变量,成员函数存放在公共的代码段(现阶段)
//现阶段
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}int _year;int _month;int _day;
};int main()
{Date d1;Date d2;//不是一块空间,成员变量各自是各自的空间d1._year;d2._month;//是一块空间,成员函数是一块空间d1.Init(1,1,1);d2.Init(1,1,1);return 0;
}

 【对象中包含类的各个成员】

缺陷:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码(成员函数的地址),相同代码保存多次,浪费空间。那么如何解决呢?


 【代码只保存一份,在对象中保存存放代码的地址】


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

7.3结构体内存对齐规则

1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

  • 注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
  • VS中默认的对齐数为8。

3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。


【面试题】

  • 1. 结构体怎么对齐? 为什么要进行内存对齐?
  • 2. 如何让结构体按照指定的对齐参数进行对齐?能否按照3、4、5即任意字节对齐?
  • 3. 什么是大小端?如何测试某机器大端还是小端,有没有遇到过要考虑大小端的场景。
  • 前面C语言进阶我们也讲解过(结构体)C语言之自定义类型_结构体篇(1)-CSDN博客

7.3.1内存对齐

  • 怎样内存对齐?
  • 类对象的包含了什么,需要包括成员函数吗?
  • 为什么要内存对齐?
  • 可以改变对齐参数吗?

【怎么内存对齐---按照规则】 

类的对象存储空间只包含了成员变量,成员函数在公共区域。 

#include<iostream>
using namespace std;
struct A
{
private:int _a;char _i;
};struct B
{
private:char _i;int _a;
};

 【为什么要内存对齐】

  • 对齐是为了提高效率!
  • 硬件规定CPU一次只能读取4/8个字节。
  • 32/64个线,32/64个位,组成0/1的二进制数。(消耗相同,一次只读取32/64位,4/8个字节)
  • 硬件规定:CPU的控制器只能从整数倍开始读取。起始位置必须是整数倍的位置。
  • 以上规定从而导致了:对齐和不对齐读取存在效率上的问题。
  • 对齐读取的次数比不对齐读取的次数多。

【对齐可以改变编译器默认对齐数的大小】

#include<iostream>
using namespace std;//改变默认对齐数的大小
#pragma pack(1)//相当于不对齐struct A
{
private:int _a;char _i;
};struct B
{
private:char _i;int _a;
};int main()
{cout << sizeof(A) << endl;cout << sizeof(B) << endl;return 0;
}

7.3.2大小端  

8.this指针

8.1this指针的引出

8.2this指针的特性

C和C++实现栈的对比

【C语言】

可以看到,在用C语言实现时,Stack相关操作函数有以下共性:

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

结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据方式是分离开,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出错。 


【C++】感谢祖师爷

  • C++中通过类可以将数据 以及 操作数据的方法进行完美结合。
  • 通过访问权限可以控制那些方法在类外可以被调用,即封装。
  • 在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。
  • 而且每个方法不需要传递Stack*的参数了。
  • 编译器编译之后该参数会自动还原,即C++中 Stack *参数是编译器维护的,C语言中需用用户自己维护。

【C语言实现】

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>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++实现】 

#include<iostream>
using namespace std;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;
}

🙂感谢大家的阅读,若有错误和不足,欢迎指正。

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

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

相关文章

【网络安全】 MSF提权

本文章仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若读者因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与作者无关。 环境准备&#xff1a; 名称系统位数IP攻击机Kali Linux6410.3.0.231客户端Windows 76410.3.0.234…

CentOS 7 socat命令端口转发 —— 筑梦之路

命令简介 socat是一个功能强大的命令行工具&#xff0c;也可以看作是netcat的加强版&#xff0c;它可以在两个端口之间建立虚拟通道&#xff0c;实现数据的传输。适用于网络调试、端口转发、安全测试等多种场景&#xff0c;是一个适合网络管理员和开发者的工具 yum在线安装 yu…

搭建GItlab实现自动化部署Springboot项目(超详细)

提示&#xff1a;本例程中使用Docker搭建GItlab&#xff0c;Gitlab runner 通过编写CICD文件实现Springboot项目自动部署。 1、拉取GitLab镜像 命令&#xff1a; docker pull gitlab/gitlab-ce2、部署Gitlab&#xff1a; 我们通过docker搭建的gitlab部署项目的时候会出现一个…

LeetCode 面试经典150题 80.删除有序数组中的重复项II

题目&#xff1a; 给你一个有序数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使得出现次数超过两次的元素只出现两次 &#xff0c;返回删除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件…

cuda cudnn安装

安装 cudnn是否安装成功 注意 cudnn对应的cuda的10.0版本无win11版本 下载win10 即可

002——编译鸿蒙(Liteos -a)

目录 一、鸿蒙是什么 二、Kconfig 2.1 概述 2.2 编译器 2.3 make使用 本文章引用了很多韦东山老师的教程内容&#xff0c;算是我学习过程中的笔记吧。如果侵权请联系我。 一、鸿蒙是什么 这里我补充一下对鸿蒙的描述 这张图片是鸿蒙发布时使用的&#xff0c;鸿蒙是一个很…

探讨NLP对行业大量数据信息抽取的技术实现

在本文中&#xff0c;为了实现高效的信息抽取&#xff0c;我们采用了一个自主研发的多模态AI的大模型NLP平台。 这个平台的使用过程分为以下几个步骤&#xff1a; 数据收集&#xff1a;我们收集了与项目相关的100条数据样本&#xff0c;这些样本涵盖了各种商品描述&#xff0c…

k8s部署hadoop

&#xff08;作者&#xff1a;陈玓玏&#xff09; 配置和模板参考helm仓库&#xff1a;https://artifacthub.io/packages/helm/apache-hadoop-helm/hadoop 先通过以下命令生成yaml文件&#xff1a; helm template hadoop pfisterer-hadoop/hadoop > hadoop.yaml用kube…

酷开科技以酷开系统的力量让电视机“活”起来

让用户回归电视的绝不会是因为电视机本身&#xff0c;而是电视系统的内容和交互的形式。酷开科技以系统的力量让电视机“活”起来。对于许多人来说&#xff0c;观看电影是一种享受、一种放松、一种逃避现实的方式。而现在&#xff0c;酷开科技作为行业内领军企业&#xff0c;为…

在react中使用tailwindcss

安装tailwind css npm i -D tailwindcssnpm:tailwindcss/postcss7-compat postcss^7 autoprefixer^9安装 CRACO 由于 Create React App 不能让您覆盖原生的 PostCSS 配置&#xff0c;所以我们还需要安装 CRACO 才能配置 Tailwind。 npm install craco/craco配置CRACO 在项目根…

Unity PS5开发 天坑篇 之 DEVKit环境部署与系统升级02

上一篇各位大神们已经收到了SONY官方免费寄送的PS5开发机与测试机&#xff0c;恭喜大家成为SONY的开发者, 本篇继续PS5开发机的部署与开发套件使用。 一, PC安装PS5 SDK与系统升级 1. PC/PS5 SDK Manager下载安装包 登录开发者账号后&#xff0c;Development->Resources&a…

基于有限状态机开发健壮的Nodejs/TCP客户端

有限状态机是一种数学计算模型&#xff0c;它描述了在任何给定时间只能处于一种状态的系统的行为。形式上&#xff0c;有限状态机有五个部分&#xff1a; 初始状态值 (initial state)有限的一组状态 (states)有限的一组事件 (events)由事件驱动的一组状态转移关系 (transition…