动态分配内存,C++动态内存分配

今天翻书看到动态内存分配,所以就来写个博文来杂谈一下动态内存分配的实现吧。
动态内存分配技术可以保证程序在运行的过程中按照需要申请适量的内存,使用结束后还可以释放,这种在程序运行过程中申请和释放的存储单元的也称为堆对象,申请和释放过程一般称为建立和删除。
new和delete的使用
在C++中建立和删除堆对象使用两个关键字符:new和delete。
运算符的new的功能是动态的分配内存,或者称为动态的创建堆对象语法形式为:
new 数据类型 (初始化参数表)
该语句在运行的过程中申请分配用于存放指定类型数据的内存空间,并根据初始化参数表中给出的值进行初始化。如果内存申请成功,new运算便返回一个指向新分配内存的首地址的类型指针,可以通过这个指针对堆对象进行访问;如果申请失败,会抛出异常。
int *p; p=new int (2);
上面的语句是创建的是数组,在创建一个类的对象的时候,如果该类存在用户定义的默认构造函数则new 类名 和new 类名()这两种写法是一样的,都会调用默认构造函数。如果用户未定义默认构造函数,用new 类名 创建对象时,会调用系统生成的隐含的默认构造函数;使用new 类名 创建对象时,系统除了执行默认构造函数外,还会为基本数据类型和指针类型的成员用0赋初值,如果该对象的某个成员对象也没有用户定义的默认构造函数,那么对该成员对象的基本数据类型和指针类型的成员,同样以0赋初值。
申请了空间,使用过后要释放(删除)delete用来删除new创建的对象。释放指针指向的空间,形式为:
删除new申请用于新对象的格式:delete 指针名;
删除new申请的用于储存int数组或者char字符的格式: delete [] 指针名;
注意:必须对申请的空间进行回收,不然会造成内存泄漏,new创建的对象只能使用delete进行一次删除操作,多次删除会导致运行错误。
下面来看个对类的对象的创建:
#include using namespace std; class Point { private: int x , y ; public: Point():x(0),y(0){} Point(int x , int y ):x(x),y(y){} ~Point(){} }; int main() { Point * P=new Point;//动态创建对象,没有给出参数表,因此调用默认构造函数(无参)
delete P; Point * q=new Point(1,2);//动态创建对象,有参数表示,因此调用有参数的构造函数 delete q;//删除对象,自动调用析构函数
return 0; } 在创建动态数组的时候:
new 类型名 [数组长度]
数组长度=任何能够得到整数的表达式
在对其进行删除时和对对象的删除不同,为:delete [] 指针名
在每次都有创建和删除,这样操作似乎有点繁琐,下面讲一下将new和delete封装在一起来用的情况;我们将new和delete封装在一起构成了动态数组类。在封装的时候我们加入一个
函数来检查数组越界的问题。可以在每次访问的时候检查一下是否越界,函数名:assert(断言)在标准C++cassert头文件中。assert在debug模式下起作用,在release模式下不
起作用。下面是一个例子。为了对创建的数组类的对象进行初始化或者赋值,读取的操作加入了相应的成员函数,动态数组类还可以根据自己的需要对功能函数进行定制。
#include #include using namespace std; class Arryofint { private: int *ints; int size; public: Arryofint(int size):size(size) { ints=new int[size];//申请空间 } int &element(int index)//返回指定的元素 { assert(index>=0&&index=0 && i=0 && i>n,n) { Arryofint ints(n); for (int i=0; i>theelement; ints.setelement(i,theelement); } ints.show(); } return 0; } 在上面的例子中看到了动态数组的封装使用,每次要使用其中的元素的时候必通过接口来访问,这样也很麻烦。下面同对[]重载来方便对数据成员的访问。
#include #include using namespace std; class Arryofint { private: int *ints; int size; public: Arryofint(int size):size(size) { ints=new int[size]; } int &element(int index) { assert(index>=0&&index=0 && i=0 && i=0 && i< size); return ints[i]; } ~Arryofint(){delete [] ints;} }; int main() { int n; int theelement; while (cin>>n,n) { Arryofint ints(n); for (int i=0; i>theelement; ints.setelement(i,theelement); } ints.show(); cout<
上面讲的都是对单维度的数组的创建,下面来讲下多维度的数组的创建,形式如下:
new 类型名 t[数组第一维长度][数组第二维长度][..]..
这里说明下第一维的长度是可以是任何结果为整数的表达式。而其他各维的长度必须是结果为正的常量表达式如:1+2+3+3…..可以说没多大用。如果内存申请成功了的话,new运算
返回的一个指向新内存的首地址,不是t类型的指针,是指向t类型的指针。比如:float (*p)[12][12]=new float [表达式][12][12],这里可以知道,t类型即是二维的元素个数是12*12
的数组。开辟新的空间来指向的是上面t类型的指针。上面(*p)的括号是一定要的。不然编译器会报错。如果你要开二维空间:float (*p)[12]=new float [表达式][12]这里用12来代替常
量表达式。 这样的方式开新的空间不大灵活,因为只能一维可以变,其它维度是已知。下面说一下动态的开二维指针:
#include using namespace std; int main() { int n; cin>>n; int **p=new int *[n]; for (int i=0; i 上开了个二维的方阵的空间。这样控制开辟空间的多少,但是也繁琐了。自由选择,不同情况,那种好用用哪种方式。
用vector来开辟数组空间
由C++标准库提供的动态数组封装-vector,这种被封装的数组具有各种类型。vector不是类,而是一个类模板,用vector定义的动态数组形式如下:
vector<元素类型>数组对象名(数组长度);数组长度可以是含变量的表达式;例如:int n; vectorarr(n);
需要说明的是:与普通的数组不同的是,用vector定义的数组对象的所有元素都会被初始化,如果数组元素的类型为基本类型(如int)数据,则所有元素都会被以0初始化,如果数组元素类型为类类型的,则会调用类的默认构造函数初始化。因此如果你以vector定义的动态数组要保证作为数组元素的类具有默认构造函数,初始值也可以自己指定但只能为所有元素指定相同的初始值。如: vector<元素类型>数组对象名(数组长度,元素初始值);vector定义的类中的size函数很好用。
#include #include using namespace std; double average(const vector &arr) { double sum=0; for (unsigned i=0; iTags:  内存分配 c动态分配 c内存分配 c语言内存分配 动态分配内存

延伸阅读

最新评论

发表评论