学习笔记:C++动态对象的创建
当我们创建数组时,总是需要提前预定数组长度,然后编译器分配预定长度的数组空间,在使用数组时,会有这样的问题,数组也许空间太大了,浪费空间,也许空间不足,所以对于数组来讲,如果可以根据需要分配空间大小再好不过。
所以动态的意思意味着不确定性。
为了解决这个普遍的编程问题,在运行中可以创建和销毁对象是最基本的要求。当然C早就提供了动态内存分配,函数 malloc 和 free 可以在运行时从堆中分配储存单元。
然而这些函数在C++中不能很好的运行,因为它不能帮助我们完成对象的初始化工作。
1. 对象创建
当创建一个C++对象时会发生两件事:
1.为对象分配内存
2.调用构造函数来初始化那块内存
第一步我们能保证实现,需要我们能确保第二步一定能发生。C++强迫我们这么做的原因是因为使用未初始化的对象是程序出错的一个重要原因。
2. C 动态分配内存的方法
为了在运行时分配内存,C在它的标准库中提供了一些函数,malloc 以及它的变种 calloc 和 realloc,释放内存的 free ,这些函数是有效的, 但是原始的, 需要程序员理解和小心使用。为了使用C的动态分配内存函数在堆上创建一个类的实例,我们必须这样做:
class person{
public:
person( ){
mAge = 20;
pName = (char*)malloc(strlen("john")+1);
strcpy(pName,"john")
}
//Init法初始化
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;
}
问题:
(1) 程序员必须确定对象的长度
(2) malloc返回一个void指针,C++不允许将void赋值给其他任何指针,必须强转。
(3) malloc 可能申请内存失败,所以必须判断返回值来确保内存分配成功。
(4) 用户在使用对象之前必须记住对他初始化,构造函数不能显示调用初始化 (构造函数是由编译器调用)。
C的动态分配函数太复杂,容易令人混淆,是不可接受的,C++中我们推荐使用运算符 new 和 delet。
3. new operator
C++中解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个称为 new 的运算符里。当使用 new 创建一个对象时,它就在堆里为对象分配内存并调用构造函数并完成初始化。
preson* person = new person;
相当于
person* person = (person*)malloc(sizeof(person));
if(person == NULL){
return 0;
}
person->Init( );
new操作符能确定在调用函数初始化之前内存分配是成功的,所以不用显示确定调用是否成功。
现在我们发现在堆里创建对象的过程变得简单了。只需要一个简单的表达式,它带有内置的长度计算,类型转化和安全检查。这样在堆创建一个对象和在栈里创建一个对象一样简单。
验证:
#include<iostream>
using namespace std;
class person
{
public:
person( )
{
cout << "默认构造调用" << endl;
}
~person( )
{
cout << "析构函数调用" << endl;
}
};
void test01( )
{
person p1; //栈区开辟
}
int main( )
{
test01( );
system("pause");
return EXIT_SUCCESS;
}
void test02( )
{
person* p2 = new person; //堆区开辟(不会自动释放) 相对应的delete p2
//所有new出来的对象,都会返回该类型的指针。
}
int main( )
{
test02( );
system("pause");
return EXIT_SUCCESS;
}
注意事项:
1.
void *p = new person;
//当用void* 接受new出来的指针会出现释放的问题。
delete p;
//无法释放p,所以要避免这种情况。
2.
person* pArray = new person[10];
//堆区:通过new开辟数组 一定会调用默认构造函数,所以一定要提供默认构造。
person pArray2[2] = {person(1),person(2) };
//在栈上开辟数组,可以指定有参构造。
释放堆区数组时:delete [] 数组名
delete [] pArray
//如果不加[],则只释放第一个。