【C++】什么是类与对象?

🦄个人主页:修修修也

🎏所属专栏:C++

⚙️操作环境:Visual Studio 2022


目录

面向对象概述

       封装

       继承

       多态

类是什么?

C++中类的引入

C++中类的定义

类的两种定义方式:

1.声明和定义全部放在类体中

2.类声明与成员函数定义分别放在不同的工程文件中:

类的访问限定符

类的封装

类的作用域

类的实例化

类对象模型

如何计算类对象的大小

类对象存储方式

结语


面向对象概述

        面向对象是一种符合人类思维习惯编程思想。现实生活中存在各种形态不同的事物,这些事物之间存在着各种各样的联系。在程序中使用对象来映射现实中的事物,使用对象的关系来描述事物之间的联系,这种思想就是面向对象。
        对于学习过其他编程语言的人员来说,可能会想到面向过程。面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一一实现,使用的时候依次调用就可以了。面向对象则是把构成问题的事物按照一定规则划分为多个独立的对象,然后通过调用对象的方法来解决问题。当然,一个应用程序会包含多个对象,通过多个对象的相互配合即可实现应用程序所需的功能,这样当应用程序功能发生变动时,只需要修改个别的对象就可以了,从而使代码更容易维护。
        面向对象的特点可以概括为封装继承多态,接下来针对这三种特性进行简单介绍。

       封装

            封装是面向对象的核心思想,将对象的属性和行为封装起来,不需要让外界知道具体实现细节,这就是封装思想。例如,用户使用计算机,只需要使用手指敲键盘就可以了,无须知道计算机内部是如何工作的,即使用户可能碰巧知道计算机的工作原理,但在使用时,并不完全依赖计算机工作原理这些细节。

       继承

            继承主要描述的就是类与类之间的关系,通过继承,可以在无须重新编写原有类的情况下,对原有类的功能进行扩展。例如,有一个汽车的类,该类中描述了汽车的普通属性和功能。而轿车的类中不仅应该包含汽车的属性和功能,还应该增加轿车特有的属性和功能,这时,可以让轿车类继承汽车类,在轿车类中单独添加轿车特有的属性和功能就可以了。继承不仅增强了代码的复用性,提高了开发效率,还为程序的维护补充提供了便利。

       多态

            多态指的是在一个类中定义的属性和功能被其他类继承后,当把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法所呈现出的多种不同行为特性。例如,当听到cu这个单词时,理发师的行为表现是剪发,演员的行为表现是停止表演等。不同的对象,所表现的行为是不一样的。


类是什么?

  • 类是面向对象语言的程序设计中的概念,是面向对象编程的基础。
  • 类的实质是一种引用数据类型,类似于 byte、short、int(char)、long、float、double 等基本数据类型,不同的是它是一种复杂的数据类型。因为它的本质是数据类型,而不是数据,所以不存在于内存中不能被直接操作只有被实例化为对象时才会变得可操作
  • 类是对现实生活中一类具有共同特征的事物的抽象。如果一个程序里提供的数据类型与应用中的概念有直接的对应,这个程序就会更容易理解,也更容易修改。一组经过很好选择的用户定义的类会使程序更简洁。此外,它还能使各种形式的代码分析更容易进行。特别地,它还会使编译器有可能检查对象的非法使用。
  • 类的内部封装了属性和方法,用于操作自身的成员。类是对某种对象的定义,具有行为(behavior),它描述一个对象能够做什么以及做的方法(method),它们是可以对这个对象进行操作的程序和过程。它包含有关对象行为方式的信息,包括它的名称属性、方法事件
  • 类的构成包括成员属性成员方法数据成员成员函数)。数据成员对应类的属性,类的数据成员也是一种数据类型,并不需要分配内存。成员函数则用于操作类的各项属性,是一个类具有的特有的操作,比如“学生”可以“上课”,而“水果”则不能。类和外界发生交互的操作称为接口。

C++中类的引入

        C语言结构体中只能定义变量,比如:之前在数据结构中,我们用C语言方式实现的栈,结构体中只能定义成员变量:

【数据结构】C语言实现顺序栈(附完整运行代码)

//C语言中定义顺序栈的结构体

struct Stack
{
    int *arr;
    int top;
    int capacity;
};

        并且,在C语言中,我们创建的结构体变量,只能访问自己的成员变量:

        而在C++中,结构体内不仅可以定义成员变量,也可以定义函数。现在我们以C++方式实现栈,可以发现struct中也可以定义函数:

struct Stack
{
    void Init(size_t capacity)
    {
        _arr = (int*)malloc(sizeof(int) * capacity);
        if (nullptr == _arr)
        {
            perror("malloc申请空间失败");
            return;
        }
        _capacity = capacity;
        top = 0;
    }

    //成员变量的位置可以是类内的任意地方
    int *arr;
    int top;
    int capacity;
};

        并且,在C++中,我们创建的结构体变量,是可以访问结构体成员函数的:


C++中类的定义

        虽然C++因为兼容C语言的缘故,将C语言中的struct升级为了类,但实际应用中,C++更喜欢使用class关键字来声明类。class 声明类时格式如下:

class class_name
{
    //类体:由成员函数和成员变量组成
};  

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

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

类的两种定义方式:

1.声明和定义全部放在类体中

需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

class Stack
{
public:

    void Init(size_t capacity)
    {
        _arr = (int*)malloc(sizeof(int) * capacity);
        if (nullptr == _arr)
        {
            perror("malloc申请空间失败");
            return;
        }
        _capacity = capacity;
        _top = 0;
    }

public:

    //成员变量的位置可以是类内的任意地方
    int* _arr;
    int _top;
    int _capacity;
};

注意:为了防止在类中定义函数时像下面这样容易使成员变量函数参数混淆:

一般当声明和定义全部放在类体中时我们会将类中的成员变量名前面加_以示区分:

2.类声明与成员函数定义分别放在不同的工程文件中:

类声明放在.h文件中:

成员函数定义放在.cpp文件中,注意:成员函数名前需要加  类名::


类的访问限定符

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

访问限定符的特性:

  • public修饰的成员在类外可以直接被访问
  • protectedprivate修饰的成员在类外不能直接被访问(此处protectedprivate是类似的)。
  • 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  • 如果后面没有访问限定符,作用域就到 } 即类结束。
  • class默认访问权限为privatestruct为public(因为struct要兼容C)。

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

C++中struct和class的区别是什么?
        C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是private。在继承和模板参数列表位置,struct和class也有部分区别。


类的封装

        我们在文章一开始就介绍了面对对象的三大特性:封装,继承,多态

        封装是面向对象的核心思想,将对象的属性和行为封装起来,不需要让外界知道具体实现细节,这就是封装思想。

        封装本质上是一种管理让用户更方便使用类。比如:对于电脑这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
        在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节控制哪些方法可以在类外部直接被使用

        在C语言中,由于对结构体没有封装的概念,从而会导致使用者通过变量对结构体中的成员变量进行任意不合规的操作。

        如下,在栈程序中,使用者就没有通过取栈顶元素函数获取栈顶元素,而通过直接访问成员变量的方式访问了栈顶元素:

        而在C++中,我们可以通过访问权限来隐藏对象内部实现细节控制哪些方法可以在类外部直接被使用,而哪些方法在类外部不可以被使用。


类的作用域

        类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员(包括变量和函数)时,需要使用 :: 作用域操作符指明成员属于哪个类域。

        如,上文中在C++中类的定义方式中我们提到的:

        有关类的作用域,有以下几点需要注意:

  • 局部域,全局域,命名空间域,类域,不同的域中可以定义同名变量。
  • 类中的变量查找优先级:局部域>类域>全局域/命名空间域(非指定展开则不查找)。
  • 局部域和全局域会影响生命周期,类域和命名空间域不会影响生命周期。

类的实例化

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

  1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类但并没有分配实际的内存空间来存储它;比如:入学时发放的学生信息表,表格就可以看成是一个类,来描述具体学生信息。但在还没有学生填表时,这个表格就并没有实际存储学生的任何信息。
  2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量。
  3.        打个比方来说,上面这个例子中的Date就类似于一个房屋建造的图纸,它规定了这个房屋建造多高,多宽,多长,以及各个地方的设计细节。但你如果不真正的将房子按照图纸建造出来,你就永远无法将家具之类的东西搬进房子里住,上图中使用Date类直接存储_year的数据就类似于要将现实世界的床搬进房屋设计图纸里住人。

类对象模型

如何计算类对象的大小

        在C语言中,我们学习过如何使用sizeof操作符计算结构体的大小,以及如何通过结构体对齐规则去计算结构体的大小(还不太了解的朋友可以移步这篇博客):

【C语言】结构体的大小是如何计算的?(结构体对齐)icon-default.png?t=N7T8https://blog.csdn.net/weixin_72357342/article/details/131135555?spm=1001.2014.3001.5502

        在C++中,对类的大小计算我们同样是使用sizeof操作符,如下例子,我们使用sizeof操作符分别计算Date类和Date类对象Birthday的大小:

class Date
{
public:
    void InitDate(int year, int mouth, int day)
    {
        _year = year;
        _mouth = mouth;
        _day = day;
    }
public:
    int _year;
    int _mouth;
    int _day;
};

int main() {
    Date Birthday;
    Birthday._year = 2000;

    int sz1 = sizeof(Date);
    int sz2 = sizeof(Birthday);

    printf("%d %d\n", sz1, sz2);
    return 0;
}

        运行查看结果,可以看到,类的大小和类对象的大小是一致的,这和结构体与结构体变量的大小计算结果一致:


类对象存储方式

        在知道了如何使用sizeof操作符计算类的大小后,我们还需要探究一下类对象究竟在内存中是怎样存储的。

        在C语言中,结构体的大小是由成员变量构成的,这我们很好理解,但在C++中,类的大小计算起来却看起来复杂的多,因为类中不仅包含了成员变量,还包含了成员函数,它们是如何存储在内存中的?它们和成员变量的关系又是怎样的?下面我们一起来探究一下:

`        如下,我们分别构建了一个类对象Date1,以及一个结构体对象Date2,分别计算他们的大小以及由它们创建的变量Birthday1,Birthday2的大小:

class Date1{
public:
    void InitDate(int year, int mouth, int day){
        _year = year;
        _mouth = mouth;
        _day = day;
    }
public:
    int _year;
    int _mouth;
    int _day;
};

struct Date2{
    int year;
    int mouth;
    int day;
};

int main() {
    Date1 Birthday1;
    Birthday1._year = 2000;

    int sz1 = sizeof(Date1);
    int sz2 = sizeof(Birthday1);
    printf("%d %d\n", sz1, sz2);

    Date2 Birthday2;
    Birthday2.year = 2000;

    int sz3 = sizeof(Date2);
    int sz4 = sizeof(Birthday2);
    printf("%d %d\n", sz3, sz4);
    return 0;
}

运行程序,我们可以发现,它们的大小是一样大的:

        也就是说,类的大小是不包含成员函数的大小的,它的大小只取决于类中的成员变量.同样的,类对象的大小也仅取决于成员变量的大小.

        这样的存储策略其实并不难理解,打个比方说,如果我们将类比作一个小区,那么类对象中的成员变量就类似于一间一间的居民楼,供用户存放个人的物品使用,而类中的成员函数则类似于小区中的凉亭或健身器材区域,它是可以提供给所有小区中的人使用的,并且所有人使用它都是一样的,因此完全不需要给每家都修一个,而是可以整个小区只修建一个,大家一起使用就可以了。

        因此,我们可以推断出,类成员的存储方式应该类似于下图这种结构:

        下面我们设计几个类来验证一下我们的推断:

//类中既有成员变量也有成员函数
class Date1 {
public:
    void InitDate(int year, int mouth, int day) {
        _year = year;
    }
public:
    int _year;
    int _mouth;
    int _day;
};

//类中只有成员函数
class Date2 {
public:
    void InitDate(int year, int mouth, int day) {
        year=2000;
    }
};

//类中只有成员变量
class Date3 {
public:
    int _year;
    int _mouth;
    int _day;
};

//类中什么都没有
class Date4 {

};

int main() {
    int sz1 = sizeof(Date1);
    int sz2 = sizeof(Date2);
    int sz3 = sizeof(Date3);
    int sz4 = sizeof(Date4);
    printf("%d %d %d %d\n", sz1, sz2,sz3,sz4);
    return 0;
}

        运行程序,得到结果:

        可以看到,类中既有成员函数也有成员变量和类中只有成员变量的大小是一样的,而类中只有成员函数和类中什么都没有的大小是一样的.

        需要注意的是,在没有成员变量的情况下,类的大小是1,它需要1字节的空间占位,表示对象存在,但不存储任何有效数据。

        需要注意的是,类中的成员变量的计算规则和C语言中的结构体一样,都遵循结构体对齐规则,所以下面这种情况中,Date1类的大小是8而不是5:

        对于结构体对齐规则的详解,感兴趣的朋友可以移步下面这篇博客:

【C语言】结构体的大小是如何计算的?(结构体对齐)icon-default.png?t=N7T8https://blog.csdn.net/weixin_72357342/article/details/131135555?spm=1001.2014.3001.5502


结语

希望这篇关于类与对象的基础博客能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

相关文章推荐

【C++】缺省参数(默认参数)

【C++】“Hello World!“

【C++】命名空间


慎在此祝愿全世界伟大的劳动妇女们,节日快乐!

广阔天地,大有作为!

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

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

相关文章

C语言数据结构之二叉堆

愿你千山暮雪 海棠依旧 不为岁月惊扰平添忧愁 🎥前期回顾-二叉树 🔥数据结构专栏 期待小伙伴们的支持与关注!!! 目录 前期回顾 二叉堆的概念及结构 二叉堆的创建 顺序表的结构声明 顺序表的创建与销毁 二叉堆的插入 …

CUDA安装及环境配置——最新详细版

确定安装版本 在安装之前呢,我们需要确定三件事 第一:查看显卡支持的最高CUDA的版本,以便下载对应的CUDA安装包 第二:查看对应CUDA对应的VS版本,以便下载并安装对应的VS版本(vs需要先安装) 第三…

基于Jupyter快速入门Python,Numpy,Scipy,Matplotlib

文章目录 Jupyter 和 Colab 笔记本PythonPython 版本基础数据类型数字Numbers布尔值Booleans字符串Strings 容器列表List字典Dictionaries集合Sets元组Tuples 函数类 Numpy数组Array数组索引Array indexing数据类型DatatypesArray math广播Broadcasting Scipy图像操作MATLAB文件…

IOS覆盖率报告info文件解读

一,IOS覆盖率报告的生成 在做前端精准测试的时候,对于iOS端,通常会做如下操作: (1)合并覆盖率数据 如下操作: xcrun llvm-profdata merge coverage_file1657885040728.profraw coverage_fil…

Java 可变长参数

可变长参数定义 从 Java5 开始,Java 支持定义可变长参数,所谓可变长参数就是允许在调用方法时传入不定长度的参数。可变长参数允许方法接受任意多个相同类型的参数,在方法内部可以将这些参数视为数组来处理。可变长参数通过省略号&#xff0…

【洛谷 P8668】[蓝桥杯 2018 省 B] 螺旋折线 题解(数学+平面几何)

[蓝桥杯 2018 省 B] 螺旋折线 题目描述 如图所示的螺旋折线经过平面上所有整点恰好一次。 对于整点 ( X , Y ) (X, Y) (X,Y),我们定义它到原点的距离 dis ( X , Y ) \text{dis}(X, Y) dis(X,Y) 是从原点到 ( X , Y ) (X, Y) (X,Y) 的螺旋折线段的长度。 例如 …

java SSM汽车租赁管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM汽车租赁管理系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用…

【Docker】了解Docker Desktop桌面应用程序,TA是如何管理和运行Docker容器(3)

欢迎来到《小5讲堂》,大家好,我是全栈小5。 这是《Docker容器》系列文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对…

MySQL实战:SQL优化及问题排查

有更合适的索引不走,怎么办? MySQL在选取索引时,会参考索引的基数,基数是MySQL估算的,反映这个字段有多少种取值,估算的策略为选取几个页算出取值的平均值,再乘以页数,即为基数 查…

Springboot+vue的项目申报管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频: Springbootvue的项目申报管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。 项目介绍: 采用M(model)V(view)C(controller&#xff09…

盲盒抽卡机小程序——开启神秘之旅!

亲爱的朋友们,欢迎来到盲盒抽卡机小程序!这里,是一个充满神秘与惊喜的世界,让你随时随地体验抽卡的乐趣。在这里,你可以轻松尝试各种盲盒,发现隐藏的宝藏,感受心跳加速的刺激。 【丰富多样的盲…

ARM单片机中程序在ROM空间和RAM空间的分布(分散加载文件,Scatter-Loading Description File)

对于 K e i l u V i s i o n I D E Keil\quad uVision\quad IDE KeiluVisionIDE,程序编译好之后,代码的下载位置( R O M ROM ROM空间)以及代码运行的时候使用的 R A M RAM RAM空间( R A M RAM RAM空间)默认…

华为云云耀云服务器L实例评测|用PHP从数据库到后端到前端完整实现一个中秋节祝福语项目

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师…

考虑到通信链路中断的(Delay Tolerant Network, DTN)

文章目录 A Study of DTN for Reliable Data Delivery from Space Station to Ground Stationabstractintroduction An Analytical Framework for Disruption of Licklider Transmission Protocol in Mars Communicationsabstract本文贡献 OVERVIEW OF RELIABLE DATA TRANSMISS…

Excel转pdf

1、excel-内存值--Workbook 转pdf /** * excel To pdf * * param outPath 输出路径 * param workbook excel-内存值 * throws IOException */ public static void excelToPdf(String outPath,Workbook workbook) throws IOException, DocumentException { Document documentnul…

Android studio Gradle下载失败,如何手动配置解决该问题详解

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。 👉点击跳转到教程 前言: 今天在打开公司一个项目时,突然要重新下载相关的gradle&am…

图机器学习(4)-面向连接层面的人工特征工程

0 问题定义 通过已经连接去猜未知连接: 有两个思路: (1)直接提取link的特征,把link变成D维向量; (2)把link两端节点的D维向量拼在一起,缺点:丢失了link本身…

CSAPP-程序的机器级表示

文章目录 概念扫盲思想理解经典好图安全事件 概念扫盲 1.汇编代码使用文本格式,相较于汇编的二进制可读性更好 2.程序内存包括:可执行的机器代码、操作系统需要的信息、管理过程调用和返回的运行时栈、用户分配的内存块 3.链接器为函数调用找到匹配的可…

基于SpringBoot宠物领养系统的设计与实现(代码+数据库+文档)

** 🍅点赞收藏关注 → 私信领取本源代码、数据库🍅 本人在Java毕业设计领域有多年的经验,陆续会更新更多优质的Java实战项目,希望你能有所收获,少走一些弯路。🍅关注我不迷路🍅** 一、研究背景…

运维:记一次寻找定时任务并删除的经历

前言 我相信接手别人的服务器、或者在没有任何文档的情况去看自己原先的服务器,都或多或少会遇到莫名其妙的服务器独有规则。 比如你服务本身跑的好好的,突然啪的一下,没了! 什么原因导致的呢?其中,很大可能是定时任务在作祟。 原因分析 本次,我遇到的问题是:在Ubuntu系…