IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++底层原理 -> 正文阅读

[C++知识库]c++底层原理


前言


进阶学习面向对象C++语言的底层知识;学习以下内容需要有一定的编程基础;后续会不断更新,排版会随着内容的调整而调整;

一、结构体

1. 定义

是C++的一个数据类型,与int、char、float等同一等级;

2. 结构体表现形式

1)结构体中可以存储变量、函数等;如果函数内容比较多,则会使得结构体看起来非常庞大,因此函数一般在结构体里声明,实现是写在结构体外面;
2)结构体可以声明变量、函数以及继承其他结构体;
3)在存储、创建、调用、继承过程中,结构体除了在权限上有区别,其余地方和类都是一样的。

3. 结构体作用

结构体存储的数据类型可以是丰富多样的,这就弥补了存储多个数据但得同类型的数组的不足;

4. 结构体特点

1)结构体创建后是保存在堆中的;
2)结构体作为参数传入函数时,首先会将结构体所有内容复制一遍一个个压入栈中;若作为返回值时,会从栈中复制一遍取出来;结构体定义不要定义为局部变量;
3)由于2的特点传结构体或返回结构体时使用指针的形式,那么函数内部使用的结构体内容还是堆中的,只是通过指针指向的结构体首地址去堆中取。这样传将有利于资源合理利用。

5.结构体字节对齐

聚焦问题:字节对齐是一个空间和时间的问题,一字节对齐不会有空间浪费,但在搜索时间上会比较慢;若选择合适的对齐空间,可以节省搜索时间;
本机宽度:本机有32位、64位等等的差别,不同宽度对齐最优对齐参数不同;
对齐参数:结构体对齐数是选择成员宽度和设置对其参数值小的进行对齐,确定对齐数后结构体内存必须是对齐数的整数倍。对齐参数可以取值为1、2、4、8,默认为8,代码如下:

#pragma  pack(n)
//放结构体
#pragma pack()

对齐原则:
–d

问题:结构体存储同样的数据类型和元素个数;只是写的向后顺序不一样,导致结构体的占用空间不同;

6. 结构体和类的区别:

结构体和类的区别主要是权限。权限体现在继承和声明成员;结构体声明或继承的所有成员默认都是public,类声明或继承的成员默认都是private。

二、指针

  • 内存分布:
    代码区:代码;
    栈:参数、局部变量;
    数据区:主要有全局变量区和常量区,其中全局变量存储全局变量,是可读,可写的;常量区存储的是常量,是可读不可写。
//【“china"是存储在常量区的,这时x存储的是"China"的地址,*(x+1)="s"会报错】
char* x = "china"
//【"china"也是存储在常量区,但是在编译是会将每个字符从常量区拷贝到堆栈里,因此y[0]="s"是正确的】
char[] y = "China" 
  • 代码解释
    所有的代码通过编译器后转成了汇编。每行代码都有对应的硬编码,硬编码是存储在一块地址中的,每个硬编码通过反汇编引擎翻译成汇编语句。

1.指针函数

定义:返回值是指针的函数;

2.函数指针

1)定义
每个函数都会存放在一块地址中,一个指针变量去指向这块地址的首地址,而这个指针变量就称为函数指针;
2)声明

int (*pFun)(int,int);

3)特性
-pFun的宽度:4
-赋值:

pFun = (int (*)(int,int)10;

-运算:
函数指针不能做++,–,相减。因为函数体内部代码不同,其*pFun宽度不同,但可以做比较;
4)作用
----加载动态链接库(DLL);首先通过逆向分析出来了DLL库中的函数地址、返回值和参数,然后就可以通过指针函数去调用该函数;
----将硬编码复制到数据区,然后定义已给函数指针,给指针赋值硬编码的首地址,再通过函数指针调用即可;
----在对函数代码进行加密,自己保存一串密钥,使其对硬编码进行数据运算,解密时使用密钥进行反运算即可;

3.数组指针

例子1:char* arr[5];
解释:这里arr是一个数组,数组中的每一个数据都是char*类型,一个char*类型占用4个字节的空间用于存储地址,那么这个数组总共占用20个字节;

例子2:定义是:char (*px)[5];赋值时:px = (int (*)[5])10;
解释:变量为px,px存储的是一个5个char类型的数组首地址,sizeof(px)则为4;px+=3后px的十进制为1x5x3=15;
这里的
px和px区别:*px是一个int数组类型,px是一个int (*)[5]类型;*px和px的地址是一样的,他们都是指针,但是他们的宽度不同,px的宽度是5,*px的宽度是1;
作用:这种写法就可以将一维数组用二维数组的方式操作;

例子3:定义是:char (*px)[2][4];求*(*(*(p+2)+3)+4)
解释:变量为px,宽度为2x4x1 = 8,*p的宽度是4x1 = 4,**p的宽度则为1,因此*(*(*(p+2)+3)+4)访问的值是从首地址往后偏移8x2+4x3+1x4=32个字节;

总结:例子1表示是数组中存储的指针,例子2和例子3是指针数组(用指针操作多维数组)。

4.结构体指针

1)例子:假设student是一个3个int类的结构体;student* A=(student*) 100,进行A++运算后,十进制打印A是112,若student** A=(student**) 100,进行A++运算后,十进制打印A是104;
解释:student结构体存储的宽度为12,在A进行运算时去掉一个星则类型变为student,此时100+12=112;同样student**变为student*,此时student*宽度是4,所以A++后,A的十进制为104;

2)结构体指针存储的不一定只能存结构体,例如:student是一个包含int、char、short类型的结构体;此时
int data = 100;
student* A = (student*)data;
print(“%d,%d,%d”%(A->one,A->two,A->three))
输出第一个值是100,但第二第三个值是一个乱码;
解释:A存储的是将data作为首地址,取student宽度的地址范围作为*A的可访问的地址;此时只有第一个4字节地址是正确的后面的所有地址存储的都是乱序的。

3)底层调用约定主要有cdCall,stdCall,msCall三种:
cdCall:该调用约定主要有外平栈,参数从右往左传这两个特点;

5.多重指针

1)问题:char* A、char** B、char*** C他们之间有什么区别呢?该怎么使用?
特点一:在指针运算时,类型需要去星,A++后时在A的初始地址上加上char类型的宽度(1字节);同理B++是在B的首地址上加char*类型的宽度(4字节),char**类型宽度也是4字节;
特点二:“*”取值时,类型需去星,如 *A的类型为char,*B的类型为“char*”类型;

三、类

(一).this指针

1. 定义:

this是一个指针,该指针只做一件事就是指向对象首地址。

2. 表现形式:

1)创建类对象是编译器会默认生成一个this指针,在类内部可以使用this指针去获成员变量和函数;
2)若类中有成员函数,编译器会将this指针作为成员函数的第一个参数传入;

3. 作用:

1)区分那些是参数,哪些是成员;
2)可以返回当前对象的首地址;
3)编译器不允许使用者对this指针赋值;

(二)类的控制权限

1.定义:

类等控制权限有三个关键字,分别是private(本类成员可以使用)、protect(子类成员可使用)、public(其他任何地方都可以使用);注意在类外面声明并定义的一个函数,该函数里面创建了类对象,此时不能通过对象去访问private或protect中的内容。

2.作用:

1)方便编写者管理。一般public中的内容不能随意改动,若有不确定的内容可以写到private中,后续更改时也不用改其他类使用该成员的代码。

四、继承

1.定义:

2.作用:

3.机制:

1)继承本质就是复制。子类继承父类,实际上将父类所有的成员复制到子类;
2)继承时会有一个控制权限,若未选择继承权限,那么继承的所有成员都是private成员,尽管父类中有声明是protect或public;

五、多态

(一)虚函数

1. 定义:

虚函数是出现在类中的,是一个用于面向对象的多态的,有虚函数和纯虚函数两种;

2. 虚函数的作用:

3. 虚函数底层:

  • 虚函数的位置:
    首先一个类创建时,都会有已给this指针,这个指针指向地址所存的东西即是该类下所有内容,若类中有虚函数,那么该内容的首地址所存储的内容将是一个虚函数表,虚函数表中存的东西就是虚函数的地址。
  • 虚函数的特点:
    1)不管一个类中有多少个虚函数,this指针下存的都只有一个虚函数表的地址;
    2)使用对象"."这种形式调用的话,虚函数和普通函数在底层的表示都一样(都是直接调用形式);若使用对象指针的形式去调用,则虚函数是间接调用,普通函数是直接调用;
    3)调用时可以通过指针去取出要调用的虚函数地址(该地址是一个4字节,32位系统一般用int类型表达),然后创建一个函数指针,将虚函数地址的int类型强转为函数指针类型,再实行调用;
  • 虚函数表存储的虚函数内容:
    1)多继承无函数覆盖:若继承俩个父类,那就有两个虚函数表,第一个虚函数表只有第一个继承的虚函数地址和子类虚函数地址(每个虚函数表地址都是4字节);
    2)多继承有函数覆盖:覆盖的哪个虚函数,虚函数就在被覆盖的那个虚函数表里;
    3)多重继承无函数覆盖:只有一个虚函数表,子类虚函数表根据继承的顺序依次从基类到子类的顺序存储
    4)多重继承有函数覆盖:和多继承有函数覆盖同理;
    5)

(二)多态

1.定义

绑定:调用的代码与函数真正的地址关联的这个过程;
编译期绑定:在编译时就以及确定调用内容的真正地址。普通成员属性和函数都是编译时绑定;
动态绑定(运行期绑定):在运行时才能确定调用的内容的真正地址,虚函数会在运行时绑定(因为编译时绑定的是虚函数表地址,虚函数真正的地址在虚函数表中);
多态:动态绑定的一种形式多态,指的是类中成员函数可以有多种形态、行为(通过虚函数进行变换);

2.作用

1)若子类继承父类时,由于继承的特性,父类的属性会在子类地址的首位,指针指向的首地址就是父类的地址;
2)问题:若无虚函数就算子类重写父类的函数,通过子类去调用重写的函数,此时调用的只是父类被重写的函数;
3)解决方案:将重写的函数声明为虚函数;此时用子类指针调用则指向子类重写的函数,用父类指针调用指向的就是父类被重写的函数。
4)应用:多态用于析构函数,当用父类指针访问子类对象时,调用完子类对象后此时使用的析构函数将是子类的析构函数;

总结

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 18:17:37  更:2022-04-22 18:19:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:41:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码