【C++】——类和对象(上)

news2024/10/3 17:02:34

【C++】——类和对象(上)

文章目录

  • 【C++】——类和对象(上)
    • 前言
    • 1. 类的定义
      • 1.1 类定义格式
      • 1.2 访问限定符
      • 1.3 类域
    • 2. 实例化
      • 2.1 实例化概念
      • 2.2 对象的大小
    • 3. this指针
    • 4. C++和C语言实现Stack对比
    • 结语

前言

小伙伴们大家好呀,今天我们就开始学习C++的重点及难点——类和对象

这一章的内容非常的重要,让我们一起来揭开它神秘的面纱,好好看看吧

1. 类的定义

1.1 类定义格式

C++中类的定义与C语言中的结构体十分相似,像结构体一样,class是类的关键字,Date是类的名字,类体中内容称为类的成员:类中的变量称为类的属性或成员变量,类中的函数称为类的方法或者成员函数

比方说,我们写日期类应该怎么写呢

class Date
{
    void DateInit(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    int _year;
    int _month;
    int _day;
};

一个日期类是不是这样写的

需要注意的是

  • 成员变量和函数可以在类位置可以任意顺序,一般是成员在下,函数在上
  • 注意类定义结束时后面分号不能略
  • 为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前面或者后面加_或者m开头,注意C++中这个并不是强制的,只是⼀些惯例,具体看公司的要求
  • C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化是struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类
  • 类的名字就是类的类型。所以我们定义类直接用类名定义即可
  • 定义在类面的成员函数默认为inline(内联)。但声明和定义分离,声明在类里就不是内联了

1.2 访问限定符

访问限定符有三种:

  • private(私有)
  • protected(保护)
  • public(共有)

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

所以我们可以把日期类改成:

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

通常情况下,我们不希望类中的成员变量被修改,所以使用私有private修饰,而对于成员函数我们想要在类外部进行对成员函数的调用,所以我们将成员函数进行公有修饰public

关于访问限定符我们要注意的是

  • public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private目前可以认为是⼀样的
  • 访问权限作用域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为止,如果后面没有访问限定符,作用域就到 } 即类结束。一种访问可以出现多次。但一般同一作用域放在一起
  • class定义成员没有被访问限定符修饰时默认为private,struct默认为public
  • ⼀般成员变量都会被限制为private/protected,需要给别人使用的成员函数会放为public。因为一般我们不想别人修改我们数据,只需要给别人调用我的函数接口

1.3 类域

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

现在我们希望类看起来简洁一些,可以将成员函数的定义放到外面,在类中留下成员函数的声明

所以我们又可以把日期类改为:

class Date
{
public:
    void DateInit(int year, int month, int day);
private:
    int _year;
    int _month;
    int _day;
};

void Date::DateInit(int year, int month, int day)
{
    _year = year;
    _month = month;
    _day = day;
}

类域影响的是编译的查找规则,倘若不写上 Date:: ,那么编译器会把成员函数当作是全局函数,那么在编译的时候,就找不到函数体中的 _year , _month , _day,只有让编译器知道了这是一个类的成员函数,在编译的时候就会到类中去寻找 _year , _month , _day 这些变量

2. 实例化

2.1 实例化概念

类是对象进行⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时,才会分配空间

就好比说类是一张房子的设计图,而对象是根据图纸制造出来的房子, 设计图规划了有多少个房间,房间大小功能等,但是并没有实体的建筑存在,也不能住人,用设计图修建出房子,房子才能住人。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据

还是举一个例子

class Date
{
public:
    void DateInit(int year, int month, int day);
private:
    int _year;
    int _month;
    int _day;
};

void Date::DateInit(int year, int month, int day)
{
    _year = year;
    _month = month;
    _day = day;
}

int main()
{
    Date d1;
    d1.DateInit(2024, 10, 3);
    return 0;
}

现在我们在主函数中创建的 d1 ,就是Date类进行实例化出来的对象,只有创建对象之后,我们类中的成员变量才有了空间

那么,一个类对象的大小应该怎么计算呢,接下来我们看看类对象的大小

2.2 对象的大小

一个类对象大小还得看它里面存储的是啥?

那存储的是啥,类里面 只存储了类成员变量,不存储成员函数

我们知道,对象中的成员变量都是独立的,每个对象的成员变量不一定相同,而每个对象的成员函数也有不同吗?,对象中如果想储存成员函数,只能储存函数的指针,那么对于一样的函数,其函数指针也是一样的,那么就没必要将每个对象的成员函数都进行储存,否则就太浪费空间了,所以对象的大小不包括成员函数的大小

另外,对于对象的大小计算,也要符合内存对齐规则:

  • 第一个成员在与结构体偏移量为0的地址处
  • 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
  • 注意:对齐数=编译器默认的一个对齐数 与 该成员大小的较小值
  • VS中默认的对齐数为8
  • 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

来个题目试试

// 计算⼀下
A实例化的对象是多⼤?
 
class A
{
public:
    void Print()
    {
        cout << _ch << endl;
    }
private:
    char _ch;
    int _i;
};

在这里插入图片描述

偏移量:占8个字节空间

大家可以想一下为什么要内存对齐呢

在这里插入图片描述

现代计算机的CPU通常在特定的字节边界上访问数据(如4字节或8字节),如果数据未对齐,CPU可能需要进行多次内存访问才能读取完整的数据。这会减慢访问速度,因此通过对齐,可以优化内存访问的效率

在这里插入图片描述

理解了没,再来看看这个例子

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

class B
{
public:
    void Print()
    {
        //...
    }
};

class C
{};

int main()
{
    A a;
    B b;
    C c;
    cout << sizeof(a) << endl;
    cout << sizeof(b) << endl;
    cout << sizeof(c) << endl;
    return 0;
}

代码运行结果如下:
在这里插入图片描述

A是8没问题。可是B和C都没有成员变量,前面我们说类只存储成员变量

那他们没有为什么不是0呢

我们说给出的类这种没有成员变量的类,怎么证明我们创建的对象存在呢,所以计算出的大小为1,表示该对象存在

3. this指针

我们刚才讲到了,不同对象中的成员函数是没有区别的,那么在调用成员函数的时候,函数是怎么知道这是哪个对象调用的呢,那么这⾥就要看到C++给了⼀个隐含的this指针解决这里的问题

对于我们刚才日期类中的 DateInit 函数,表面上它有三个参数,可实际上还有一个隐含的参数,就是this指针

void DateInit(Date* const this, int year, int month, int day)
{
    this->_year = year;
    this->_month = month;
    this->_day = day;
}

类的成员函数中访问成员变量,本质都是通过this指针访问的,如函数中给 _year 赋值, this->_year = year,可见,this指针是存放对象地址的指针

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

来个题体会一下

this指针存在内存哪个区域的 ()

A. 栈 B.堆 C.静态区 D.常量区 E.对象里面

首先排出E选项。为什么?前面我们说了类对象里面只存储成员变量

其次又因为this指针是形参,形参是存储在栈帧里面,所以选A比较合理

但是VS下因为this指针会频繁使用,所以把this指针放在寄存器里面,这可以认为是VS做的优化

4. C++和C语言实现Stack对比

面向对象三大特性:封装、继承、多态,下面的对比我们可以初步了解⼀下封装

通过下面两份代码对比,我们发现C++实现Stack形态上还是发生了挺多的变化,底层和逻辑上没啥变化

C++实现栈:

void Push(STDataType x)
{
    if (_top == _capacity)
    {
        int newcapacity = _capacity * 2;
        STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
            sizeof(STDataType));
        if (tmp == NULL)
        {
            perror("realloc fail");
            return;
        }
        _a = tmp;
        _capacity = newcapacity;
    }
    _a[_top++] = x;
}
void Pop()
{
    assert(_top > 0);
    --_top;
}
bool Empty()
{
    return _top == 0;
}
int Top()
{
    assert(_top > 0);
    return _a[_top - 1];
}
void Destroy()
{
    free(_a);
    _a = nullptr;
    _top = _capacity = 0;
}

C语言实现栈:

void STDestroy(ST* ps)
{
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->top = ps->capacity = 0;
}
void STPush(ST* ps, STDataType x)
{
    assert(ps);
    // 满了,扩容
    if (ps->top == ps->capacity)
    {
        int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
        STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity *
            sizeof(STDataType));
        if (tmp == NULL)
        {
            perror("realloc fail");
            return;
        }
        ps->a = tmp;
        ps->capacity = newcapacity;
    }
    ps->a[ps->top] = x;
    ps->top++;
}
bool STEmpty(ST* ps)
{
    assert(ps);
    return ps->top == 0;
}
void STPop(ST* ps)
{
    assert(ps);
    assert(!STEmpty(ps));
    ps->top--;
}
STDataType STTop(ST* ps)
{
    assert(ps);
    assert(!STEmpty(ps));
    return ps->a[ps->top - 1];
}

C++中数据和函数都放到了类里面,通过访问限定符进行了限制,不能再随意通过对象直接修改数据,这是C++封装的一种体现,这个是最重要的变化。这里的封装的本质是⼀种更严格规范的管理,避免出现乱访问修改的问题。当然封装不仅仅是这样的,我们后面还需要不断的去学习

C++中有⼀些相对方便的语法,比如Init给的缺省参数会方便很多,成员函数每次不需要传对象地址,因为this指针隐含的传递了,方便了很多,使用类型不再需要typedef用类名就很方便

结语

这就是类和对象的初步了解,感觉是不是蛮简单的,类和对象这里内容比较多,大家就好好理解

好了,感谢你能看到这里,溜了溜了,我们下期再见吧

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为云+WordPress+Puock主题搭建个人博客

网站访问地址&#xff1a;qingxuly.cn 搭建网站 购买华为云服务器&#xff0c;购买域名&#xff0c;进行备案&#xff0c;配置域名解析等操作&#xff0c;请参考华为云文档。 安装Ubuntu系统 华为云控制台中给云服务器安装Ubuntu2204。 配置服务器安全组 华为云安全组中创建安…

用HTML CSS JS打造企业级官网-源码直接可用

页面效果&#xff1a;本前端网站应用多个JS特效 1.index.html&#xff1a; <!DOCTYPE html> <html lang"zxx"> <head><title>蓝网团队</title><meta name"viewport" content"widthdevice-width, initial-scale1&q…

C++之多线程

前言 多线程和多进程是并发编程的两个核心概念,它们在现代计算中都非常重要,尤其是在需要处理大量数据、提高程序性能和响应能力的场景中。 多线程的重要性: 资源利用率:多线程可以在单个进程中同时执行多个任务,这可以更有效地利用CPU资源,特别是在多核处理器上。 性…

量化交易backtrader实践(三)_指标与策略篇(2)_内置指标A开头

在第1节中&#xff0c;我们学习了移动平均线的原理&#xff0c;中位数以及正态分布的概念&#xff0c;并通过python手工做了一个双均线的策略回测。了解了怎么用pandas计算移动平均线&#xff08;rollingmean)&#xff0c;怎么得到某一列上1个的值&#xff08;shift)&#xff0…

DOM树(上) -- 第七课

文章目录 前言一、DOM是什么&#xff1f;二、基础用法1.DOM树2. 获取元素1. id2. 标签3. name4. HTMLS新增的获取的方法1. 根据类型获取2. 根据querySelector获取 3. 事件基础1. 概述2. 事件三要素3. 操作元素内容1. 操作内容2. 操作属性3. 案例 4. 操作元素样式5. 案例 -- 显示…

【JDK动态代理】JDK动态代理:为何只能代理接口和接口实现类

在Java开发中&#xff0c;JDK动态代理是一种非常有用的技术&#xff0c;它允许开发者在不修改目标类代码的情况下&#xff0c;为目标类添加额外的功能。然而&#xff0c;JDK动态代理的使用有一些限制&#xff0c;特别是它只能代理接口和接口实现类。本文将深入探讨这一限制的原…

​IAR全面支持国科环宇AS32X系列RISC-V车规MCU

全球领先的嵌入式系统开发软件解决方案供应商IAR与北京国科环宇科技股份有限公司&#xff08;以下简称”国科环宇”&#xff09;联合宣布&#xff0c;最新版本IAR Embedded Workbench for RISC-V将全面支持国科环宇AS32X系列RISC-V MCU&#xff0c;双方将共同助力中国汽车行业开…

云原生(四十一) | 阿里云ECS服务器介绍

文章目录 阿里云ECS服务器介绍 一、云计算概述 二、什么是公有云 三、公有云优缺点 1、优点 2、缺点 四、公有云品牌 五、市场占有率 六、阿里云ECS概述 七、阿里云ECS特点 阿里云ECS服务器介绍 一、云计算概述 云计算是一种按使用量付费的模式&#xff0c;这种模式…

捕获Net-NTLM HASH 实验

1. Responder监听 sudo ./Responder.py -I eth0 -wv2. 触发NTLM请求 0x01. LLMNR && NBNS协议 winR : //asdasdasd0x02. 打印机漏洞 krbrelayx下的printerbug.py&#xff1a; 注意&#xff0c;这个凭据不是DC的&#xff0c;只要是个域用户都可以。后面是攻击目标&…

Linux中的进程间通信之管道

管道 管道是Unix中最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道” 匿名管道 #include <unistd.h> 功能:创建一无名管道 原型 int pipe(int fd[2]); 参数 fd&#xff1a;文件描述符数组,其中fd[0]表示读端, fd[1]表示写端 …

p2p、分布式,区块链笔记: Libp2p分散打孔 2022 IEEE 第 42 届分布式计算系统国际会议研讨会 (ICDCSW)

Decentralized Hole Punching 我们提出了一种内置于对等网络库 libp2p [1] 中的去中心化打洞机制。打洞对于对等网络至关重要&#xff0c;它使每个参与者能够直接与任何其他参与者通信&#xff0c;尽管被防火墙和 NAT 隔开。去中心化的 libp2p 打洞协议利用了类似于 STUN&…

Vivado - BD(差分时钟、简单分频、RESET、KEY)

目录 1. 简介 1.1 要点 1.2 buffer 介绍 2. vivado 工程 2.1 Block Design 2.2 IBUFDS 2.3 BUFGCE_DIV 2.4 Processor System Reset 2.5 key_mod 2.6 led_drv 3. 编译与调试 3.1 XDC 3.2 Debug 4. 总结 1. 简介 1.1 要点 了解 Utility Buffer v2.2 中的 Buffer…

关于HTML 案例_个人简历展示01

案例效果展示 代码 <!DOCTYPE html> <lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>个人简历信息</title> </he…

C语言高阶【1】--动态内存管理【1】(可以灵活的申请和使用内存,它不香吗?)

本章概述 为什么要有动态内存分配&#xff1f;malloc函数和free函数calloc函数和realloc函数常见的动态内存的错误彩蛋时刻&#xff01;&#xff01;&#xff01; 为什么要有动态内存分配&#xff1f; 情况描述&#xff1a;当我们创建一个变量时&#xff0c;比如&#xff0c;i…

《中安未来护照阅读器 —— 机场高效通行的智慧之选》

在机场&#xff0c;高效与准确的旅客信息处理至关重要。中安未来护照阅读器&#xff0c;为机场带来全新的智能化体验。它能够快速准确地读取护照信息&#xff0c;自动识别多种证件类型&#xff0c;极大提高了值机、安检等环节的效率。无论是繁忙的国际航站楼&#xff0c;还是国…

51单片机的串口

目录 一、串口的介绍 1、硬件电路 二、51单片机的UART 1、串口参数及时序图 2、串口模式图 3、串口和中断系统结构图 4、串口相关寄存器 三、串口向电脑发送数据 1、通过STC-ISP软件 四、电脑通过串口控制LED 1、主函数 2、 UART串口通信模块 一、串口的介绍 串口是一…

倒排索引是什么

倒排索引 简单了解&#xff1a; 什么是正向索引? 基于文档id创建索引。查询词条时必须先找到文档&#xff0c;而后判断是否包含词条 什么是倒排索引? 对文档内容分词&#xff0c;对词条创建索引&#xff0c;并记录词条所在文档的信息。查询时先根据词条查询到文档id&#…

C++和OpenGL实现3D游戏编程【连载13】——多重纹理混合详解

🔥C++和OpenGL实现3D游戏编程【目录】 1、本节要实现的内容 前面说过纹理贴图能够大幅提升游戏画面质量,但纹理贴图是没有叠加的。在一些游戏场景中,要求将非常不同的多个纹理(如泥泞的褐色地面、绿草植密布的地面、碎石遍布的地面)叠加(混合)起来显示,实现纹理间能…

WPS(金山文档)与金蝶云星空通过HTTP实现连接

WPS(金山文档)通过HTTP与金蝶云星空实现数据互通 该方式不需要通过金蝶SDK webapi官方文档地址&#xff1a;https://vip.kingdee.com/article/407944297573586944?langzh-CN&productLineId1&isKnowledge2 一、两种方式 airscript脚本发送http请求和PY脚本编辑器发送…

SCoRe: 通过强化学习教导大语言模型进行自我纠错

大语言模型(LLMs)在推理任务中,如数学问题求解和编程,已经展现出了优秀的性能。尽管它们能力强大,但在实现能够通过计算和交互来改进其回答的算法方面仍然面临挑战。现有的自我纠错方法要么依赖于提示工程,要么需要使用额外的模型进行微调,但这些方法都有局限性,往往无法产生有…