教做缝纫的网站,商丘简淘网络科技有限公司,无锡专业网站制作的公司,石家庄网络建站在C语言中我们通常会使用malloc/realloc/calloc来动态开辟的空间#xff0c;malloc是只会开辟你提供的空间大小#xff0c;并不会初始化内容#xff1b;calloc不但会开辟空间#xff0c;还会初始化#xff1b;realloc是专门来扩容的#xff0c;当你第一次开辟的空间不够用… 在C语言中我们通常会使用malloc/realloc/calloc来动态开辟的空间malloc是只会开辟你提供的空间大小并不会初始化内容calloc不但会开辟空间还会初始化realloc是专门来扩容的当你第一次开辟的空间不够用的时候就要使用realloc如果你第一次使用realloc的时候前面没有开辟过空间那么realloc的行为会跟malloc一样之后再发挥realloc自己的行为。而我们的C是面向对象的编程当开辟空间失败了malloc只会返回一个空指针我们还需要自己来判断。所以在C这里就将malloc升级成为了newnew在开辟空间失败的时候会抛出异常这跟我们面向对象的理念是一致的。 其实我在C语言阶段学习动态开辟空间的时候是有点迷糊的就是不知道如何去开辟给谁开辟现在想想其实不难理解假设我们要开辟10个int类型的空间大小我们有两种办法一种就是直接定义一个int类型的数组int arr[10]但是这样的空间是定长的空间我们无法对定长数组进行扩容。第二种就是用到动态开辟的空间了我们肯定是先要malloc10个int类型的空间但是我们应该如何取到这段空间呢用指针变量接收是吧但是为什么是指针变量呢我们继续讲解因为我们先开辟了一段空间正常情况下是无法取到这段空间的只有拿到这段空间的地址才可以访问这段空间。这也就是为什么我们会用指针变量来接收malloc开辟的空间了。 下面是malloc的函数声明通过malloc的返回值也不难看出要用指针接收。所以我们要开辟空间一种就是直接定义类型比如int a、int arr[10]、char ch等等一种就是用malloc/realloc/calloc开辟空间用指针接收。 一、介绍
1. malloc、realloc、calloc的介绍 1. malloc只是动态开辟空间并没有初始化这段空间的内容 2. realloc是用来扩容的如果没有预先开辟空间直接使用reallocrealloc的作用相当于malloc 3. calloc相当于是malloc的加强版不仅可以开辟空间也顺便初始化我们的内容 4. 面对内置类型可以直接开辟空间但是对于自定义类型的话比如我们的栈则无法一步到位因为栈里面还有数组空间的开辟需要两层开辟。 下面就是栈的举例因为我们仅仅是用的malloc来开辟一个栈的空间所以栈内部的数组空间还需要开辟如果我们要是写一个构造函数去初始化在主函数中是无法调用的也就完成不了对数组空间的开辟不过在了解定位new之后是可以调用的我们这里使用写一个成员函数Init来完成对数组空间的开辟这样我们在主函数中也可以调用。然后其实这样的方法有点麻烦而且在malloc失败的时候只是返回空指针判断条件也需要我们自己去写这很不满足面向对象的要求所以C基于这样的原因创造了new这个操作符。 #include iostream
using namespace std;
class Stack
{
public:void Init(int capacity){_a (int*)malloc(sizeof(int) * capacity);if(_a nullptr){perror(malloc fail);exit(-1);}_size 0;_capacity capacity;}
private:int *_a;int _size;int _capacity;
};
int main()
{Stack *st (Stack*)malloc(sizeof(Stack));st-Init(4);return 0;
}
2. free的介绍 free专门释放动态开辟的空间如果释放的不是动态开辟的空间就会报错所以这里一定要多加注意 一般就是free( )括号里面就是指向那段空间的指针就行。 3. new、operator new的介绍内置类型和自定义类型 new是在C中才有的操作符因为C兼容C语言所以在C中可以写C但是在C中写不了C。new也是用来动态开辟空间的也可以初始化即可以开辟内置类型的空间也可以开辟自定义类型的空间这两种类型都可以初始化 1. new对内置类型开辟空间直接开辟 int *a new int; //单纯开辟空间
int *b new int(3); //开辟空间并初始化int *arr1 new int[5]; //单纯开辟5个整型的空间
int *arr2 new int[5]{1, 2, 3, 4, 5}; //开辟5个整型空间并初始化 2. new对自定义类型开辟空间第一步开辟这个自定义类型需要的空间 第二步调用这个自定义类型的构造函数 所以我们上面对于栈的开辟可以改善为下面这样 #include iostream
using namespace std;
class Stack
{
public:Stack(int capacity 4, int size 0, int num 0): _a(new int[capacity]{0}), _size(size), _capacity(capacity){cout Stack(int capacity 4) endl; //方便观察确实调用了构造函数}private:int *_a;int _size;int _capacity;
};
int main()
{Stack *st new Stack;//Stack *st new Stack(4, 0, 0);这种就是对于显式构造函数的写法或者是你想传入的初始化内容return 0;
} 3. 那operator new 又是什么呢 相信大家对这个并不熟悉所以这部分知识了解即可operator new 是一种全局函数对malloc进行了封装也就是让malloc函数更加面向对象而不是面向过程了。因为我们在C语言的时候malloc开辟空间失败会返回个空指针我们会根据空指针去找过程中的错误然后C是面向对象编程所以就必须知道是哪个对象出错了new出错就报new。所以operator new就是对malloc进行了一个包装底层还是通过malloc实现只不过在出错的时候operator new是抛异常不是返回空指针了。下面就是operator new这个函数的定义 所以operator new 还是通过malloc实现的看不懂下面的也没关系记住operator new 开辟空间失败抛异常malloc失败是返回NULL void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytesvoid *p;while ((p malloc(size)) 0){if (_callnewh(size) 0){// report no memory// 如果申请内存失败了这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}}return (p);
}
4. delete、operator delete的介绍 delete是用来释放动态开辟的空间如果释放的不是动态开辟的空间会报错 他跟free的区别是在自定义类型这里free仅仅释放他括号里指向的空间 delete在释放自定义类型的空间时候会做两件事 1. 先调用自定义类型的析构函数 2. 再去调用operator delete函数 int *a new int;char *ch new char;double *d new double[5];delete a;delete ch;delete []d; 而operator delete 我们会在new、delete针对数组这里拿出来讲解 二、new、delete的原理
1. new对于自定义类型 1. new 会先调用 operator new 开自定义类型的空间 2. 在申请的空间上再去调用自定义类型的构造函数 这里解答一下为什么new步调用malloc而是调用的operator new 因为还是C是面向对象编程所以我们不使用malloc这套判断错误的方式而是选择operator new 这个函数去封装一下new再让new来调用自己一切都是为了面向对象所以new的产生原因也是这样的因为面向对象。 2. delete对于自定义类型 1. 在空间上执行析构函数完成对象中资源的清理工作 2. 调用operator delete函数释放对象的空间 需要注意的就是为什么delete要先调用析构函数 因为我们要知道再对于栈这样的数据结构他里面数组的空间是被栈空间的指针指着的如果先释放栈的空间就相当于把数组的地址释放了然后再去调用析构函数就会找不到要析构的位置了所以就会出错 基于这样的原因一定要先析构再释放自定义类型的空间 3. new、delete对于数组、自定义类型的数组重点难点 这里其实又涉及到一些不一样的知识我们知道在new一个数组的时候我们是告诉他开辟多少个元素的空间的如下图我们是知道开5个空间的大小的那在释放的时候delete又是如何知道需要释放多大的空间的呢我们保持这个问题继续往下看。 #include iostream
using namespace std;
class Stack
{
public:Stack(int capacity 4, int size 0, int num 0): _a(new int[capacity]{0}), _size(size), _capacity(capacity){cout Stack(int capacity 4) endl; //方便观察确实调用了构造函数}private:int *_a;int _size;int _capacity;
};
int main()
{Stack *st new Stack[5];delete []st;return 0;
} 其实是因为在开辟空间之前我们多开辟了一个int的空间这个空间就是用来存放数组的个数的相当于是图中这样开辟的空间 所以delete面对数组这样的空间他先通过st指针指向的位置先调用多次析构函数连续释放完5个_a指针指向的空间之后返回到a位置处再用通过调用operator delete释放自定义类型数组的这段空间。 换句话说delete 的[ ] 是识别这个类型是数组然后知道数组会多开辟4个字节的空间进而从多开辟的空间的地址处释放。而单独的delete就不会认为这个类型是数组类型也就不会寻找这段空间直接在数组首元素这里释放空间当然一定要注意自定义类型还需要调用析构函数。 new -- 内置类型数组 new -- operator new[ ] 函数 -- operator new 函数 -- malloc 这里要知道的是上面都是调用了一次因为开辟的空间是提前算好的开辟一次就行 delete -- 内置类型数组 delete -- operator delete[ ] 函数 -- operator delete 函数 -- free new -- 自定义类型数组 1. new -- operator new[ ] 函数 -- operator new 函数 -- malloc 2. 每一个自定义类型再调用构造函数 开辟的空间都是一次性开辟好的所以只需要new一次而每个自定义类型都需要调用构造函数所以是多个。 delete -- 自定义类型数组 1. 每一个自定义类型先调用自己的析构函数 2. delete -- operator delete[ ] 函数 -- operator delete 函数 -- free 三、面试题new/delete 和 malloc/free 的 异同 共同点 都是从堆上申请空间并且需要用户手动释放。 不同点 1. malloc和free是函数new和delete是操作符 2. malloc申请的空间不会初始化new可以初始化 3. malloc申请空间时需要手动计算空间大小并传递new只需在其后跟上空间的类型即可 如果是多个对象[ ]中指定对象个数即可 4. malloc的返回值为void*, 在使用时必须强转new不需要因为new后跟的是空间的类型 5. malloc申请空间失败时返回的是NULL因此使用时必须判空new不需要但是new需 要捕获异常 6. 申请自定义类型对象时malloc/free只会开辟空间不会调用构造函数与析构函数而new在申请空间后会调用构造函数完成对象的初始化delete在释放空间前会调用析构函数完成空间中资源的清理 四、练习题 1. C中类ClassA的构造函数和析构函数的执行次数分别为( ) ClassA *p new ClassA[5];delete p; A.5,1 B.1,1 C.5,5 D.程序可能崩溃 答案及解析D 大家可以参考下面这个图 delete p这就相当于先调用b位置的析构函数然后直接在b位置释放了。因为是delete p;并不是delete [ ]p所以delete并不知道说这是一个数组并不会跳到最前面来释放释放的位置不对导致了程序的崩溃 new和delete一定要匹配避免未定义行为
new —— delete
new [ ] —— delete[ ]
malloc —— free