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

news2024/11/26 17:29:30

【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/2190172.html

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

相关文章

[python]Flask_Login

flask_login是flask框架中的一个拓展功能&#xff0c;用于更快捷的实现用户会话管理功能&#xff0c;主要处理登录&#xff0c;注销和长时间会话存储的功能处理。 目录 安装 使用 第一步,配置SECRET_KEY 第二步,创建LoginManager实例绑定app 第三步,用户类继承UserMixin …

Github优质项目推荐-第三期

文章目录 Github优质项目推荐 - 第三期一、【coding-interview-university】&#xff0c;305k stars - 软件工程师学习计划二、【drawdb】&#xff0c;20.1k stars - 数据库设计工具三、【twenty】&#xff0c;16k stars - 排名第一的开源 CRM四、【Douyin-Vue】&#xff0c;9.…

海南网站建设提升网站用户体验实用技巧

海南网站建设提升网站用户体验实用技巧 在当今数字时代&#xff0c;网站已成为企业展示形象和吸引客户的重要平台。尤其对于海南这一旅游胜地来说&#xff0c;优化网站用户体验显得尤为重要。以下是一些实用技巧&#xff0c;可帮助您提升网站的用户体验。 首先&#xff0c;确保…

柔性作业车间调度(FJSP)

1.1 调度问题的研究背景 生产调度是指针对一项可分解的工作(如产品制造),在尽可能满足工艺路线、资源情况、交货期等约束条件的前提下,通过下达生产指令,安排其组成部分(操作)所使用的资源、加工时间及加工的先后顺序,以获得产品制造时间或成本最优化的一项工作。 一般研究车间…

django的路由分发

前言&#xff1a; 在前面我们已经学习了基础的Django了&#xff0c;今天我们将继续学习&#xff0c;我们今天学习的是路由分发&#xff1a; 路由分发是Web框架中的一个核心概念&#xff0c;它指的是将不同的URL请求映射到对应的处理函数&#xff08;视图&#xff09;的过程。…

发现一篇瑞芯微RK3588上使用Gstreamer的文章(野火)

1. 前言 最近经常使用英伟达的Orin和瑞芯微RK3588做开发,自己还买了好几块开发板,很多需要自己琢磨,今天忽然发现了一篇文章,意外解决了一些之前的问题,以此作为记录: 文章链接:https://doc.embedfire.com/linux/rk356x/quick_start/zh/latest/lubancat_rk_software_har…

Redis基础三(redis的高级配置)

Redis进阶配置 一、Redis持久化操作 ​ 持久化就是把内存的数据写到磁盘中去&#xff0c;防止服务宕机了内存数据丢失。&#xff08;Redis 数据都放在内存中。如果机器挂掉&#xff0c;内存的数据就不存在。所以需要做持久化&#xff0c;将内存中的数据保存在磁盘&#xff0c…

Gralloc图形缓冲的分配过程

广告 首先帮我朋友打个广告 我们一起在运营一个视频号 感兴趣的可以帮忙点击右边这个小铃铛 铃铛 序 其实越往底下走在很多人嘴里就会变得很玄乎&#xff0c;变得不可思议&#xff0c;这里的gralloc就是一个native service&#xff0c;只是分装了一些调用接口&#xff0c;上…

使用Scikit-image进行图像处理入门

简介 在数据科学的广阔领域中&#xff0c;图像处理占据了重要的一席之地&#xff0c;为分析和处理视觉数据提供了各种工具和技术。Python 拥有丰富的库生态系统&#xff0c;为图像处理提供了多种选择&#xff0c;其中&#xff0c;scikit-image 凭借其强大且易用的功能脱颖而出…

记录使用gym和stable_baseline3训练出成功通关的贪吃蛇ai

参考自b站up林亦LYi的开源项目 传送门 本次只训练了cnn版本的 第一次接触这种项目&#xff0c;建python虚拟环境时出了点难以说清楚的小问题&#xff0c;安装不上requirement.txt中的gym库那个版本&#xff0c;折腾了一会&#xff0c;自己都乱了头绪&#xff0c;最后导致训练…

TMGM:黄金价格持稳,而WTI原油价格和天然气价格飙升

黄金价格顽强地拒绝下跌&#xff0c;昨天和周三继续抵制下行的趋势。 价格继续保持在近期的历史高位附近&#xff0c;很可能会有进一步上涨的动力。美元走强阻止了价格的进一步上涨&#xff0c;但总体上升趋势仍在进行中。 目前还没有明显的向下催化剂迹象&#xff0c;不过如…

c++联合体

// // Created by 徐昌真 on 2024/10/5. // #include <iostream> using namespace std;//定义一个结构体 struct DataS{ //内存空间独立int a;double b;char c[10]; };//定义一个联合体 union DataU{ //内存空间在一起int a;double b;char c[10]; };int main() {//创建…

Pikachu-目录遍历

目录遍历&#xff0c;跟不安全文件上传下载有差不多&#xff1b; 访问 jarheads.php 、truman.php 都是通过 get 请求&#xff0c;往title 参数传参&#xff1b; 在后台&#xff0c;可以看到 jarheads.php 、truman.php所在目录&#xff1a; /var/www/html/vul/dir/soup 图片…

奥博思软件总经理刘玉军受邀为项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 北京奥博思软件技术有限公司联合创始人、总经理刘玉军先生受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“高效协作的秘密&#xff1a;项目管理平台助力跨部门协作”。大会将于…

信息学奥赛复赛复习12-CSP-J2021-01分糖果-模运算、余数、打擂台求最值、最大值、最小值

PDF文档回复:20241005 **1 P7909 [CSP-J 2021] 分糖果 ** [题目描述] 红太阳幼儿园有 n 个小朋友&#xff0c;你是其中之一。保证 n≥2 有一天你在幼儿园的后花园里发现无穷多颗糖果&#xff0c;你打算拿一些糖果回去分给幼儿园的小朋友们 由于你只是个平平无奇的幼儿园小朋…

【数学分析笔记】第4章第4节 复合函数求导法则及其应用(2)

4. 微分 4.4 复合函数求导法则及其应用 【例4.4.3】 y e 1 cos ⁡ x ye^{\sqrt{1\cos x}} ye1cosx ​&#xff0c;求 y ′ y y′ 【解】 y ′ e 1 cos ⁡ x ⋅ 1 2 1 cos ⁡ x ⋅ ( − sin ⁡ x ) − sin ⁡ x 2 1 cos ⁡ x e 1 cos ⁡ x ye^{\sqrt{1\cos x}}\cdot\f…

【LeetCode】每日一题 2024_10_5 完成旅途的最少时间(二分答案)

前言 每天和你一起刷 LeetCode 每日一题~ 大家国庆节快乐呀~ LeetCode 启动&#xff01; 突然发现&#xff0c;国庆的每日一题&#xff0c;不是坐公交就是坐火车&#xff0c;不是坐火车就是做飞机&#xff0c;这就是你的国庆旅游计划吗&#xff01;力扣&#xff01; 题目&a…

今日凌晨,ChatGPT重磅更新!—— 我心目中的终极AGI界面

今日凌晨&#xff0c;ChatGPT重磅更新&#xff01;—— 我心目中的终极AGI界面 我心目中的终极 AGI 界面是一张空白画布&#xff08;canvas&#xff09;。 今日凌晨&#xff0c;OpenAI 发布 canvas&#xff0c;一个与 ChatGPT 合作写作和编程的新界面&#xff01; canvas&…

Linux基于CentOS学习【进程状态】【进程优先级】【调度与切换】【进程挂起】【进程饥饿】

目录 进程状态 状态决定了什么 进程等待方式——队列 进程状态的表现 挂起状态 基于阻塞的挂起——阻塞挂起 swap分区 进程状态表示 Z僵尸状态 进程的优先级 什么是进程的优先级 为什么会有进程的优先级 进程饥饿 Linux的调度与切换 切换 调度 queue [ 140 ]&am…

模拟算法(3)_Z字形变换

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 模拟算法(3)_Z字形变换 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 题目链…