malloc分配内存的问题
- 程序员必须确定对象的长度。
- malloc返回一个void*指针,C++不允许将void*赋值给其他任何指针,必须强转。
- malloc可能申请内存失败,所以必须判断返回值来确保内存分配成功。
- 用户在使用对象之前必须记住对他的初始化,构造函数不能显示调用初始化(构造函数由编译器调用),用户可能忘记调用初始化函数。
malloc实例
class Person{
public:
Person(){
mAge = 20;
pName = (char*)malloc(strlen("john")+1);
strcpy(pName, "john");
}
void Init(){
mAge = 20;
pName = (char*)malloc(strlen("john")+1);
strcpy(pName, "john");
}
void Clean(){
if (pName != NULL){
free(pName);
}
}
public:
int mAge;
char* pName;
};
int main(){
//分配内存
Person* person = (Person*)malloc(sizeof(Person));
if(person == NULL){
return 0;
}
//调用初始化函数
person->Init();
//清理对象
person->Clean();
//释放person对象
free(person);
return EXIT_SUCCESS;
}
new分配内存
new operator
C++解决动态内存分配的方案是将创建一个对象所需要的操作均结合在一个称为new的运算符里。当用new创建一个对象时,它就在堆里为对象分配内存并调用构造函数完成初始化。
Person* person = new Person;
相当于:
Person* person = (Person*)malloc(sizeof(Person));
if(person == NULL){
return 0;
}
person->Init();
delete operator
new表达式的反面是delete表达式。delete表达式先调用析构函数,然后释放内存。delete只适用于new创建的对象(malloc与free配套使用)
#define _CRI_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Person
{
public:
Person() {
cout << "默认构造调用" << endl;
}
Person(int a) {
cout << "有参构造" << endl;
}
~Person()
{
cout << "析构函数调用" << endl;
}
};
void test01()
{
//Person p1; 栈区开辟
Person* p2 = new Person; //堆区开辟
//所有new出来的对象 都会返回该类型的指针
//malloc返回void* 还要强转
//malloc 会调用构造吗?不会 new会调用构造
//new 运算符, malloc为函数
//释放 堆区空间 delete
delete p2; //delete也是运算符 配合new用,malloc配合free用
}
int main()
{
test01();
return 0;
}
delete void*可能会出错
如果对一个void*执行delete操作,可能会是一个程序错误,除非指针指向的内容是非常简单的,因为其不执行析构函数,以下代码未调用析构函数,导致可用内存减少。因此也void *接受new出来的指针,会出现释放问题。
#define _CRI_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Person
{
public:
Person() {
cout << "默认构造调用" << endl;
}
Person(int a) {
cout << "有参构造" << endl;
}
~Person()
{
cout << "析构函数调用" << endl;
}
};
void test02()
{
Person* p = new Person;
//当用void *接受new出来的指针,会出现释放的问题 delete p;无法释放
delete p;
}
int main()
{
test02();
return 0;
}
用于数组的new和delete
通过new开辟数组 一定会调用默认构造函数,所以一定要提供默认构造函数
Person* pArray = new Person[10]; //在堆区必须使用默认构造,不可以指定有参构造
Person pArray2[2] = { Person(1), Person(2) };//在栈上开辟数组,可以指定有参构造
//释放数组
delete [] pArray; //数组释放 一定 要加 [],否则会出错
#define _CRI_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Person
{
public:
Person() {
cout << "默认构造调用" << endl;
}
Person(int a) {
cout << "有参构造" << endl;
}
~Person()
{
cout << "析构函数调用" << endl;
}
};
void test03()
{
//通过new开辟数组 一定会调用默认构造函数,所以一定要提供默认构造函数
Person* pArray = new Person[10]; //在堆区必须使用默认构造,不可以指定有参构造
Person pArray2[2] = { Person(1), Person(2) };//在栈上开辟数组,可以指定有参构造
//释放数组
delete [] pArray; //数组释放 一定 要加 [],否则会出错
}
int main()
{
test03();
return 0;
}
delete数组为加[]的问题
Person* person = new Person[10];
delete person;
以上代码是用了new,也搭配了delete。但问题在于Person有是个对象,那么此时其余9个可能没有调用析构函数,也就是其余9个对象可能删除不完全,因此其析构函数没有被调用。
我们知道调用new和delete发生了如下事件:
- new: 一. 分配内存;二. 调用构造函数
- delete: 一. 析构函数;二. 释放内存
而上段代码最大的问题在于:person指针指向的内存中到底有多少个对象,因为这决定多少个析构函数应该被调用。换句话说,person指针指向的是一个单一的对象还是一个数组对象,由于单一对象和数组对象的内存布局不一样。明确确的说,数组所用的内存通常还包括“数组大小记录”,使得delete的时候知道应该调用几次析构函数。单一对象无此记录。可以如下图理解:
因此,当我们使用delete时,我们必须要让delete知道指针指向的内存空间中是否存在一个“数组大小记录”的方法就是我们告诉delete。而当我们使用delete [] 时,delete就知道是一个数组对象,从而清楚应该调用几次析构函数。