动态对象概述:
当我们创建数组的时候,总是需要提前预定数组的长度,然后编译器分配预定长度 的数组空间,在使用数组的时,会有这样的问题,数组也许空间太大了,浪费空 间,也许空间不足,所以对于数组来讲,如果能根据需要来分配空间大小再好不 过。
所谓动态的意思意味着不确定性。 而在程序运行中 可以创建和销毁对象是最基本的要求。当然 c 早就提供了动态内存分配(dynamic memory allocation),函数 malloc 和 free 可以在运行时从堆中分配存储单元。 然 而这些函数在 c++中不能很好的运行,因为它不能帮我们完成对象的初始化工作。
也就是说虽然C语言的动态内存分配可以实现动态内存分配但是,不能够调用对象的析构函数和构造函数,也就是不能够初始化对象和清理对象。
创建c++对象的过程:
- 为对象分配内存
- 调用构造函数来初始化那块内存
第一步我们能保证实现,需要我们确保第二 步一定能发生。c++强迫我们这么做是因为使用未初始化的对象是程序出错的 一个重要原因。 4.3.9.2 C 动态分配内存方法 为了在运行时动态分配内存,c 在他的标准库中提供了一些函数,malloc 以及它的变种 calloc 和 realloc,释放内 存的 free,这些函数是有效的、但是原始的,需要程序员理解和小心使用。为 了使用 c 的动态内存分配函数在堆上创建一个类的实例,我们必须这样做
class Person {
public:
Person()
{
this->name = (char*)malloc(sizeof("test"));
strcpy(this->name, "test");
}
void Init_Person(char *pname)
{
name = (char*)malloc(sizeof("test"));
strcpy(name, pname);
}
void clean()
{
if (name != NULL)
{
free(name);
}
}
char *name;
};
void test01()
{
Person* person = (Person*)malloc(sizeof(Person));
if(person == NULL)
{
return 0;
}
person->Init("name");
cout << person->name << endl;
person->clean();
}
每次实现对象的初始化和清理的时候,都需要自己调用函数实现。
实现内存动态分配的时候有以下几个问题:
- 程序员必须确定对象的长度。
- malloc 返回一个 void 指针,c++不允许 将 void 赋值给其他任何指针,必须强转。
- malloc 可能申请内存失败,所以必须 判断返回值来确保内存分配成功。 4) 用户在使用对象之前必须记住对他初始化, 构造函数不能显示调用初始化(构造函数是由编译器调用),用户有可能忘记调用初 始化函数。 c 的动态内存分配函数太复杂,容易令人混淆,是不可接受的
c++中我们推荐使 用运算符
new
和delete
.
new运算符:
C++中解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个称为 new 的运算符里。当用 new 创建一个对象时,它就在堆里为对象分配内存并调用构造函数完成初始化。
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
class Person {
public:
Person(int age)
{
this->age = age;
cout << "Person的构造函数调用" << endl;
}
~Person()
{
cout << "Person的析构函数调用" << endl;
}
int age;
};
void test01()
{
Person* p = new Person(10);
cout << p->age << endl;
delete p;
}
执行结果:
New 操作符能确定在调用构造函数初始化之前内存分配是成功的,所有不用显式 确定调用是否成功。 现在我们发现在堆里创建对象(动态创建对象)的过程变得简单了,只需要一个简单的表达式,它带有内置的长度计算、类型转换和安全检查。
对于delete
运算符。delete 表达式先调用析构函数,然后释放内 存。delete 只适用于由 new 创建的对象。 如果使用一个由 malloc 或者 calloc 或 者 realloc 创建的对象使用 delete,这个行为是未定义的。
对于数组的new和delete
//创建字符数组
char*pStr=new char[100];
//创建整型数组
int*pArr1=new int[100];
//创建整型数组并初始化
int*pArr2=new int[10]{1,2,3,4,5,6,7,8,9,10};
//释放数组内存
delete[]pStr;
delete[]pArr1;
delete[]pArr2;
classPerson{
public:
Person(){
pName=(char*)malloc(strlen("undefined")+1);
strcpy(pName,"undefined");
mAge=0;
}
Person(char*name,int age){
pName=(char*)malloc(sizeof(name));
strcpy(pName,name);
mAge=age;
}
~Person()
{
if(pName != NULL)
{
delete(pName);
}
}
char *pName;
int mAge;
}
对象数组初始化:
对象数组如果在栈上初始化时候可以是栈聚合初始化:
Personperson[]={Person("john",20),Person("Smith",22)};
对象数组如果是要动态分配在堆上的必须要构造函数:
Person*workers=newPerson[20];
使用new和delete对对象数组操作时需要注意的:
对于创建前面已经说了很多了
Person* person = new Person[10];
但是对于释放,以下的执行结果是不同的
delete person;
delete[10] person;
当我 们使用一个 delete 的时候,我们必须让 delete 知道指针指向的内存空间中是否存 在一个“数组大小记录”的办法就是我们告诉它所要释放的数组大小。当我们使用 delete[],那么 delete 就知道是一个对象数组,从而清楚应该调用几次析构函数。 结论: 如果在 new 表 达式中使用[],必须在相应的 delete 表达式中也使用[].如果在 new 表达式中不使 用[], 一定不要在相应的 delete 表达式中使用[]。