标签:
C++的动态对象创建
当创建一个C++对象时,会发生两件事:
(1)为对象分配内存
(2)调用构造函数来初始化那个内存
然而,为对象分配内存可以用以下几种方式或在可选择的时间发生:
(1)在静态存储区域,存储空间在程序开始之前就可以分配。这个存储空间在整个运行期间都存在。
(2)无论何时到达一个特殊的执行点(左大括号)时,存储单元都可以在栈上被创建。出了执行点(右大括号),这个存储单元自动被释放。这些栈分配运算内置于处理器的指令集中,非常有效。但是,在写程序的时候,必须知道需要多少个存储单元,以便编译器知道生成正确的指令。
(3)存储单元也可以从一块称为堆的地方分配。这被称为动态内存分配。在运行时调用程序分配这些内存。这就意味着在任何时候可以分配内存以及分配多少内存,当然也需要负责决定何时释放内存。
为了在运行时动态分配内存,C在它的标准库函数中提供了一些函数:从堆中申请内存的函数malloc()以及它的变种calloc()和realloc()、释放内存返回给堆的函数free()。下面我们看一个例子:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #define inf 0x7fffffff 8 using namespace std; 9 10 class Obj 11 { 12 public: 13 void initialize() { 14 cout<< "initializing Obj" <<endl; 15 i = j = k = 0 ; 16 memset(buf, 0, sz); 17 } 18 void destroy() const { 19 cout<< "destroying Obj" <<endl; 20 } 21 private: 22 int i, j, k; 23 enum {sz=100}; 24 char buf[sz]; 25 }; 26 27 int main() 28 { 29 Obj* obj = (Obj*)malloc(sizeof(Obj)); 30 obj->initialize(); 31 obj->destroy(); 32 free(obj); 33 return 0; 34 }
程序中有这样一行代码,使用了malloc()为对象分配内存:
1 Obj* obj = (Obj*)malloc(sizeof(Obj));
这里用户必须决定对象的长度,由于malloc()只是分配了一块内存而不是生成一个对象,所以它返回了一个void*类型指针。而C++里面不允许将一个void*指针赋予任何其他指针,所以必须做类型转换。
用户在使用对象之前必须记得对它初始化。注意构造函数没有被使用,这是因为构造函数不能被显示地调用---它是在对象创建时由编译器调用。
许多程序设计者发现C的动态内存分配函数太复杂,容易令人混淆。所以,C程序设计者常常在静态内存区域使用虚拟内存机制分配很大的变量数组以避免使用动态内存分配。为了在C++中使得一般的程序员可以安全使用库函数而不费力,所以没有接受C的动态内存分配方法。
C++中的解决方法是把创建一个对象所需的所有动作都结合在一个称为new的运算符里。我们当用new(new 的表达式)创建一个对象时,它就在堆里为对象分配内存并为这块内存调用构造函数。我们可以为类使用任何可用的构造函数而写一个new表达式,如果构造函数没有参数,可以写没有构造函数参数表的new表达式。
new表达式的反面是delete表达式。delete表达式首先调用析构函数,然后释放内存。如果正在删除的对象的指针是0,将不发生任何事情。为此,我们经常建议在删除指针后立即把指针赋值为0以免对它删除两次,从而产生某些问题。
1 #ifndef TREE_H 2 #define TREE_H 3 #include<iostream>//Tree.h 4 using namespace std; 5 6 class Tree 7 { 8 public: 9 Tree(int treeHeight) :height(treeHeight) {} 10 ~Tree() {cout<< "*" <<endl; } 11 friend ostream& operator << (ostream& os, const Tree* t) { 12 return os<< "Tree height is: " << t->height <<endl; 13 } 14 private: 15 int height; 16 }; 17 18 #endif // TREE_H
1 #include "Tree.h" 2 using namespace std; 3 4 int main() 5 { 6 Tree* t = new Tree(40); 7 cout<< t; 8 delete t; 9 return 0; 10 }
当使用new在堆上创建对象数组时,如下面这一行代码:
MyType* fp = new MyType[100];
这样在堆上为100个MyType对象分配了足够的内存并为每一个对象调用了构造函数。销毁这个数组时我们应该这样写:
delete []fp;
空的方括号告诉编译器产生代码,该代码的任务是将从数组创建时存放在某处的对象数量取回,并为数组的所有对象调用析构函数。
当operator new()找不到足够大的连续内存块来安排对象时,将会发生什么事情呢?一个称为new-handler的特殊函数将会被调用。首先,检查指向函数的指针,如果指针非0,那么它指向的函数将被调用。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #include<new> 8 #define inf 0x7fffffff 9 using namespace std; 10 11 int cnt = 0; 12 13 void out_of_memory() 14 { 15 cerr<< "memory exhausted after " << cout << " allocations!" <<endl; 16 exit(1); 17 } 18 19 int main() 20 { 21 set_new_handler(out_of_memory); 22 while (1) { 23 cnt ++ ; 24 new int[1000]; 25 } 26 return 0; 27 }
说明:new-handler函数必须不带参数且其返回值为void 。While循环将持续分配int对象直到空的内存被耗尽。在紧接下去的下一次对new的调用时,将没有内存可被调用,所有调用了new-handler函数。
new-handler函数试着调用operator new(),如果已经重载了operator new(),则new-handler将不会按默认调用。所以如果我们仍想调用new-handler,那么我们不得不在重载的operator new()的代码里加上做这些工作的代码。
下一篇的动态对象创建将会给大家讲解重载new和delete。
标签:
原文地址:http://www.cnblogs.com/BaiYiShaoNian/p/4678269.html