《类和对象:基础原理全解析(上篇)》

目录

  • 一、浅谈面向过程和面向对象
  • 二、C++ 中的结构体(struct)
    • 1. C++ 中 struct 的使用
  • 三、C++ 中的类(class)
  • 四、类的封装性
    • 1. 类成员的权限控制关键字
    • 2. 权限控制关键字的使用
  • 五、类的六大默认成员函数介绍
  • 六、构造函数
    • 1. 使用构造函数
    • 2. 默认构造函数
  • 七、类作用域
  • 八、类中函数的声明和定义
  • 九、类的实例化
  • 十、类的大小计算

一、浅谈面向过程和面向对象

  C 语言是面向过程的语言,而 C++ 是面向对象的语言。并且当前主流的语言大多数都是面向过程的语言,这足以证明面向过程的语言更适合当前时代的发展。

  面向过程在解决问题时主要关注解决问题的过程,也就是解决问题的步骤,把每个步骤抽象成一个函数,然后传入对应的数据调用这些函数一步一步解决问题。而面向对象在解决问题时主要关注问题涉及的对象,把每个对象、相关的数据(成员变量)和能进行的操作(成员函数)封装成一个类。接着创建每个类的对象实例用来存储数据和执行相应的操作。

  就拿玩游戏闯关打 BOSS 来举例子,如果是面向过程的游戏,那么就需要按照顺序从第一关,第二关,…,第 N 关,BOSS。你需要按照策划设定好的过程一关一关地通过,最后面对 BOSS 。而如果是面向对象的游戏,那么就抽象出来了三个类:玩家、小怪和BOSS,小怪和 BOSS 的设定是如果与玩家的距离在 N 之内或者玩家主动攻击,那么小怪和 BOSS 就会反击。这样游戏就多了很多可能性,比如玩家只通过了一部分关卡就去打 BOSS 或者上来就攻打 BOSS。

  通过上述分析可以得出,面向过程的语言关注的是过程,把一个问题分解成多个步骤,调用函数逐步解决。而面向对象是把问题中涉及的对象(包括其属性和操作)抽象出来,通过对象之间的交互来完成。

二、C++ 中的结构体(struct)

  C++ 中的 struct 中不仅能包含成员变量还能包含成员函数,因为 C++ 中把 struct 当作类来处理。

1. C++ 中 struct 的使用

  下面是一个 C++ 中的学生 struct :

// 头文件
#include <iostream>// 常量声明
const int SIZE_NAME = 20;
const int SIZE_ID = 11;// 使用声明
using std::cout;
using std::endl;// 学生结构声明
struct Student
{// 成员变量char _name[SIZE_NAME];size_t _age;char ID[SIZE_ID];// 成员函数void PrintInfo(){cout << _name << " " << _age << " " << ID << endl;}
};int main()
{// 创建结构体变量Student zhangsan = { "张三", 18, "2101240002" };// 调用成员函数zhangsan.PrintInfo();return 0;
}

  程序的运行结果如下:
在这里插入图片描述
  从上面的代码中可以看到在创建结构体变量时,直接使用 Student 而不是 C 语言中的 struct Student,这是因为 C++ 中把结构体当作类处理,而 Student 是一个类名。但是由于 C++ 兼容 C 语言,所以原来 C 语言的 struct Student 语法依旧可以使用。

  结构体对象(变量)通过成员运算符(.)来访问成员变量和调用成员函数。

三、C++ 中的类(class)

  C++ 中的类由描述类的成员变量和能对类进行操作的成员函数组成。

  C++ 中的类的创建类似 struct,只不过是把 struct 改成了 class,把结构体名称改为了类名。
class classname
{
  //…
};

  下面依旧是学生结构体:

// 头文件
#include <iostream>// 常量声明
const int SIZE_NAME = 20;
const int SIZE_ID = 11;// 使用声明
using std::cout;
using std::endl;// 学生类
class Student
{// 成员变量char name[SIZE_NAME];size_t age;char ID[SIZE_ID];// 成员函数void Print(){cout << name << " " << age << " " << ID << endl;}
};int main()
{Student zhangsan = { "zhangsan", 18, "2101240002" };// 调用成员函数zhangsan.Print();return 0;
}

  可以看到上面的代码与 struct 中的代码除了一个是结构体一个是类,其他都没有区别,但是为什么使用类时编译器报错了呢?
在这里插入图片描述
  上面的问题就涉及类的封装性和六大默认函数了。

四、类的封装性

  什么是封装性?就拿王者荣耀来说,在面向对象设计中,玩家是一个类,该类包含了玩家的属性(金币、点券、对局数等信息)和玩家能进行的操作(娱乐、匹配、排位等)。那么玩家的属性就是玩家类的成员变量,玩家能进行的操作就是玩家类的成员函数。玩家可以调用成员函数进行对局,但是玩家不能访问成员变量修改点券,只能通过成员函数(充值)从而间接修改点券。如果可以直接访问玩家属性,那么王者荣耀就无法进行游戏管理和盈利。

  类的封装性通过隐藏类的成员变量,展示类的成员函数,让用户通过成员函数来访问成员变量,这样用户的操作就比较规范合理。也可以这样理解,类的设计者可以通过类的封装性,让用户可以看见设计者想让用户看见的,也可以让用户看不见设计者不想让用户看见的。

1. 类成员的权限控制关键字

  类成员有 public(公有的)、private(私有的)和 protected(受保护的)。public 成员可以在类外通过类对象进行访问,private 只能在类内进行访问,而在当前的学习阶段只需要把 protected 当作 private 理解就行。protected 在后面的类继承中才会有其他作用。

  现在来解释一下为什么上面使用 class 定义的 Student 类不能正常使用,而使用 struct 定义的 Student 却能正常使用。因为如果不显示指明权限,class 默认权限为 private,而 struct 默认权限为 public。

2. 权限控制关键字的使用

  下面是使用了权限关键字之后可以正常使用的 Student 类:
在这里插入图片描述
  虽然现在已经加上了权限关键字,但是上述代码仍存在问题。成员函数 Print() 的权限为 public,所以现在不存在函数调用的问题。而是类对象初始化的问题,这就涉及到六大默认函数中的构造函数了。

五、类的六大默认成员函数介绍

  六大默认成员函数函数分别为:构造函数、析构函数、拷贝构造函数、赋值运算符重载、普通对象和 const 对象取地址运算符重载。

  为什么称呼为默认成员函数?是因为如果程序员不显式定义这些函数,那么编译器会自动生成对应的默认函数。

  主要需要学习前四个默认函数,而最后两个很少自己重新定义。

六、构造函数

  构造函数的作用是在创建类对象的同时对其初始化,而不是创建类对象。且构造函数的函数名和类名相同,没有返回值,也不写 void。

  且在初始化对象时,无需使用成员运算符(.),直接在后面接上圆括号然后传入对应参数即可(中间用逗号隔开)。如果没有参数,直接创建对象即可,无需使用圆括号(前提是存在默认构造函数)。

  构造函数可以重载,且构造函数是编译器在创建类的对象时根据初始化的数据自动调用的。

1. 使用构造函数

  下面使用构造函数纠正 Student 类的最后一个错误。

// 头文件
#include <iostream>
#include <string>// 常量声明
const int SIZE_NAME = 20;
const int SIZE_ID = 11;// 使用声明
using std::cout;
using std::endl;// Student 类
class Student
{
private:char _name[SIZE_NAME];size_t _age;char _ID[SIZE_ID];public:// 构造函数Student(const char* name, size_t age, const char* ID){strcpy(_name, name);_age = age;strcpy(_ID, ID);}// 打印信息void Print(){cout << _name << " " << _age << " " << _ID << endl;}
};int main()
{// 自动调用构造函数Student zhangsan("zhangsan", 18, "2101240002");// 打印信息zhangsan.Print();return 0;
}

  程序的运行结果如下:
在这里插入图片描述

  虽然上述程序可以正常运行,但是仍然存在 bug。如:Student zhangsan;,这条语句没有对应的构造函数,因为创建该对象是没有传递参数。

  如上述这种创建参数时不显式传递参数,编译器实际上会去寻找默认构造函数。

2. 默认构造函数

  默认构造函数就是不需要传递参数的构造函数,可以是全缺省,也可以是无参函数。

  当用户未定义任何构造函数时,编译器会生成一个默认构造函数,该函数没有参数,且该函数不对内置类型(int、double等)进行处理,对自定义类型调用其默认构造函数。

  默认构造函数有以下三种:编译器自动生成的默认构造函数、无参构造函数和全缺省构造函数。但是这三种构造函数只能存在一种。

  下面是使用了默认构造函数之后的 Student 类。

// 头文件
#include <iostream>
#include <string>// 常量声明
const int SIZE_NAME = 20;
const int SIZE_ID = 11;// 使用声明
using std::cout;
using std::endl;// Student 类
class Student
{
private:char _name[SIZE_NAME];size_t _age;char _ID[SIZE_ID];public:// 默认构造函数Student(){strcpy(_name, "小明");_age = 18;strcpy(_ID, "2101240001");}// 构造函数Student(const char* name, size_t age, const char* ID){strcpy(_name, name);_age = age;strcpy(_ID, ID);}// 打印信息void Print(){cout << _name << " " << _age << " " << _ID << endl;}
};int main()
{// 默认构造函数Student xiaoming;xiaoming.Print();// 构造函数Student zhangsan("张三", 19, "2101240002");zhangsan.Print();return 0;
}

  程序的运行结果如下:
在这里插入图片描述
  在创建小明这个对象的时候,虽然看着像创建了一个未初始化的局部变量,但是实际上编译器为其调用了默认构造函数。

七、类作用域

  类有属于自己的作用域,和命名空间 namespace 类似。可以在类中定义与外部全局函数相同的函数名。
在这里插入图片描述
  跟命名空间 namespace 一个理解就好了,相当于创建了一个新的作用域。只要不再同一个作用域,名称相同就没有问题。当然,函数重载不算。

  由于类中是一个新的作用域,所以类中的成员函数在类外定义时,需要显示声明该函数所属的类域。

八、类中函数的声明和定义

  如果在类中直接定义函数,那么相当于内联函数(inline),也就是在函数前面加了个关键字 inline。当然也只是给编译器一个建议,并不是直接就是内联函数。

  所以通常都是在类中声明函数,在类外定义函数,而在类外定义函数又有所不同。如果在类内声明函数,在类外定义函数,那么在类外定义函数时需要指明该函数属于哪个类,否则编译器会当作全局函数处理。

九、类的实例化

  使用 class 创建一个类时,实际上只是该类的声明,并没有创建空间。参照结构体,使用 struct 创建一个结构体时,只是告诉编译器我要创建一个类型,该类型包含如下内容。然后使用该类型创建具体的变量时,编译器才会开辟空间。

  所以当使用类创建具体的对象时,编译器才会分配具体的空间。该过程也叫做类的实例化。

十、类的大小计算

  类大小的计算和结构体类似,也存在内存对其。但是计算类大小的时候并不包括其中的函数,因为每个类的实例化对象都有其各自的成员变量数据,但是调用的都是同一个函数,所以类的函数保存在公共的代码段,所有类的实例化对象均可共享。

// 头文件
#include <iostream>// 使用声明
using std::cout;
using std::endl;// Date 类声明
class Date
{
private:size_t _year;size_t _month;size_t _day;public:Date(size_t year = 1949, size_t month = 10, size_t day = 1);  // 默认构造函数void Print();
};// Date 类成员函数定义
Date::Date(size_t year, size_t month, size_t day)
{_year = year;_month = month;_day = day;
}void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}int main()
{// 计算 Date 类的大小cout << sizeof(Date) << endl;return 0;
}

  代码的运行结果如下:
在这里插入图片描述
  由于我的编译器上 size_t 是 64 位的整数,所以结果算出来是 24 个字节。

  上面的内存计算还是很简单的,大家自己复习一下内存对其相关的知识。

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

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

相关文章

电子应用设计方案-59:智能电动床系统方案设计

智能电动床系统方案设计 一、引言 智能电动床作为一种高端家居产品&#xff0c;旨在为用户提供更加舒适、便捷和个性化的睡眠体验。本方案将详细介绍智能电动床系统的设计架构、功能特点和技术实现。 二、系统概述 1. 系统目标 - 实现床体的多部位电动调节&#xff0c;满足不…

电商环境下的财务ERP系统架构

先介绍一下自己的工作经历&#xff0c;2002年开始进入ERP实施行业&#xff0c;专注于O记EBS系统&#xff0c;正好赶上中国经济和信息化高度发展的阶段&#xff0c;先后实施过很多大国企和民企的大型ERP项目&#xff0c;在实施过程中逐渐对ERP系统的架构、模块设计有更深入的认识…

【WRF教程第3.2期】预处理系统 WPS详解:以4.5版本为例

预处理系统 WPS 详解&#xff1a;以4.5版本为例 WPS 嵌套域&#xff08;WPS Nested Domains&#xff09;USGS 和 MODIS 土地利用重力波拖拽方案静态数据&#xff08;Gravity Wave Drag Scheme Static Data&#xff09;1. 什么是重力波拖拽方案&#xff08;GWDO&#xff09;静态…

在Proteus软件仿真STM32F103寄存器方式PID调速电机

因为电脑中只装了IAR&#xff0c;所以本次编译环境就只能是IAR&#xff0c;所用软件版本是9.32.1。 本次仿真为&#xff0c;纯手写代码&#xff0c;不用任何库&#xff0c;包括启动文件也是手写。 首先是启动文件&#xff0c;该文件是汇编文件&#xff0c;命名为start.s&…

QtitanChart组件——高效、灵活的Qt数据可视化解决方案

在现代应用开发中&#xff0c;数据可视化已经成为不可或缺的一部分。无论是商业分析工具、财务报表、工程图表&#xff0c;还是科学实验数据展示&#xff0c;如何以直观、易理解的方式展示数据&#xff0c;往往决定了软件的可用性与用户体验。对于Qt开发者来说&#xff0c;Qtit…

YOLOv11融合[CVPR2024]Starnet中的star block特征提取模块

YOLOv11v10v8使用教程&#xff1a; YOLOv11入门到入土使用教程 YOLOv11改进汇总贴&#xff1a;YOLOv11及自研模型更新汇总 《Rewrite the Stars》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/abs/2403.19967 代码链接&#xff1a;https://github.com/ma-xu/Rewri…

LabVIEW农机自主导航监控系统

随着现代农业技术的快速发展&#xff0c;自主导航农机的需求日益增加&#xff0c;提高作业效率和减少劳动成本成为农业现代化的关键目标。本文介绍了一个基于LabVIEW的农机自主导航监控系统的开发案例&#xff0c;该系统通过先进的传感器与控制技术&#xff0c;实现农机在田间作…

JAVA:代理模式(Proxy Pattern)的技术指南

1、简述 代理模式(Proxy Pattern)是一种结构型设计模式,用于为其他对象提供一种代理,以控制对这个对象的访问。通过代理模式,我们可以在不修改目标对象代码的情况下扩展功能,满足特定的需求。 设计模式样例:https://gitee.com/lhdxhl/design-pattern-example.git 2、什…

番外篇 | Hyper-YOLO:超图计算与YOLO架构相结合成为目标检测新的SOTA !

前言:Hello大家好,我是小哥谈。Hyper-YOLO,该方法融合了超图计算以捕捉视觉特征之间复杂的高阶关联。传统的YOLO模型虽然功能强大,但其颈部设计存在局限性,限制了跨层特征的融合以及高阶特征关系的利用。Hyper-YOLO在骨干和颈部的联合增强下,成为一个突破性的架构。在COC…

php基础:正则表达式

1.正则表达式 正则表达式是用于描述字符排列和匹配模式的一种语法规则。它主要用于字符串的模式分割、匹配、查找及替换操作。到目前为止&#xff0c;我们前面所用过的精确&#xff08;文本&#xff09;匹配也是一种正则表达式。 在PHP中&#xff0c;正则表达式一般是由正规字…

Postman接口测试:全局变量/接口关联/加密/解密

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 全局变量和环境变量 全局变量&#xff1a;在postman全局生效的变量&#xff0c;全局唯一 环境变量&#xff1a;在特定环境下生效的变量&#xff0c;本环境内唯一 …

vue响应式数据-修改对象的属性值,视图不更新

如图&#xff1a; 一&#xff1a;问题是&#xff1a; 我把数据处理后能console.log()打印出来&#xff0c;但是页面的内容不能同步的更新渲染&#xff1b; 二&#xff1a;要求&#xff1a; 在数组循环列表里面&#xff0c;我点击单个的item按钮时&#xff0c;需要实时加载进度…

configure错误:“C compiler cannot create executables“

执行./configure命令出现如下奇怪的错误&#xff0c;百思不得姐&#xff1a; ./configure命令的日志文件为config.log&#xff0c;发生错误时&#xff0c;该文件的内容&#xff1a; This file contains any messages produced by compilers while running configure, to aid d…

【Java学习笔记】多线程基础

并行&#xff1a;同一时刻&#xff0c;多任务同时进行 多任务分别进行 一、线程相关概念 1.程序 是为完成特定任务、用某种语言编写的一组指令的集合。 简单的说:就是我们写的代码 2.进程 &#xff08;1&#xff09;进程指的就是运行中的程序&#xff0c;比如我们使用QQ,就…

PyTorch基础入门

目录 前言一、[张量的广播&基本运算](https://www.bilibili.com/video/BV1Gg411u7Lr/?spm_id_from333.999.0.0)1. 张量的广播特性2. 逐点&规约&比较运算 二、张量的线性代数运算1. BLAS & LAPACK2. 矩阵形变及特殊矩阵构造3. 矩阵基本运算4. 矩阵的线性代数运…

XXE靶机攻略

XXE-Lab靶场 1.随便输入账号密码 2.使用bp抓包 3.插入xml代码&#xff0c;得到结果 xxe靶机 1.安装好靶机&#xff0c;然后输入arp-scan -l&#xff0c;查找ip 2.输入ip 3.使用御剑扫描子域名 4.输入子域名 5.输入账号密码抓包 6.插入xml代码 7.使用工具解码 8.解码完毕放入文…

计算机知识笔试

一、计算机网络 1.网络分类 树型、总线型、环型一般是局域网所用的拓扑结构&#xff0c;广域网和远程计算机网络普遍采用网状拓扑结构。 2.OSI模型各层功能 比特、帧、端到端 3.传输层协议 TCP:传输控制协议 UDP:用户数据报协议 传输层、可靠、连接 4.网络层协议 IP协议是…

设计规规范:【App 配色】

文章目录 引言I App 配色组成色彩象征 & 联想II 知识扩展设计流程图UI设计交互设计UI交互设计引言 设计规范,保持设计一致性,提高设计效率。宏观上对内统一,管理与合作变得容易。 按类型管理颜色、文本样式、图标、组件(symbol)。 蓝湖设计规范云 https://lanhuapp.co…

计算生物学与系统生物学

计算生物学 计算生物学是一个利用数学、统计学、计算机科学和生物学知识来解决生物学问题的学科。它的核心目标是通过开发和应用计算方法来分析和解读大量的生物数据&#xff0c;以揭示生命现象的规律和机制。 1. 基因组学分析 计算生物学在基因组学中的应用主要集中在基因的…

timestamp 时间戳转换成日期的方法 | java.util

时间戳通常是一个long数据&#xff08;注意java中赋值时需要带上L标识是long整型&#xff0c;否则int过长报错&#xff09; 代码实现 常用工具类&#xff1a; java.util.Datejava.time.Instantjava.time.format.DateTimeFormatter toInstant() 方法的功能是将一个 Date 对象…