【C++】初识类和对象

本篇介绍一下C++的自定义类型,类和对象。

1.类的定义

1.1 类定义格式

class 为定义类的关键字,Stack为类的名字,类名随便取,{}中为类的主体,类定义结束时后面的分号不可省略。类主体中内容称为类的成员:类中的变量称为类的属性成员变量;类中函数称为类的方法成员函数

 与C语言的结构体的定义相似,第一个不同就是类成员还可以是函数,C语言的结构体里面没有函数。比如我们定义一个栈的类。

class Stack
{void Push(int x)  //成员函数(类的方法){//...}void Pop(){//...}int top(){//...}int* _a;           //成员变量(类的属性)int _top;int _capacity;
};

为了区分成员变量,一般习惯在成员变量上加一个特殊标识,如_或者m开头,有的也把_加在成员变量名后面,像int* a_;  ,但这并不是强制的。

C++中也可以用struct定义类,C++兼容C中的struct用法,同时struct升级成了类,明显变化是C++的struct中可以定义函数,像下面的日期类,class可以是struct。一般情况下还是推荐用class定义类。

struct Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};

在类里面直接定义的函数默认就是内联函数,但是声明和定义分离就不是内联了,比如在类里面声明,在类外面定义。

1.2访问限定符

C++一种实现封装的方式,用类将对象的属性(变量)和方法(函数)结合在一起,让给对象更完善,通过访问权限选择性的将其接口提供给外部的用户使用。

访问限定符有3个:public  private  protected。public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private目前是一样的,后面的学习中才能体现出他们的区别。

 访问权限作用域从该访问限定符出现的位置开始,到下一个访问限定符出现为止。如果后面没有访问限定符,作用域就到}即类的结束。

class Stack
{void Push(int x)  //成员函数(类的方法){//...}
public:void Pop(){//...}int top(){//...}
private:int* _a;           //成员变量(类的属性)int _top;int _capacity;
};

class定义成员没有被限定符修饰时默认为private,struct默认为public。一般来说,成员变量都会被限制为private/protected,需要给别人使用的成员函数会被设为public。 

第二个不同是类名就是类型,可直接用类名定义对象。比如这里随便弄个结构体来对比一下,类还是用前面那个Stack类举例。

struct S
{int a;
};
struct S s; //C语言结构体定义结构体变量
Stack st;   //类定义对象

类不需要写成class Stack st; 直接Stack st;  C语言的结构体就不能丢掉struct。

因为C++兼容C语言,传统的struct在C++可以用,而struct在C++中又升级成了类,所以在C++中,下面两种写法都可以

struct S s;
S s;
struct ListNode  //C语言链表节点的表示
{int val;struct ListNode* next;
};
struct ListNode  //C++链表节点的表示
{int val;ListNode* next;
};

C语言的表示在C++也能用,C++就有两种表示方法 

当我们访问类成员时,跟结构体一样,用点(.)访问。

当我们引出成员变量时发现只有public限制的可以访问,Push没有访问限定符限制,默认为私有,成员变量也是私有。

这里访问限定符,可以重复设置,比如我们想把Push设为公有,可重复使用public,但是一般放在一起,这里只是说明一下可以重复。

class Stack
{
public:void Push(int x)  //成员函数(类的方法){//...}
public:void Pop(){//...}int top(){//...}
private:int* _a;           //成员变量(类的属性)int _top;int _capacity;
};

1.3 类域

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

class Stack
{
public:void Push(int x);  //类内:成员函数的声明private:int* _a;      //成员变量     int _top;int _capacity;
};
void Stack::Push(int x) //类外:函数的定义
{//...
}

2.实例化

2.1 实例化概念

• 用类类型在物理内存中创建对象的过程,称为类实例化出对象。
• 类是对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员变量只是声明没有分配空间,用类实例化出对象时,才会分配空间。
• 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量

打个比方:类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,设计图规划了有多少个房间,房间大小功能等,但是并没有实体的建筑存在,也不能住人,用设计图修建出房子,房子才能住人。

 同样类就像设计图一样,不能存储数据,实例化出的对象分配物理内存存储数据。

2.2 对象大小

分析一下类对象中哪些成员呢?类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?

首先函数被编译后是一段指令,对象中没办法存储,这些指令存储在一个单独的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针。再分析一下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year / _month / _day 存储各自的数据,但是d1和d2的成员函数Init /Print指针却是一样的,存储在对象中就浪费了。如果用Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。这里需要再额外说一下,其实函数指针是不需要存储的,函数指针是一个地址,调用函数被编译成汇编指令[call地址],其实编译器在编译链接时,就要找到函数的地址,不是在运行时找,只有动态多态是在运行时找,就需要存储函数地址,这个我们以后会讲解。

上面一大段的意思就是对象的大小不计算成员函数,只看成员变量,成员函数会存在一个公共区域。

对象中的成员变量在内存中的存储和C语言时结构体在内存中的存储规则一模一样,要内存对齐,结构体内存对齐详解在【C语言】结构体详解-CSDN博客 ,内存对齐规则以及为什么要内存对齐都在这篇写过,不知道的可以先看结构体这篇。

我们还是举个例子回顾一下。下面这个A类对象有多大?

class A
{
public:void Ptint(){cout << "void Ptint()" << endl;}
private:char _ch;int _i;
};

应该是8个字节 ,具体计算如下

 我们再来看两个,类B里面只有成员函数,类C里面没有成员,它们大小是多少呢?0吗?

class B
{
public:void Ptint(){cout << "void Ptint()" << endl;}
};class C
{
};

 我们用类B实例化一个对象b,类C实例化一个对象c,看看它们的大小。

B b; //实例化对象
C c;
cout << sizeof(b) << endl;
cout << sizeof(c) << endl;

我们可以看到类B和类C对象的大小是1个字节,为什么不是0?因为如果一个字节也不给,怎么表示对象存在过呢?所以就给1个字节,纯粹是为了占位,表示对象存在。

3. 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;
};

我现在实例化两个对象d1和d2

Date d1;
Date d2;

 用这两个对象像下面这样引出里面的函数

d1.Init(2024, 8, 2);
d1.Print();d2.Init(2024, 8, 3);
d2.Print();

运行,看结果

思考一下,d1和d2明明执行的是同一个函数,结果为什么不一样? 函数体中没有关于不同对象的区分,那么当d1调用Init函数和Print函数的时候该函数是如何知道访问d1对象还是d2对象?

这里就介绍一个C++给的一个隐含的this指针解决问题。

编译器编译后,类的成员函数默认都会在形参的第一个位置增加一个当前类类型的指针,叫this指针。所以Init的第一个形参并不是year,Print函数也并不是没有参数,前面的Print函数和Init函数实际上是像下面这样。

void Init(Date* const this, int year, int month, int day)
{//...
}
void Print(Date* const this)
{//...
}

 所以传参的时候也会传相应的地址过去。

//d1.Init(&d1, 2024, 8, 2);d1.Init(2024, 8, 2);
//d1.Print(&d1)d1.Print();//d2.Init(&d2, 2024, 8, 3);d2.Init(2024, 8, 3);
//d2.Print(&d2)d2.Print();

类的成员函数中访问成员变量,本质是通过this指针访问的

void Init(int year, int month, int day)
{this->_year = year;this->_month = month;this->_day = day;
}
void Print()
{cout << this->_year << "." << this->_month << "." << this->_day << endl;
}

 C++规定不能在实参和形参的位置显示的写this指针,编译时编译器会处理,但是可以在函数体内显示使用this指针。this指针是不能修改的。

一般this指针存在栈区,因为this指针其实是函数的形参,形参就存放在栈区,有的也会存放在寄存器里面,vs下就是把this指针放在了寄存器里。

这次就分享到到这里,拜拜~

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

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

相关文章

C++开源界面库duilib的使用细节与实战技巧总结(实战经验分享)

目录 1、使用CEditUI编辑框 2、使用CLabelUI或CTextUI的Html文本效果 3、使用CTextUI控件对文字宽度自适应的特性 4、CRichEditUI富文本控件使用注意点 4.1、指定CRichEditUI加在2.0版本的Riched20.dll库 4.2、解决向CRichEditUI中插入文字后显示空白的问题 5、设置窗口…

STL——栈和队列和优先队列

栈和队列和优先队列 概述std&#xff1a;&#xff1a;堆栈核心函数和操作成员函数示例注意事项 std&#xff1a;&#xff1a;队列核心函数和操作成员函数示例注意事项 std&#xff1a;&#xff1a;优先队列底层实现原理效率分析deque双端队列原理块结构&#xff1a;指针管理&am…

企业源代码加密软件|6款好用的源代码加密软件推荐

在软件开发领域&#xff0c;源代码作为企业最核心的资产之一&#xff0c;其安全性直接关系到企业的技术竞争力和商业机密保护。因此&#xff0c;选择一款高效、可靠的源代码加密软件对于企业来说至关重要。以下是企业源代码加密软件的六款推荐&#xff0c;每款软件均具备其独特…

【Android】系统架构、四大组件、结构目录

文章目录 Android系统架构Android四大组件ActivityServiceBroadcast ReceiverContent Provider 两大视图主要结构目录 Android系统架构 linux内核层 为设备的各种硬件提供了底层的驱动&#xff0c;如显示驱动等 系统运行库层 通过C/C库来为android提供主要的特性支持。如SQLit…

普元Devops学习笔记-devops对接jenkins提示crumb不可用问题

前言 普元devops需要对接jenkins&#xff0c;对接jenkins后&#xff0c;devops会调用jenkins的提供的API。 问题 新版本的jenkins提供跨域保护&#xff0c;即大名鼎鼎的CSRF问题。 因此&#xff0c;普元devops调用jenkins的时候&#xff0c;会出现跨域问题。 后台报错信息如…

【kubernetes】kubeadm部署k8s集群

1、环境准备 master01: 192.168.10.25master02: 192.168.10.26master03: 192.168.10.27node01: 192.168.10.28node02: 192.168.10.29负载均衡器1&#xff1a;192.168.10.30负载均衡器2&#xff1a;192.168.10.31 //所有节点&#xff0c;关闭防火墙规则&#xff0c;关闭selinu…

大型语言模型入门

大型语言模型ChatGPT 快速、全面了解大型语言模型。学习李宏毅课程笔记。 ChatGPT 目前由OpenAI公司发明的非常火的人工智能AI应用ChatGPT&#xff0c;到底是什么原理呢&#xff1f; G&#xff1a;Generative(生成) P&#xff1a;Pre-trained(预训练) T&#xff1a;Transform…

2024年7月25日(Git gitlab以及分支管理 )

分布式版本控制系统 一、Git概述 Git 是一种分布式版本控制系统,用于跟踪和管理代码的变更。它是由Linus Torvalds创建的,最 初被设计用于Linux内核的开发。Git允许开发人员跟踪和管理代码的版本,并且可以在不同的开 发人员之间进行协作。 Github 用的就是Git系统来管理它们的…

【Unity编辑器拓展】GraphView自定义可视化节点

1、创建节点区域脚本 其中的new class UxmlFactory&#xff0c;可以让该元素显示在UI Builder中&#xff0c;我们就可以在Library-Project中看到我们新建的这两个UI元素&#xff0c;就可以拖入我们的UI窗口编辑了 public class NodeTreeViewer : GraphView {public new class…

一文了解一下 MindSpeed,MindSpeed 是专为华为昇腾设备设计的大模型分布式加速套件。

https://gitee.com/ascend/MindSpeed Gitee Ascend/MindSpeed 项目&#xff0c;MindSpeed 是针对华为昇腾设备的大模型加速库。 MindSpeed 是专为华为昇腾设备设计的大模型加速库&#xff0c;旨在解决用户在大模型训练过程中遇到的显存资源不足等挑战。该库借鉴了 Megatron、D…

福建聚鼎:现在的装饰画还赚钱不

在轻风拂面的清晨&#xff0c;或在星光璀璨的夜晚&#xff0c;人们常常沉浸于对艺术的思考。装饰画作为艺术的一种表现形式&#xff0c;既丰富了人们的精神世界&#xff0c;又装点了生活的每一个角落。但在这个快速变化的时代&#xff0c;一个引人深思的问题浮出水面&#xff1…

前端-如何通过docker打包Vue服务成镜像并在本地运行(本地可以通过http://localhost:8080/访问前端服务)

1、下载安装docker&#xff0c;最好在vs code里安装docker的插件。 下载链接&#xff1a;https://www.docker.com/products/docker-desktop &#x1f389; Docker 简介和安装 - Docker 快速入门 - 易文档 (easydoc.net) 2、准备配置文件-dockerfile文件和nginx.conf文件 do…

postman查询单条数据Get方法,无任何输出,idea后端也没有任何数据和提示的解决方法

问题描述&#xff1a; 正常使用postman测试&#xff0c;输入内容没有错误&#xff0c;但是却没有任何消息 后端也是&#xff0c;没有任何消息&#xff1a; 解决方法&#xff1a; 问题的原因主要是因为postman&#xff1a; 我们只需要新建一个页面&#xff0c;把刚才的查询语…

《C语言程序设计 第4版》笔记和代码 第十二章 数据体和数据结构基础

12.1从基本数据类型到抽象数据类型 1 所有的程序设计语言都不能将所有复杂数据对象作为其基本数据类型&#xff0c;因此需要允许用户自定义数据类型&#xff0c;在C语言中&#xff0c;就存在构造数据类型&#xff08;复合数据类型&#xff09;。 2 结构体是构造数据类型的一种…

记录|如何统一管理多个同一个对象?

目录 前言一、对象就用对象数组管理更新时间 前言 自己的感想 一开始&#xff0c;自己没弄懂C# winform中的testBox是什么&#xff0c;导致创建多个testBox的时候要用很笨的方法来进行管理。 就是下面这种&#xff1a;用数组一个一个掉用里面的单独属性。 string[] str new …

芋道以开源之名行下作之事 恬不知耻 标榜自己开源 公开源码+sql 不用再加入知识星球

资源 链接: https://pan.baidu.com/s/1TeuxbAUfLQ5_BqMBF1kniQ?pwdcqud 提 取码: cqud 依次为后端、补充版的sql、前端 此文档内安装部署等一应俱全

反序列化靶机serial漏洞复现 超详细教程

环境搭建 漏洞环境&#xff1a;https://www.vulnhub.com/entry/serial-1,349/ 下载后使用Vmware打开 创建新的虚拟机&#xff1a; 选择客户机版本为Ubuntu 64位&#xff1a; 一直下一步&#xff0c;知道选择使用现有磁盘&#xff1a; 选择下载的vmdk磁盘文件&#xff1a; 开机…

24年电赛——自动行驶小车(H题)完赛感受

前言&#xff1a; 笔者大二&#xff0c;也算是第一次正式的打电赛省赛&#xff08;大一电赛的时候还没接触32&#xff0c;校赛的时候就被刷下去了。。。&#xff09;。经过一年的学习&#xff0c;三天两夜的校赛、两天一夜的七校联赛终于是挺到了省赛。比赛过程中真的是有太多感…

Git、Gitlab以及分支管理

分布式版本控制系统 一、Git概述 Git是一种分布式版本控制系统&#xff0c;用于跟踪和管理代码的变更。它由Linus torvalds创建的&#xff0c;最初被设计用于Linux内核的开发。Git 允许开发人员跟踪和管理代码的版本&#xff0c;并且可以在不同的开发人员之间进行协作。 Githu…

浏览器用户文件夹详解 - WebData(八)

1.WebData简介 1.1 什么是WebData文件&#xff1f; WebData文件是Chromium浏览器中用于存储用户表单数据、自动填充信息和支付信息的一个重要文件。每当用户在浏览器中填写表单或保存支付信息时&#xff0c;这些数据都会被记录在WebData文件中。通过这些记录&#xff0c;浏览…