C++
摸森堡
这个作者很懒,什么都没留下…
展开
-
memcpy()和 memmove()
memcpy()和memmove()都是从一块内存拷贝内容去另外一块内存。区别在于memcpy()不考虑内存重叠,容易出错,memmove()考虑内存重叠,保证安全。memcpy()void* memcpy(void *dest, void *src, size_t size){ if(dest == nullptr || src == nullptr||size<=0) return nullptr; char *p=(char *) dest; char *q=(char *)原创 2020-09-14 16:39:30 · 119 阅读 · 0 评论 -
C++ STL内存池底层原理
STL内存池allocate申请内存,construct调用构造函数。两级配置器:第一级配置器:以malloc(),free(),realloc()等C函数执行实际的内存配置、释放、重新配置等操作,并且能在内存需求不被满足的时候,调用一个指定的函数。第二级配置器:避免太多小区块造成的内存碎片,小额区块带来的不仅是内存碎片,配置时还有额外的负担。区块越小,额外负担所占比例就越大。128,第一级配置器。<128,第二级配置器,每次配置一大块内存,并维护对应的16个空闲链表(free-list原创 2020-09-08 15:10:11 · 319 阅读 · 0 评论 -
malloc内存分配的原理
Malloc函数用于动态分配内存。为了减少内存碎片和系统调用的开销,malloc其采用内存池的方式,先申请大块内存作为堆区,然后将堆区分为多个内存块,以块作为内存管理的基本单位。当用户申请内存时,直接从堆区分配一块合适的空闲块。Malloc采用隐式链表结构将堆区分成连续的、大小不一的块,包含已分配块和未分配块;同时malloc采用显示链表结构来管理所有的空闲块,即使用一个双向链表将空闲块连接起来,每一个空闲块记录了一个连续的、未分配的地址。当进行内存分配时,Malloc会通过隐式链表遍历所有的空闲块,选择原创 2020-09-08 10:58:05 · 2946 阅读 · 0 评论 -
利用共用体来判断大小端
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。1 在c中,联合体(共用体)的数据成员都是从低地址开始存放。2 若是小端模式,由低地址到高地址c.a存放为0x01 00 00 00,c.原创 2020-09-01 21:23:14 · 465 阅读 · 0 评论 -
C++ 11的新特性
1.auto关键字:编译器可以根据初始值自动推导出类型。但是不能用于函数传参以及数组类型的推导。2.nullptr关键字:nullptr是一种特殊类型的字面值,它可以被转换成任意其它的指针类型;而NULL一般被宏定义为0,在遇到重载时可能会出现问题。3.智能指针:C++11新增了std::shared_ptr、std::weak_ptr等类型的智能指针,用于解决内存管理的问题。4.初始化列表:使用初始化列表来对类进行初始化。5.右值引用:基于右值引用可以实现移动语义和完美转发,消除两个对象交互时不必原创 2020-08-30 15:29:51 · 187 阅读 · 0 评论 -
RTTI
运行时类型识别(Run-Time Type Identification,RTTI)。在 C++ 中,只有类中包含了虚函数时才会启用 RTTI 机制,其他所有情况都可以在编译阶段确定类型信息。编译器会在虚函数表 vftable 的开头插入一个指针,指向当前类对应的 type_info 对象。当程序在运行阶段获取类型信息时,可以通过对象指针 p 找到虚函数表指针 vfptr,再通过 vfptr 找到 type_info 对象的指针,进而取得类型信息。**(p->vfptr - 1)...原创 2020-08-28 10:36:12 · 121 阅读 · 0 评论 -
C++的类型转换
类型转换就是对数据所占用的二进制位做出重新解释,如果有必要,在重新解释的同时还会修改数据,改变它的二进制位。。隐式转换和显式转换对于隐式类型转换,编译器可以根据已知的转换规则来决定是否需要修改数据的二进制位;而对于强制类型转换,由于没有对应的转换规则,所以能做的事情仅仅是重新解释数据的二进制位,但无法对数据的二进制位做出修正。这就是隐式类型转换和强制类型转换最根本的区别。隐式类型转换必须使用已知的转换规则,虽然灵活性受到了限制,但是由于能够对数据进行恰当地调整,所以更加安全(几乎没有风险)。强制类型转原创 2020-08-28 10:10:13 · 112 阅读 · 0 评论 -
智能指针
我们在写程序的时候会遇到许多内存管理问题,比如:1.有些内存资源已经被释放,但指向它的指针并没有改变指向(成为了野指针),并且后续还在使用;2.有些内存资源已经被释放,后期又试图再释放一次(重复释放同一块内存会导致程序运行崩溃);3.没有及时释放不再使用的内存资源,造成内存泄漏,程序占用的内存资源越来越多。智能指针是一种内存管理工具,可以自动删除分配的内存。智能指针和普通指针类似,只是不需要手动释放指针,而是通过智能指针自己管理内存的释放。C++ 智能指针底层是采用引用计数的方式实现的。简单的理解原创 2020-08-26 11:42:27 · 677 阅读 · 0 评论 -
C语言实现多态
相比于C语言,C++多了继承、封装、多态的特性。那么我们如何在C语言里模拟多态呢?通过宏替换来实现静态多态我们知道宏替换的一个很大的缺点就是二义性和数据不需要声明类型,这样就很容易导致逻辑错误,所以用内联函数来替换宏。二义性和数据不需要声明类型的意思就是不同的数据类型能实现相同的运算,相当于C++的重载,也就是静态多态。#define ADD(A, B) (A) + (B);int main(){ int a =1; int b =2; cout<<ADD(a,原创 2020-08-25 16:55:03 · 1134 阅读 · 0 评论 -
STL底层实现
STL主要由:以下几部分组成:容器、迭代器、仿函数、算法、分配器、配接器他们之间的关系:分配器给容器分配存储空间,算法通过迭代器获取容器中的内容,仿函数可以协助算法完成各种操作,配接器用来套接适配仿函数1.vector连续存储的容器,动态数组,在堆上分配空间,支持快速随机访问,访问:O(1)插入:在最后插入(空间够):很快在最后插入(空间不够):需要内存申请和释放,以及对之前数据进行拷贝。在中间插入(空间够):内存拷贝在中间插入(空间不够):需要内存申请和释放,以及对之前数据进行拷贝。删除原创 2020-08-21 19:37:58 · 185 阅读 · 0 评论 -
指针和迭代器的区别
迭代器(1)迭代器不是指针,是类模板,表现的像指针。他只是模拟了指针的一些功能,通过重载了指针的一些操作符,->,*,++ --等封装了指针,是一个“可遍历STL( Standard Template Library)容器内全部或部分元素”的对象, 本质是封装了原生指针,是指针概念的一种提升(lift),提供了比指针更高级的行为,相当于一种智能指针,他可以根据不同类型的数据结构来实现不同的++,–等操作;(2)迭代器返回的是对象引用而不是对象的值,所以cout只能输出迭代器使用*取值后的值而不能直原创 2020-08-21 19:42:48 · 438 阅读 · 0 评论 -
堆和栈的区别
1.申请方式:栈由系统自动分配和管理,堆由程序员手动分配和管理。2.效率:栈由系统分配,速度快,不会有内存碎片。堆由程序员分配,速度较慢,可能由于操作不当产生内存碎片。3.扩展方向栈从高地址向低地址进行扩展,堆由低地址向高地址进行扩展。4.程序局部变量是使用的栈空间,new/malloc动态申请的内存是堆空间,函数调用时会进行形参和返回值的压栈出栈,也是用的栈空间。栈的效率高的原因:栈是操作系统提供的数据结构,计算机底层对栈提供了一系列支持:分配专门的寄存器存储栈的地址,压栈和入栈有专门的指原创 2020-08-18 16:48:25 · 257 阅读 · 0 评论 -
栈溢出
栈溢出概念:栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致栈中与其相邻的变量的值被改变。栈溢出的原因:局部数组过大。当函数内部的数组过大时,有可能导致堆栈溢出。局部变量是存储在栈中的,因此这个很好理解。解决这类问题的办法有两个,一是增大栈空间,二是改用动态分配,使用堆(heap)而不是栈(stack)。递归调用层次太多。递归函数在运行时会执行压栈操作,当压栈次数太多时,也会导致堆栈溢出。指针或数组越界。这种情况最常见,例如进行字符串拷贝,或处理用原创 2020-08-18 16:45:32 · 3194 阅读 · 0 评论 -
STL中vector的扩容
转载自:https://blog.csdn.net/rusbme/article/details/98102016?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159624865719724811820002%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=159624865719724811820002&转载 2020-08-01 10:40:38 · 335 阅读 · 0 评论 -
宏定义和內联函数的区别
带参数的宏和普通函数 带参数的宏可以代替函数。宏的替换发生在预编译阶段,占据编译时间;函数调用发生在运行阶段,占据运行时间。对于一些比较大的函数:1.程序的主要运行时间集中在函数体上,函数调用开销可以忽略不计;2.如果函数体过长,宏替换会增加源码的长度,过长的源程序执行起来系统会频繁换页,导致执行效率变低,而函数则不会增加源程序长度。 基于这两个原因,对于函数体较短的函数,可以用带参数宏来替换,对于函数体较长的函数则不使用宏替换。 1.函数调用时,先求出实参表达式的值,然后带入形参。而使用带原创 2020-07-20 21:09:54 · 87 阅读 · 0 评论 -
从源码到可执行文件的过程
1.预处理预处理过程主要是处理那些源文件和头文件中以#开头的命令,预处理的规则一般如下:1.对所有的宏定义进行替换2.处理所有条件编译命令3.处理#include命令,将被包含文件的内容插入到该命令所在的位置4.删除所有的注释//和/* … */5.添加行号和文件名标识,便于在调试和出错时给出具体的代码位置6.保留所有的#pragma命令,因为编译器需要使用它们2.编译编译就是把预处理完的文件进行一些列的词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件。3.汇编汇编的过程就原创 2020-07-20 17:23:30 · 159 阅读 · 0 评论 -
C/C++防止头文件被重复包含
头文件包含是一个递归(循环)的过程,如果被包含的头文件中还包含了其他的头文件,预处理器会继续将它们也包含进来;这个过程会一直持续下去,直到不再包含任何头文件,这与递归的过程颇为相似。递归包含会导致一个问题,就是重复引入同一个源文件,如果不做任何处理,不仅会出现重复定义错误,而且不符合编程规范。对此,可以通过宏保护来解决这个问题。#ifndef _HMCSynth_h#define _HMCSynth_h/* 头文件内容 */#endif...原创 2020-07-20 15:56:46 · 318 阅读 · 0 评论 -
内存对齐
计算机内存是以字节(Byte)为单位划分的,理论上CPU可以访问任意编号的字节,但实际情况并非如此。CPU 通过地址总线来访问内存,一次能处理几个字节的数据,就命令地址总线读取几个字节的数据。32 位的 CPU 一次可以处理4个字节的数据,那么每次就从内存读取4个字节的数据;少了浪费主频,多了没有用。64位的处理器也是这个道理,每次读取8个字节。以32位的CPU为例,实际寻址的步长为4个字节,也就是只对编号为 4 的倍数的内存寻址,例如 0、4、8、12、1000 等,而不会对编号为 1、3、11、10原创 2020-07-01 10:07:03 · 88 阅读 · 0 评论 -
C++内存泄露的检测方法
在主函数末尾添加_CrtDumpMemoryLeaks(),运行结束后可以看到输出,{435}normal block数据长度,然后在主函数第一行添加_CrtSetBreakAlloc(435)定位到内存位置435的内存块,发生中断以后就能知道问题在哪儿了。...原创 2020-06-30 20:43:38 · 133 阅读 · 0 评论 -
常量的存储
c语言中const全局变量存储在只读数据段,编译期最初将其保存在符号表中,第一次使用时为其分配内存,在程序结束时释放。而const局部变量(局部变量就是在函数中定义的一个const变量,)存储在栈中,代码块结束时释放。在c语言中可以通过指针对const局部变量进行修改,而不可以对const全局变量进行修改。因为const全局变量是存储在只读数据段而c++中,一个const不是必需创建内存空间,而在c中,一个const总是需要一块内存空间。在c++中是否要为const全局变量分配内存空间,取决于这个c转载 2020-06-30 16:03:59 · 731 阅读 · 0 评论 -
友元函数的优缺点
为什么要使用友元函数在实现类之间数据共享时,减少系统开销,提高效率。如果类A中的函数要访问类B中的成员(例如:智能指针类的实现),那么类A中该函数要是类B的友元函数。具体来说:为了使其他类的成员函数直接访问该类的私有变量。即:允许外面的类或函数去访问类的私有变量和保护变量,从而使两个类共享同一函数。实际上具体大概有下面两种情况需要使用友元函数:(1)运算符重载的某些场合需要使用友元。(2)两个类要共享数据的时候。使用友元函数的优缺点优点:能够提高效率,表达简单、清晰。缺点:友元函数破环了封装机制,原创 2020-06-29 17:49:17 · 2834 阅读 · 2 评论 -
C++中main函数执行完毕后还可以执行其他语句么?
atexit函数可以登记一些函数在main函数执行完之后按照栈的顺序进行调用。atexit函数是一个特殊的函数,它是在正常程序退出时调用的函数,我们把他叫为登记函数(函数原型:int atexit (void (*)(void)))⼀一个进程可以登记若32个函数,这些函数由exit自动调用,这些函数被称为终止处理函数,atexit函数可以登记这些函数。exit调用终止处理函数的顺序和atexit登记的顺序相反,如果一个函数被多次登记,也会被多次调用。...转载 2020-06-27 11:13:21 · 229 阅读 · 1 评论 -
strlen和sizeof的区别
strlen是库函数,从内存某个位置开始进行扫描,直到遇到字符串结束符’\0’为止,然后返回计数器值(不包括’\0’)sizeof是关键字,用来计算类型或对象所占内存大小。原创 2020-06-27 10:39:57 · 79 阅读 · 0 评论 -
虚函数和虚析构函数
虚函数是动态多态的实现手段。在子类继承父类,子类重写父类虚函数的前提下,当父类指针或引用指向子类时发生动态多态。虚函数是在函数前加关键字virtual,父类的虚函数必须加关键字,子类可加可不加。在类中,存放虚函数的指针(所以类的内存大小为4),指向虚函数表,虚函数表里存放虚函数的地址,子类继承父类时会重写虚函数表,将父类虚函数地址替换为自己的虚函数地址。如果父类的虚函数没有函数体,替换为=0;即为纯虚函数。功能和虚函数一样,不同的是此时的类被称为抽象类,不能实例化。子类继承父类的虚函数后,如果不进行重写原创 2020-06-26 21:41:33 · 642 阅读 · 0 评论 -
友元
全局函数做友元;类做友元;成员函数做友元;C++的封装的特性使得类中的成员函数和成员变量有着不同的访问权限。外部的类或者函数只能访问公有权限的成员,如果想访问私有成员,就需要友元。全局函数想访问类中额私有成员,可以在类中添加全局函数的友元,即在类中最上方添加全局函数的函数声明,函数声明前加friend关键字。友元类也一样,类中最上方添加类的声明,声明前加关键字friend。成员函数稍微麻烦一点,除了在函数原型声明前加friend之外,还要在函数名前加作用域。...原创 2020-06-23 15:46:05 · 82 阅读 · 0 评论 -
深拷贝和浅拷贝
浅拷贝:编译器提供的拷贝构造函数,能进行简单的赋值操作;深拷贝:需要自己实现的函数,能够在堆区重新申请内存空间进行拷贝操作;在调用拷贝构造函数时,如果原来的类在堆区开辟了空间,浅拷贝只是拷贝了堆里内存的地址,两个类共用同一块堆内存,这样在调用析构函数时就会造成内存重复调用的问题。所以需要程序员自己写一个构造函数,重新在堆里开辟空间,虽然内存中的内容相同,但是是两块独立的内存空间,释放互不干扰。注:1.因为堆是后进先出,所以拷贝得到的类先释放内存,然后是原来的类;2.堆的内存需要程序员手动申请和释放,原创 2020-06-17 20:23:03 · 120 阅读 · 0 评论 -
拷贝构造函数调用规则
在创建一个类的时候,编译器会提供添加三个函数:1.默认构造函数,函数体为空;2.默认析构函数,函数体为空;3.默认拷贝构造函数,值拷贝;如果已经有了有参构造函数,编译器不再提供默认无参构造,但是会提供默认拷贝构造函数;如果已经有了拷贝构造函数,编译器不再提供其他构造函数。如果程序运行需要无参构造则会报错。...原创 2020-06-17 20:00:13 · 243 阅读 · 0 评论 -
拷贝构造函数调用的时机
在出现以下三种情况时会调用拷贝构造函数1.通过一个已经初始化的对象来创建一个新的对象。Person(const Person &p)2…函数按值传参对函数进行传参一般有按值传递和地址传递,将一个对象按值传递时就会调用拷贝构造函数3.值返回函数局部对象如果函数返回了一个局部对象,局部对象在函数结束就会被销毁,所以函数会构造一个新的对象来把对象返回。...原创 2020-06-16 20:54:15 · 343 阅读 · 0 评论 -
构造函数和析构函数
1.构造函数1.1函数名与类名相同;1.2没有返回值也不写void;1.3可以有参,可以无参,可以重载;1.4.创建类时编译器自动调用构造函数,如果没有调用空构造函数;2.析构函数2.1函数名与类名相同,函数名前加~号;2.2没有返回值也不写void;2.3不能有参,所以不能重载;2.4销毁类时编译器自动调用析构函数,如果没有调用空构造函数;class demo{public: demo() { cout<<"构造函数"<<endl; } ~原创 2020-06-15 16:45:03 · 116 阅读 · 0 评论 -
二维数组的排序
对于一维数组可以方便的使用sort进行排序,那么如何用sort对二维数组进行排序呢?只需要自定义比较方法即可。vector<vector<int> > demo; sort(demo.begin(),demo.end(),[](vector<int> a,vector<int> b){return a[1]<b[1];});这就是以二维数组中每个一维数组的第二个元素的大小作为比较依据的排序结果。通过Lambda 表达式可以方便的编写比较函数。原创 2020-06-15 16:23:36 · 646 阅读 · 0 评论 -
有符号数和无符号数不匹配
程序运行时遇到了一个问题:vector<int> n;for(int i=0;i<n.size()-1;++i) cout<<"hello"<<endl;waring:有符号数和无符号数不匹配;n的长度是0,所以i<n.size()-1是不成立的,for循环是进不去的,但实际上for循环进去了。因为vector数组的size函数返回的是无符号数0,所以-1也是无符号数,无符号数就意味着和i比较时要忽略符号,-1没有符号位即为1,0<1成原创 2020-06-05 11:11:54 · 625 阅读 · 0 评论 -
C++输入多行数据
在进行笔试的时候,很多道题都有如下形式:输入n,表示接下来要输入n行数据接下来输入n数据,例如:31 2 33 4 512 3 65 7这里采用getline来处理多行的输入int _tmain(int argc, _TCHAR* argv[]){ int n; cin>>n; cin.ignore (); string s; vector<string> res(n); for(int i=0;i<n;++i) { getline(cin原创 2020-05-24 17:43:21 · 6498 阅读 · 1 评论 -
C++ 分割字符串
在C++的笔试中,大家经常遇到需要对输入的字符串进行分割。在python中可以方便的使用split进行分割,C语言中可以使用strtok函数,strtok函数原型是char *strtok(char *s, char *delim),在而在C++中要对string字符串进行分割,就需要我们自己写函数实现了。void split(vector<string> &res,string input,char mark){ string temp; for(int i=0;i<in原创 2020-05-24 16:32:44 · 176 阅读 · 0 评论 -
多态和虚函数
多态就是同一名字的事物完成不同的功能。主要有编译时的多态和运行时的多态,编译时的多态就是重载。动态多态是通过虚函数来实现,子类继承父类,在运行的时候重写虚函数。在虚函数的类里,最开始是一个指针,指向了虚函数表,表里放着虚函数的地址,子类继承虚函数的时候,会将虚函数表里的地址替换为重写的函数地址。...原创 2020-04-26 09:49:37 · 125 阅读 · 0 评论 -
C++中class和struct定义类的区别
类原创 2020-04-26 09:37:19 · 205 阅读 · 0 评论 -
static关键字的作用
static原创 2020-04-26 09:33:06 · 128 阅读 · 0 评论 -
引用和指针的区别
引用和指针的区别原创 2020-04-26 09:16:25 · 1029 阅读 · 0 评论 -
new和mallco的区别
new原创 2020-04-26 08:53:46 · 224 阅读 · 0 评论 -
C和C++的区别
设计思想上:C++是以类为基础的面向对象的语言,而C是以函数为基础的面向过程的结构化编程语言语法上:C++具有封装、继承和多态三种特性C++相比C,增加多许多类型安全的功能,比如强制类型转换、智能指针。C++支持范式编程,比如模板类、函数模板等...原创 2020-04-25 10:35:38 · 85 阅读 · 0 评论 -
构建三维空间——二维数组的哈希表
要实现一个二维数组的每一个坐标(i,j),对应一个值,可以通过multimap<pair<int,int>,int> hash这样的哈希表来实现。主键就是坐标,也就是二维数组,通过pair模板类来实现。值就是这个坐标所对应的值#include "stdafx.h"#include <iostream>#include <stdlib.h>#...原创 2020-04-21 15:14:50 · 1680 阅读 · 0 评论