【C++】类和对象详解(类的使用,this指针)

文章目录

  • 前言
  • 面向过程和面向对象的初步认识
  • 类的引入
  • 类的定义
  • 类的访问限定符和封装性
    • 访问限定符
    • 封装性
  • 类的作用域
  • 类的实例化
  • 类对象模型
    • 如何计算类对象的大小
    • 类对象的存储方式猜测
    • 结构体内存对齐规则
  • this指针
    • this指针的引出
    • this指针的特性
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

在计算机编程领域,程序设计的方法论不断演化,从最初的面向过程到如今更为强大而灵活的面向对象。本文将深入探讨C++中关于类和对象的概念,为读者提供对面向对象编程的深刻理解。

提示:以下是本篇文章正文内容,下面案例可供参考

面向过程和面向对象的初步认识

在计算机编程的世界中,面向过程和面向对象是两种不同的编程范式,它们反映了程序设计的不同思想和方式。以下是对这两种范式的初步认识:

面向过程:

面向过程编程是一种以过程为中心的编程思想。在这种范式中,程序被看作一系列的函数或过程的集合,这些函数按照一定的次序依次执行,完成特定的任务。程序的执行流程主要由函数的调用关系来控制。

主要特点包括:

  • 程序的控制流程是线性的,从上到下逐行执行。
  • 重点在于解决问题的步骤和流程,以完成特定的任务。
  • 数据和函数分离,强调过程的执行。

面向对象:

面向对象编程是一种以对象为中心的编程思想。在这种范式中,程序被组织成一组对象,每个对象包含数据和相关的操作,通过这些对象之间的交互来完成任务。

主要特点包括:

  • 程序的控制流程不再是线性的,而是由对象之间的消息传递和方法调用构成。
  • 重点在于组织和管理对象,强调数据的封装和对象的行为。
  • 对象可以被看作是现实世界中的实体,有着状态和行为。

对比和选择:

面向过程和面向对象各有优缺点,选择合适的范式取决于问题的性质和解决方案的需求。面向过程适用于简单的、线性的问题,而面向对象更适用于复杂的、模块化的系统设计。

在实际开发中,很多编程语言提供了同时支持面向过程和面向对象的特性,如C++、Java等,使得程序员可以根据具体情况选择合适的编程方式。这也说明了在面向对象编程的潮流中,面向过程并没有被淘汰,而是与面向对象共同存在,相互补充,为编程提供了更灵活的选择。

类的引入

在c语言中,结构体内只能定义变量,在c++中,结构体内不仅可以定义变量,还可以定义函数

#include <iostream>
#include <cstring>  // 添加头文件以使用字符串库函数
using namespace std;struct stu
{void getName(){cout << name << endl;}int age;int num;char name[20];
};int main()
{struct stu s1;strcpy(s1.name, "dzj");  // 使用strcpy将字符串赋值给字符数组s1.getName();return 0;
}

但是在C++中,上述结构体的定义,更喜欢用class来代替

注意:C语言结构体中不能定义成员函数,C++兼容C的语法,并进行拓展,可以定义成员函数

类的定义

类名:由成员函数和成员变量组成,同时注意分号

class className
{//类名:由成员函数和成员变量组成
}

class是定义类的关键字,className是类的名称,{}中类的主体同时注意类定义结束时后面的分号。类中的元素是类的成员:类中的数据称为类的属性或者成员变量;类中的函数称为类的方法或者成员函数
类的两种定义方式:

  • 声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
class person
{
public:void print(){cout << _age << _name << _weight << endl;}
private:int _age;char _name[10];int _weight;
};
  • 声明放在.h中,类的定义放在.cpp中

在这里插入图片描述
一般情况下,一般采用第二种定义和声明分离的方式,但是为了方便,在平时的练习时,可以采用第一种方式(声明和定义全部放在类体中)

补充:声明和定义的区别是什么?
声明是一种承诺,承诺要干什么,但是还没做,定义就是把这个事情执行了或者落实了

类的访问限定符和封装性

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

访问限定符

  1. public(公有)
  2. protected(保护)
  3. private(私有)

【访问限定符说明】

  • public修饰的成员在类外可以直接被访问
  • protected修饰的成员类的内部和派生类的成员可以访问,但在类的外部无法直接访问.
  • private修饰的成员只能在本类中访问,在类外不能直接访问
  • 访问权限的作用域从该访问限定符出现的位置直到下一个访问限定符出现为止
  • class的默认访问权限是private,但是struct是public(因为struct要兼容c语言)

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

C++中struct和class的区别是什么?
C++需要兼容C语言,所以C++中struct可以当成结构体去使用。另外C++中struct还可以用来定义类。
和class是定义类是一样的,区别是struct的成员默认访问方式是public,class的成员默认访问方式是private。

封装性

面向对象的三大特性:封装、继承、多态。
在类和对象阶段,我们只研究类的封装特性,那什么是封装呢?
封装是面向对象编程(OOP)中的一个重要概念,它指的是将数据(变量)和操作数据的方法(函数)捆绑在一起的一种机制。封装有助于隐藏对象的内部实现细节,同时提供一组公共接口供外部使用。这样,对象的内部状态和行为对外部来说是不可见的,只有通过公共接口才能与对象进行交互。

封装的主要目的有以下几点:

  1. 数据隐藏: 将对象的实现细节隐藏起来,防止外部直接访问对象的内部数据,从而提高数据的安全性。

  2. 实现细节隔离: 允许对象实现者修改对象的内部实现细节而不影响外部使用者的代码。外部代码只需要关心对象的接口,而不必关心其具体实现。

  3. 代码组织: 将数据和操作数据的方法封装在一个单元中,使得代码更加模块化、可维护性更强。

在面向对象的语言中,封装通过类来实现。类封装了数据成员和成员函数,成员函数提供了访问和操作数据的接口。访问控制修饰符(如public、private、protected)用于控制成员的可见性和访问权限,实现封装的关键。

示例:

#include <iostream>class Car {
private:// 私有数据成员int speed;public:// 公有成员函数void setSpeed(int s) {if (s >= 0) {speed = s;}}int getSpeed() const {return speed;}
};int main() {Car myCar;// 使用公有接口设置速度myCar.setSpeed(60);// 使用公有接口获取速度std::cout << "Current Speed: " << myCar.getSpeed() << " km/h" << std::endl;return 0;
}

在这个例子中,Car 类封装了一个私有的速度成员,并提供了公有的 setSpeedgetSpeed 方法来设置和获取速度。外部代码通过这些公有接口与 Car 对象进行交互,而不需要了解具体的实现细节。这就体现了封装的思想。

类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类外面定义成员,需要使用::作用域解析符来指明属于哪个类域

class person
{
public:void print();
private:int _age;char _name[10];int _weight;
};
void person::print()
{cout << _age << _name<<_weight << endl;
}

类的实例化

类的实例化指的是创建类的对象。在面向对象编程中,类定义了一种数据类型,而实例化则是根据这个数据类型创建具体的对象。对象是类的一个实例,具有类定义的属性和行为。

下面是一个简单的C++示例,演示了如何定义一个类并实例化对象:

#include <iostream>
#include <string>// 定义一个简单的Person类
class Person {
public:// 公有成员函数,用于设置和获取姓名void setName(const std::string& n) {name = n;}std::string getName() const {return name;}private:// 私有数据成员,姓名std::string name;
};int main() {// 实例化Person类的对象Person person1;// 使用公有接口设置姓名person1.setName("Alice");// 使用公有接口获取姓名std::cout << "Person's Name: " << person1.getName() << std::endl;// 实例化另一个Person类的对象Person person2;// 使用公有接口设置姓名person2.setName("Bob");// 使用公有接口获取姓名std::cout << "Person's Name: " << person2.getName() << std::endl;return 0;
}

在这个例子中,Person 类定义了一个私有数据成员 name 和公有的成员函数 setNamegetName。通过实例化两个 Person 对象 person1person2,分别设置和获取了它们的姓名。

实例化是面向对象编程中的重要概念,它允许我们根据类的定义创建多个对象,每个对象都拥有自己的状态和行为。

类对象模型

如何计算类对象的大小

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

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

答:对象中只存储成员变量,不存储成员函数

类对象的存储方式猜测

  • 对象中包含类中的各个成员(成员函数和成员变量)
    事实上,这样是不合理的,每个对象中成员变量是不同的,但是调用的却是同一个函数,如果按照这种方式存储,当一个类创建多个对象的时候,每个对象中都会保存一份代码,相同的代码保存了多次,浪费空间。
  • 只保存成员变量,成员函数保存在公共的代码段
    图解:
    在这里插入图片描述
    显然计算机采用的是第二种存储方式(类中对象仅仅存储成员变量)

结论:一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。

结构体内存对齐规则

关于内存对齐可以看这篇博客

this指针

this指针的引出

#include <iostream>
using namespace std;
class Date
{
public:void setDate(int year,int month,int day){_year = year;_month = month;_day = day;}void getInfo(){cout << _year << endl;cout << _month << endl;cout << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2;d1.setDate(2024, 1, 6);d1.getInfo();return 0;
}

问题:在上述代码中,有两个成员函数setDate和getInfo,函数体中没有关于类的划分,那么程序是怎么知道d1.setDate(2024, 1, 6)设置的就是d1的空间,而不是d2的空间

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

图解
在这里插入图片描述
这里编译器偷偷帮我们做了处理,但是我们自己并不知道,即在成员函数第一个参数添加 类名*this,并在函数调用的时候传递了对象的地址。

#include <iostream>
using namespace std;
class Date
{
public:void setDate(int year,int month,int day)//隐含的this指针Date* this{this->_year = year;this->_month = month;this->_day = day;}void getInfo(){cout << _year << endl;cout << _month << endl;cout << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2;d1.setDate(2024, 1, 6);//隐含的传递对象地址 d1.setDate(&d1,2024, 1, 6)d1.getInfo();return 0;
}

this指针的特性

  1. this指针的类型:类名*const
  2. this指针只能在“成员函数”的内部使用
  3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数的时候,将对象地址作为实参传递给this形参,所以对象中不存储this指针
  4. this指针是成员函数第一个隐含的指针形参,(vs2022下)由编译器通过ecx寄存器自动传递,不需要用户传递

this指针存储在哪里?

上面说到this指针本质是成员函数的第一个形参,那么肯定存放在进程地址空间中的栈区,但是在vs2022下是存放在ecx寄存器中,不同编译器和环境下可能会有差异

this指针可以为空吗?

通过一段代码来说明

#include <iostream>
using namespace std;
class A
{
public:void PrintA(){cout << _a << endl;}void Show(){cout << "Show()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;//p->PrintA();p->Show();
}

上面代码中,p->PrintA()会发生程序崩溃,但是p->Show()可以正常运行。因为在PrintA()中发生了隐含的this指针的解引用操作(对空指针的解引用),但是在Show()并没有解引用所以程序没问题

总结

通过本文的学习,我们深入了解了C++中类和对象的方方面面,从面向过程的初步认识到类的引入和定义,再到类的作用域、实例化、访问限定符及封装,以及类对象大小的计算和类成员函数的this指针。这些概念和技术不仅为我们提供了更灵活的程序设计手段,也为构建更为可维护和可扩展的软件系统奠定了基础。在今后的编程学习和实践中,希望读者能够充分运用这些知识,编写出更高效、健壮的C++程序。面向对象编程不仅仅是一种技术,更是一种思想,期待读者通过不断实践,能够更深刻地理解和运用面向对象的优秀编程范式。

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

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

相关文章

linux反汇编工具: ida pro、rizinorg/cutter; ubuntu 22 flameshot延迟截图 以应对下拉菜单

rizinorg/cutter rizinorg/cutter 是 命令行反汇编工具 rizinorg/rizin 的图形化界面, 这比 ida pro跑在kvm虚拟机中方便多了, ubuntu22.04下直接下载Cutter-v2.3.2-Linux-x86_64.AppImage后即可运行,如下图: 注意 有个同名的报废品: radare2/Cutter 即 radare2的图形化界…

基于日照时数计算逐日太阳辐射

基于日照时数计算逐日太阳辐射

彻底认识Unity ui设计中Space - Overlay、Screen Space - Camera和World Space三种模式

文章目录 简述Screen Space - Overlay优点缺点 Screen Space - Camera优点缺点 World Space优点缺点 简述 用Unity中开发了很久&#xff0c;但是对unity UI管理中Canvas组件的Render Mode有三种主要类型&#xff1a;Screen Space - Overlay、Screen Space - Camera和World Spa…

【elfboard linux开发板】7.i2C工具应用与aht20温湿度寄存器读取

1. I2C工具查看aht20的温湿度寄存器值 1.1 原理图 传感器通过IIC方式进行通信&#xff0c;连接的为IIC1总线&#xff0c;且设备地址为0x38&#xff0c;实际上通过后续iic工具查询&#xff0c;这个设备是挂载在iic-0上 1.2 I2C工具 通过i2c工具可以实现查询i2c总线、以及上面…

普中STM32-PZ6806L 使用FlyMcu串口烧录程序

简介 我的串口下载电路坏掉了, 所以研究了下如何通过USB转TTL进行程序的下载, 为后续Bootloader部分做准备;连接 我的板几乎是十年前买的&#xff0c; 所以电路与现有网上的资料有些差异, 所以仅供参考 USB 转 TTL线 与开发板 连接&#xff0c; 如图图中 ①, 需要去掉第一个…

计算机组成原理 指令流水线

文章目录 指令流水线指令流水线的概念流水线性能分析流水线的吞吐率流水线的加速比流水线的效率 影响流水线的因素结构相关 (资源冲突)数据相关 (数据冲突)控制相关 (控制冲突) 流水线分类超量流水线 指令流水线 #mermaid-svg-VKNFSIxU0RiY8pAm {font-family:"trebuchet m…

XAgent调研

文章目录 1 简介2 快速测试 Quick Start3 结构分析 1 简介 XAgent&#xff08;链接&#xff09;是一个开源的&#xff0c;基于大语言模型的agent构建框架&#xff1b;其目标是构建出能够辅助人类处理各类任务的自动助手 定位&#xff1a;一个全能的&#xff0c;自动的辅助agen…

LeetCode-移动零(283)

题目描述&#xff1a; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 思路&#xff1a; 这里的思路跟以前做过的去重复数字的思路有点像&…

Java学习——设计模式——结构型模式2

文章目录 结构型模式装饰者模式桥接模式外观模式组合模式享元模式 结构型模式 结构型模式主要涉及如何组合各种对象以便获得更好、更灵活的结构。虽然面向对象的继承机制提供了最基本的子类扩展父类的功能&#xff0c;但结构型模式不仅仅简单地使用继承&#xff0c;而更多地通过…

IDEA 每次新建工程都要重新配置 Maven的解决方案

文章目录 IDEA 每次新建工程都要重新配置 Maven 解决方案一、选择 File -> New Projects Setup -> Settingsfor New Projects…二、选择 Build,Execution,Deployment -> Build Tools -> Maven IDEA 每次新建工程都要重新配置 Maven 解决方案 DEA 每次新建工程都要…

CSS基础笔记-04cascade-specificity-inheritance

CSS基础笔记系列 《CSS基础笔记-01CSS概述》《CSS基础笔记-02动画》CSS基础笔记-03选择器 前言 Cascading Style Sheets&#xff0c;关键就在于这个cascading&#xff0c;对于这个术语理解&#xff0c;感觉对于我这种CSS新手有点儿不太friendly。本文记录下我对这个术语的理…

Lumerical------关闭 drawing grid 去更好地显示 mesh grid

Lumerical------关闭 drawing grid 去更好地显示 mesh grid 引言正文 引言 在 Lumerical 结构设置的时候&#xff0c;有时候我们想要查看 mesh 结构的 grid&#xff0c;但是本身默认的 dtawing grid 黑框会阻碍我们的观察&#xff0c;这时&#xff0c;我们便可以通过设置关闭这…

K8S陈述式资源管理

陈述式 命令行&#xff1a;kubectl命令行工具 优点&#xff1a;90%以上的场景都可以满足&#xff0c;对增&#xff0c;删&#xff0c;查比较方便&#xff0c;对改不是很友好 缺点&#xff1a;命令比较冗长&#xff0c;复杂&#xff0c;难记 声明式 k8s当中的yaml文件来实现资…

【DevOps-07-3】Jenkins集成Sonarqube

一、简要说明 Jenkins安装Sonarqube插件Jenkins安装和配置Sonar-Scanner信息Jenkins打包项目中,增加Sonar-Scanner代码质量扫描二、Jenkins安装Sonarqube插件 1、登录Jenkins管理后台,搜索安装Sonar-Scanner插件 Jenkins管理后台示例:http://192.168.95.131:8080/jenkins/

双十一的祈祷【算法赛】

问题描述 双十一&#xff0c;不仅是购物狂欢节&#xff0c;更有 "光棍节" 之称。这源于 11:1111:11 由四个 11 构成&#xff0c;象征着单身。 作为大学生的小蓝也想经历甜甜的校园恋爱&#xff0c;于是他找到了爱神丘比特&#xff0c;向他祈祷能为自己带来一段邂逅…

WorkPlus完备的企业级功能堆栈,打造高效的企业移动平台

在如今的数字化时代&#xff0c;企业需要一个完备的功能堆栈来满足复杂的业务需求。WorkPlus作为一个完整的企业级移动平台&#xff0c;拥有完备的企业级功能&#xff0c;如IM、通讯录、内部群、模板群、工作台、权限管控、应用中心、日程管理、邮箱、同事圈、服务号、智能表单…

微服务-java spi 与 dubbo spi

Java SPI 通过一个案例来看SPI public interface DemoSPI {void echo(); } public class FirstImpl implements DemoSPI{Overridepublic void echo() {System.out.println("first echo");} } public class SecondImpl implements DemoSPI{Overridepublic void ech…

手机录屏没有声音?让你的录屏有声有色

“有人知道手机录屏怎么录声音吗&#xff1f;今天录制了一个小时的直播视频&#xff0c;后面查看的时候发现没有声音&#xff0c;真的非常崩溃&#xff0c;想问问大家有没有办法&#xff0c;解决这个问题。” 在手机录屏的过程中&#xff0c;有时候我们可能会面临录制视频没有…

汽车电子行业的 C 语言编程标准

前言 之前分享了一些编程规范相关的文章&#xff0c;有位读者提到了汽车电子行业的MISRA C标准&#xff0c;说这个很不错。 本次给大家找来了一篇汽车电子行业的MISRA C标准的文章一同学习下。 什么是MISRA&#xff1f; MISRA (The Motor Industry Software Reliability Ass…

相控阵天线阵元波程差相位差计算

如下图所示&#xff0c;O点为相位为0的基准点&#xff0c;P(x,y)点为阵元所在位置&#xff0c;需要计算P点相对于基准点在波束方向上的相位差。OP2为波束方向&#xff0c;OP2与Z轴的角度为Theta&#xff0c;OP2在XOY的投影OP1与X轴的角度为Phi。 计算得到波程差OP2&#xff0c;…