四、面向对象2(30小时精通C++和外挂实战)

四、面向对象2(30小时精通C++和外挂实战)

  • B-01-对象的内存
  • B-02-构造函数
  • B-04-成员变量的初始化
  • B-05-析构函数
  • B-06-内存管理
  • B-07-类的声明和实现分离
  • B-08-命名空间
  • B-09-继承
  • B-10-成员访问权限

B-01-对象的内存

在这里插入图片描述

在C++中对象可以自由的放在3中地方,而其他面向对象的语言如java是放在堆空间的,C语言没有面向对象的概念,尽管它也可以申请堆空间

B-02-构造函数

只要是面向对象的语言基本上都有构造函数这个概念
构造函数是用来调用的

#include <iostream>
using namespace std;struct Person{int m_age;
};int main(){Person person;//创建了person对象,但m_age并未被初始化,此对象在栈空间,里面都是ccccperson.m_age = 0;//此处进行初始化Person person2;
person.m_age = 0;/Person person3;
person.m_age = 0;/getchar();return 0;
}

如果我们定义了很多的person对象,都要进行初始化,我们希望person对象一初始化出来就是0,如上,但这样写比较麻烦,一般我们写一个构造函数,像这些初始化的东西都放在构造函数中,这我们就不用每次创建对象都去写,构造函数会自动调用。

构造函数函数名和类名一样,没有返回值,此函数比较特殊,不能写void

struct Person{int m_age;
//下方为构造函数Person(){cout << "Person()" << endl;}
};int main(){Person person;//创建了person对象但m_age并未被初始化person.m_age = 0;//此处进行初始化Person person2;Person person3;getchar();return 0;
}

我们可以看到结构,构造函数调用了3次,对象创建了三次,说明构造函数是自动调用,我们可以在期内做一些初始化的事,如person.m_age = 0;,一创建对象,就会调用构造函数,就会初始化

构造函数,可以重载,可以传值,要想调用传值的构造函数,需要传值,如下

struct Person{int m_age;Person(){m_age = 0;cout << "Person()" << endl;}Person(int age){m_age = age;cout << "Person(int age)" << endl;}void display(){cout << m_age << endl;}
};int main(){Person person;//调用无参的构造函数person.display();Person person2(20);//调用有参构造函数并传值20person2.display();Person person3(30);//调用有参构造函数并传值30person3.display();getchar();return 0;
}

一旦自定义了一个构造函数,必须选择其中一个自定义构造函数来初始化person对象

struct Person{int m_age;Person(){cout << "person()" << endl;}
};int main(){//Person person;//向栈空间申请Person *p = new Person;//向堆空间申请对象内存,因为person对象只有一个int类型变量,int栈4个字节,所以此处是申请4个字节内存从堆空间中delete p;//此处为了不让内存泄露,将内存释放

无论person对象创建出来,在栈空间还是在堆空间,只要person对象创建完毕,就会强势调用构造函数去初始化它,就算是在全局区创建person对象也会调用构造函数。
只要创建对象都会调用构造函数初始化,只是person对象放的地方不一样,有的在堆空间、栈空间、全局区。

注意malloc来向堆空间申请对象内存,是不会调用构造函数的

我们要记住要想向堆空间申请内存,就要new不要malloc

Malloc这个函数C语言就有了,那时没有对象,所以不会调用构造函数,C语言没有构造函数这个概念。Malloc的作用只是申请堆空间,也没有初始化。
New是c++的。
Sizeof()只是用来将括号中的类型所占的字节数值返回

在这里插入图片描述

下面一段代码及其重要必须仔细阅读

struct Person{int m_age;Person(){m_age = 0;cout << "Person()" << endl;}Person(int age){m_age = age;cout << "Person(int age)" << endl;}
};Person g_person0;//调用无参构造函数,Person()Person g_person1();			//这不是创建对象,这是函数声明,所以不会调用构造函数Person g_person2(10);//调用有参构造函数int main(){Person g_person0;//调用无参构造函数,Person()Person g_person1();			//这不是创建对象,这是函数声明,所以不会调用构造函数Person g_person2(10);//调用有参构造函数Person *p0 = new Person;//调用无参构造函数,Person()Person *p1 = new Person();//调用无参构造函数,Person(),这不是函数声明Person *p2 = new Person(10);//调用有参构造函数/* 4个无参,3个有参,一共创建了7个person对象 */getchar();return 0;
}

B-04-成员变量的初始化

在这里插入图片描述

此处讨论不进行自定义构造函数来初始化,而是默认情况下它是否会自动初始化

#include <iostream>
using namespace std;struct Person{int m_age;
};//全局区(全局变量默认就是0,里面全是000000):成员变量初始化为0
Person g_person;int main(){//栈空间(默认是没有初始化的,全是cccccc):没有初始化成员变量(若直接运行会报错)//Person person;//堆空间//右侧无(),没有初始化成员变量Person *p0 = new Person;//右侧有(),成员变量初始化为0Person *p1 = new Person();cout << g_person.m_age << endl;//cout << person.m_age << endl;cout << p0->m_age << endl;cout << p1->m_age << endl;getchar();return 0;
}

那么我们有写构造函数,但里面不对其初始化,会对后面成员变量有影响吗,有

struct Person{int m_age;Person(){}
};

我们发现只有全局区依然为0,而new后面有无括号都无初始化,即堆空间无初始化

全局区的东西默认全是0,无论有无构造函数无影响
堆空间,一旦自定义构造函数,它就认为你有想法,就不帮助你初始化了

这些了解一下就行,这些都是语法。

struct Person{int m_age;Person(){memset(this, 0, sizeof(Person));}
};

memset(this, 0, sizeof(Person)); 无论Person中变量有多少,这一句话就全部清零
当创建一个对象时,如Person g_person;,会将g_person的地址传入this,将sizeof(Person)个字节全部清零

B-05-析构函数

跟构造函数是反过来的
在对象销毁的时候自动调用,一般用于完成对象的清理工作

创建对象时,自动调用构造函数,构造函数被调用了,是一个新的person对象诞生的象征。

在对象销毁的时候,可以认为对象内存被回收的时候,就会调用析构函数,在对象销毁之前内部也会进行清理工作

析构函数不能重载,不能传参,有且只有一个析构函数,它是一个person对象销毁的象征,我们可以通过此函数看对象的内存是否销毁

#include <iostream>
using namespace std;struct Person{int m_age;//新的person对象诞生的象征Person(){cout << "Person::Person()" << endl;}//一个person对象销毁的象征~Person(){cout << "~Person()" << endl;}
};int main(){Person person;getchar();return 0;
}

此时结果为Person::Person(),因为对象还活着,main函数并未执行完,getchar使其暂停了,我们可以将对象创建在一个作用域中,活着在另一函数中,这样可以看到函数执行完后,栈空间会回收,对象被销毁,析构函数被调用

int main(){{Person person; }getchar();return 0;
}

Malloc申请堆空间既不会调用构造函数,也不会调用析构函数

而new都可以

int main(){Person *p = new Person;		此处像堆空间申请,会构造delete(p);				主动释放内存,会析构getchar();return 0;
}

在全局区创建的对象是一直存在的,是没有办法看到它析构的

在这里插入图片描述

我们讲的是Intel汇编。

B-06-内存管理

#include <iostream>
using namespace std;struct Car{int m_price;Car(){m_price = 0;cout << "Car::Car()" << endl;}~Car(){cout << "Car::~Car()" << endl;}
};struct Person{int m_age;Car *m_car;//用来做初始化的工作Person(){m_age = 0;m_car = new Car();cout << "Person::Person()" << endl;}//用来做内存清理的工作~Person(){cout << "Person::~Person()" << endl;}
};int main(){{Person person;}getchar();return 0;
}
执行结果
Car::Car()
Person::Person()
Person::~Person()

我们发现有一个类的析构函数没有调用,这就说明一个问题,内存泄露

内存泄露:该释放的内存并没有释放,很明显,car对象的内存没有回收,它所占用的内存依然在

因为car对象是new出来的,它在堆空间,在堆空间不可能自动回收,必须主动调用delete,在哪个地方调用,这个car对象是在person类内部new出来的,所以我们应该在在person的析构函数中delete掉car。

因为person都没有了,那么在person中new的car也就没人用了,所以最合理的地方就在析构函数~person将其delete

析构函数就是用来做内存清理的,清理在类内部产生的堆空间

struct Person{int m_age;Car *m_car;//用来做初始化的工作Person(){m_age = 0;m_car = new Car();cout << "Person::Person()" << endl;}//用来做内存清理的工作~Person(){delete m_car;cout << "Person::~Person()" << endl;}
};

Person在,car就在,person没了这个car就没价值了,就将其干掉,这样很方便,不要将delete写在外面。
将来成员变量肯定是private,而成员函数,构造函数,析构函数是public。

在这里插入图片描述

上图,car类中的指针m_car会指向堆空间的m_price,上面这幅图很清晰

要分清栈空间的对象和堆空间的对象,当调用一个函数时,就会创建一个栈空间存储此函数中的局部变量,而当这个函数执行完后,栈空间就会被回收,里面的局部变量也会被回收。而使用new创建一个对象时,会将此对象的变量放在堆空间,若不delete就会一直存在

此处我们将{person p},这就相当于一个函数的调用,出了{}此函数执行完,栈空间内变量回收,而堆空间对象存在如下

在这里插入图片描述
在这里插入图片描述

上图全部在栈空间

在这里插入图片描述

此处创建的指针p在栈空间,person对象在堆空间,所以person对象中的所有变量都在堆空间

在这里插入图片描述

对象内部申请的堆空间,应在对象内部回收

B-07-类的声明和实现分离

之前写类的时候,将类的所有东西写在类里面。

#include <iostream>
using namespace std;class Person{
private:int m_age;
public:void setAge(int age){m_age = age;}int getAge(){return m_age;}Person(){m_age = 0;}~Person(){}
};int main(){getchar();return 0;
}

类中有封装(set函数,get函数),有构造函数Person(),也有析构函数~Person
上面是在类中,函数声明与实现都在,我们可以让其只有声明,而实现放在其他地方。
如下

class Person{
private:int m_age;
public:void setAge(int age);int getAge();Person();~Person();
};void Person::setAge(int age){}int Person::getAge(){return m_age;
}Person::Person(){m_age = 0;
}
Person::~Person(){}

在类的外面进行函数实现时,需要制定类名,上面写法表示,setAge(int age)是属于 Person类的

Person::必须放在函数名之前,这是告诉这个函数是属于哪个类的

再这里是讲明类的声明和实现是可以分离的,很多时候,我们将类的声明放到头文件.h,类的实现,我们会将其放到.cpp文件。

在VS2013中,我们在头文件或源文件右击—添加----类文件,名为Person
而在2015,右击添加新建项里面就有类文件。

我们右击头文件–添加–类 。名为Person,会自动创建头文件Person.h和c++文件Person.cpp

在Person.h中程序自定义为下

#pragma once
class Person{
private:int m_age;
public:void setAge(int age);int getAge();Person();~Person();
};

而在Person.cpp中的程序如下

#include "Person.h"void Person::setAge(int age){m_age = age;
}int Person::getAge(){return m_age;
}Person::Person(){m_age = 0;
}
Person::~Person(){}

注意头文件Person.h是需要包含的,否则会报错

当我们要使用这个Person类时,如何使用,在main.cpp中包含person的头文件即可。
注意自己的头文件要用双引号“”,而系统自带的头文件使用尖括号<>

#include <iostream>
#include "Person.h"
using namespace std;int main(){Person person;person.setAge(10);cout << person.getAge() << endl;getchar();return 0;
}

B-08-命名空间

命名空间可以用来避免命名冲突
当我们的类名相同但内容不同时,防止创建对象时不知道调用的那个类名,使用命名空间,将类放到命名空间

#include <iostream>
using namespace std;namespace MJ{int g_age;//命名空间的全局变量,尽管在命名空间内,但他也是全局变量void func(){}class Person{int m_age;int m_money;};
}class Person{int m_height;};int main(){MJ::g_age = 10;//访问命名空间中的全局变量MJ::func();//访问命名空间中的函数MJ::Person person;cout << sizeof(person) << endl;getchar();return 0;
}

如果我们要使用命名空间中的类写法如下

MJ::Person person;

我们访问命名空间中的类需要在前面加MJ,还有简单的方法如下,这样写意味着在整个main函数中都可以直接使用MJ命名空间的东西

int main(){using namespace MJ;
g_age = 10;	
func();//MJ::g_age = 10;//访问命名空间中的全局变量//MJ::func();//访问命名空间中的函数//MJ::Person person;cout << sizeof(person) << endl;getchar();return 0;
}
using namespace std;

这是系统的命名空间,我们比较常用的是cout\endl,若无using则需要

std::cout << 2 << std::endl;

Std是系统中的,我们从iostream向下找就能找到

命名空间不影响内存布局,像全局变量,堆栈空间等,都不影响,只是在访问的时候多了一个命名空间的前缀。

Using namespace 放置的位置不同,作用范围也不同,放置在函数内作用范围就在函数内
在这里插入图片描述

在这里插入图片描述

我们创建的所有函数,全局变量等默认都在全局空间中,全局空间没有名字调用时

::func();

默认情况下有一个最大的空间,

void func(){cout << "func()" << endl;
}namespace MJ{void func(){cout << "MJ::func()" << endl;}
}int main(){using namespace MJ;MJ::func();::func();getchar();return 0;
}

在这里插入图片描述

我们也可以在Person.h和Person.cpp中做好命名空间,使用相同名字,令其声明和实现分离。
Person.h文件中内容如下

#pragma oncenamespace MJ{class Person{public:Person();~Person();};
}

Person.cpp 文件内容如下

#include "Person.h"namespace MJ{Person::Person(){}Person::~Person(){}
}

其他编程语言也有命名空间的概念,但有所不同
如java
Java使用package包,使用的是包的概念

在c++中直接将类放到命名空间中,而java是将类放到不同名称的文件夹(如aa,bb)中
Java使用如下

Package com.mj.bb;
Person person1;

那么在c++中是不能这样的,在我们使用的时候,我们是不用include那个文件夹的,编译器只认识类。

B-09-继承

新建–windows桌面应用程序----21继承

#include <iostream>
using namespace std;struct Person{int m_age;void run(){}
};struct Student:Person{int m_score;void study(){}
};struct Worker:Person{int m_salary;void work(){}
};int main(){getchar();return 0;
}

将其共性的东西放在一个类person,在c++中继承很简单
struct Student:Person{} 相当于Student继承了Person这个类
将person的所有东西都继承过来,所有的成员变量及成员函数

Java中有基类,最基本的类,其他所有的类都会继承自它。类的老祖宗
在java中不写继承那个类,默认都是继承基类

c++中没有基类

在这里插入图片描述

在c++中相当于直接将父类的成员变量,成员函数拿过来
在这里插入图片描述

继承类的对象的内存分布
上图中创建的gs对象是占12个字节的。
父类的成员变量地址值会排在前面

在这里插入图片描述

B-10-成员访问权限

#include <iostream>
using namespace std;//我们可以更改下方的权限看程序报错信息,知道其作用
struct Person{
//private:
//protected:
public:int m_age;void run(){m_age = 5;}
};struct Student :Person{void study(){m_age = 10;		//因为继承Person,故可以直接m_age}
};int main(){Person person;person.m_age = 11;getchar();return 0;
}

我们一般选择共有继承,因为共有继承能很好的将原来父类的权限继承下来。

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

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

相关文章

【算法】插入排序 与 希尔排序 概念+图解+代码【Python C C++】

1.插入排序 1.1概念 插入排序(InsertionSort)&#xff0c;一般也被称为直接插入排序。 对于少量元素的排序&#xff0c;它是一个有效的算法。插入排序是一种最简单的排序方法&#xff0c;它的基本思想是将一个元素插入到已经排好序的有序表中&#xff0c;从而构造出一个新的…

mathtype7.4永久激活码(mathtype7永久注册码网盘下载)

大家好&#xff0c;我是你们的数学小能手&#xff01;今天我要安利一款超实用的工具——MathType&#xff0c;让你在数学的世界里游刃有余&#xff0c;轻松搞定各种公式和计算。准备好被种草了吗&#xff1f;跟我一起来瞧瞧吧&#xff01; MathType是理科生专用的必备工具&…

EXCEL 排名(RANK,COUNTIFS)

1.单列排序 需求描述&#xff1a;如有下面表格&#xff0c;需要按笔试成绩整体排名。 解决步骤&#xff1a; 我们使用RANK函数即可实现单列整体排名。 Number 选择第一列。 Ref 选择这一整列&#xff08;CtrlShift向下箭头、再按F4&#xff09;。 "确定"即可计算…

一键解锁百变发型!上交联合Tiamat震撼发布Stable-Hair发型移植黑科技!

Stable-Hair 是一种基于扩散的新型发型转移方法&#xff0c;可以稳健地转移各种现实世界的发型。在各种具有挑战性的发型上实现了高度详细和高保真度的转移&#xff0c;效果令人印象深刻&#xff0c;同时保留了原始身份内容和结构。 相关链接 论文链接: https://arxiv.org/pdf…

案例实践 | 基于长安链的福建省气象综合治理区块链平台

案例名称-【福建省气象综合治理区块链平台】 ■ 实施单位 福建福链科技有限公司 ■ 业主单位 福建省气象信息中心 ■ 上线时间 2023年10月 ■ 用户群体 福建省气象、防灾减灾相关单位 ■ 用户规模 全省2100余个气象站、气象局以及防灾减灾部门 案例背景与解决痛点 …

跟代码执行流程,读Megatron源码(四)megatron初始化脚本initialize.py之initialize_megatron()分布式环境初始化

在前文中&#xff0c;我们讲述了pretrain函数的执行流程&#xff0c;其首要步骤是megatron分组的初始化与环境的配置。本文将深入initialize_megatron函数源码&#xff0c;剖析其初始化分布式训练环境的内部机制。 注&#xff1a;在此假设读者具备3D并行相关知识 一. initiali…

【MARL】MADDPG + attention 实现(+论文解读)

文章目录 前言注意力机制论文里的attention回顾知识-MADDPG讲解1.Q的定义2.Q的恒等式3.论文里的attention4.好处 实现 和 修改结果展示原论文代码 翻改版修改后原maddpg代码 前言 导师让在MADDPG上加一个注意力机制&#xff0c;试了很多种&#xff0c;下面的参考的论文的效果最…

C++——保持原有库头文件不变的情况下,成功编译运行工程

问&#xff1a;想要保持原来库方式&#xff0c;应该怎么操作呢&#xff1f; 答&#xff1a;如果想保持原来的方式&#xff0c;则只需要将 库所在路径 tracker/detector/rknn_model_zoo/utils 加入到 工程库包含中即可。

基于jeecgboot-vue3的Flowable流程-自定义业务表单流程历史信息显示

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、对于自定义业务表单的流程历史记录信息做了调整&#xff0c;增加显示自定义业务表单 <el-tab-pane label"表单信息" name"form"><div v-if"customF…

ESP32开发进阶:OLED屏幕显示旋转的3D模型

一、硬件接线 我选择的是最常见的一块板子&#xff1a;ESP-WROOM-32&#xff0c;硬件接线如下&#xff1a; 21 - SDA 22 - SCL 二、Arduino端代码 我们使用Arduino和Adafruit SSD1306库在OLED显示屏上绘制和旋转一个3D立方体。 首先&#xff0c;定义立方体顶点和…

CSS(七)——CSS 列表和CSS Table(表格)

目录 CSS 列表 列表 作为列表项标记的图像 列表 - 简写属性 移除默认设置 所有的CSS列表属性 CSS 表格 表格边框 折叠边框&#xff08;border-collapse&#xff09; 表格宽度和高度 表格文字对齐 表格填充 表格颜色 CSS 列表 CSS 列表属性作用如下&#xff1a; 设…

C#开发的全屏图片切换效果应用 - 开源研究系列文章 - 个人小作品

这天无聊&#xff0c;想到上次开发的图片显示软件《 PhotoNet看图软件 》&#xff0c;然后想到开发一个全屏图片切换效果的应用&#xff0c;类似于屏幕保护程序&#xff0c;于是就写了此博文。这个应用比较简单&#xff0c;主要是全屏切换换图片效果的问题。 1、 项目目录&…

【Vue3】watch 监视 ref 定义的数据

【Vue3】watch 监视 ref 定义的数据 背景简介开发环境开发步骤及源码参数说明 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努…

【C++进阶学习】第八弹——红黑树的原理与实现——探讨树形结构存储的最优解

二叉搜索树&#xff1a;【C进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫-CSDN博客 AVL树&#xff1a; ​​​​​​【C进阶学习】第七弹——AVL树——树形结构存储数据的经典模块-CSDN博客 前言&#xff1a; 在前面&#xff0c;我们已经学习了二叉搜索树和…

PCIe 6.0为什么需要14-bit tag

1.TLP中的tag是什么 在PCIe TLP&#xff08;Transaction Layer Packet&#xff09;中&#xff0c;tag是分配给特定Non-Posted Request的编号&#xff0c;协议要求CPL/CPLD中的tag 与对应non-post request TLP中的tag保持一致&#xff0c;因此Requester可以使用tag来识别CPL…

免费【2024】springboot 趵突泉景区的智慧导游小程序

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

十、SpringBoot 统⼀功能处理【拦截器、统一数据返回格式、统一异常处理】

十、SpringBoot 统⼀功能处理 1. 拦截器【HandlerInterceptor、WebMvcConfig】1.1 拦截器快速⼊⻔⾃定义拦截器&#xff1a;实现HandlerInterceptor接⼝&#xff0c;并重写其所有⽅法注册配置拦截器&#xff1a;实现WebMvcConfigurer接⼝&#xff0c;并重写addInterceptors⽅法…

堆(c++)

堆是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。 堆总是满足下列性质&#xff1a; 堆中某个节点的值总是不大于或不小于其父节点的值&#xff1b;堆总是一棵完全二叉树。 常见的堆有二叉堆、斐波那契堆等。 堆是非线性数据结构&#…

初识C++ · map和set的使用

目录 前言&#xff1a; 1 set 2 map 前言&#xff1a; 在前面阶段&#xff0c;我们已经学习了stl里面的部分容器&#xff0c;比如vector,list,deque等&#xff0c;这些容器都被称为序列式容器&#xff0c;也就是每个值之间式没有关联的&#xff0c;那么今天介绍的容器&…

IGV.js | 载入自己下载的gtf文件

1.安装 htslib-1.20 https://www.htslib.org/doc/tabix.html J3$ cd ~/Downloads/ $ wget https://github.com/samtools/htslib/releases/download/1.20/htslib-1.20.tar.bz2 $ tar jxvf htslib-1.20.tar.bz2编译安装&#xff1a; $ cd htslib-1.20/ $ ./configure --prefix/…