动态对象创建

C++的动态对象创建

对象创建

当创建一个C++对象时,会发生两件事:

(1)为对象分配内存

(2)调用构造函数来初始化那个内存

然而,为对象分配内存可以用以下几种方式或在可选择的时间发生:

(1)在静态存储区域,存储空间在程序开始之前就可以分配。这个存储空间在整个运行期间都存在。

(2)无论何时到达一个特殊的执行点(左大括号)时,存储单元都可以在栈上被创建。出了执行点(右大括号),这个存储单元自动被释放。这些栈分配运算内置于处理器的指令集中,非常有效。但是,在写程序的时候,必须知道需要多少个存储单元,以便编译器知道生成正确的指令。

(3)存储单元也可以从一块称为的地方分配。这被称为动态内存分配。在运行时调用程序分配这些内存。这就意味着在任何时候可以分配内存以及分配多少内存,当然也需要负责决定何时释放内存。

C从堆中获取存储单元的方法

为了在运行时动态分配内存,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 和 delete

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和delete

当使用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。

时间: 2024-12-12 11:25:11

动态对象创建的相关文章

动态对象创建(二)重载new和delete

前言 上文我简单介绍了一下动态对象创建的方法,这一篇文章的内容主要是对重载new和delete做一些讲解,也希望能够得到博友们的指点,在这里谢过大家. 通常我们为了一些目的而使用new和delete的内存分配系统,但是在特殊情况下,它并不能够满足需要.最常见的改变分配系统的原因是出于效率考虑:也许要创建和销毁一个特定的类的非常多的对象以至于这个运算变成了速度的瓶颈.C++允许重载new和delete来实现我们自己的存储分配方案,所以可以用它来处理问题. 另一个问题就是堆碎片:分配不同大小的内存可

《C++编程思想》 第十二章 动态对象创建 (习题+解答)

一.相关知识点 重载new和delete 当创建一个new表达式时有两件事发生.首先,使用运算符new分配内存,然后调用构造函数.在delete表达式里,调用析构函数,然后使用运算符delete释放内存.我们永远无法控制构造函数和析构函数的调用(否则我们可能意外地搅乱它们),但可以改变内存分配函数运算符new和delete. 被new和delete使用的内存分配系统是为通用目的而设计的.但在特殊的情形下,它不能满足我们的需要.改变分配系统的原因是考虑效率:我们也许要创建和销毁一个特定的类的非常多

C++ &nbsp; 类工厂实现动态对象创建

看了MFC中的动态创建对象后,感觉动态创建对象算是一种技术吧,理论上说算是一种设计模式.其实创建的原理很明了,把对象的类别名称和其构造函数用容器记录下来,在需要的时候给出创建类别查找容器即可创建对象.当然这里我们需要自定义一个全局的函数指针用来指向类别的构造函数.在这里我们使用类工厂模式,在泪工厂中我们定义一个通用构造函数,将函数指针指向它.下面是参考一位网友思路来整理和摸索的代码: //通用的函数指针 typedef  void *(*CREATE_FUNC)(); //创建类的工厂类 cla

应用程序各对象创建的顺序

应用程序对象时全局对象,它在启动之前由系统创建.应用程序启动之后,程序的主函数首先调用应用程序对象的初始化函数InitInstace(),并在该函数中创建文档模板对象 CSingleDocTemplate *pDocTemplate;//声明文档模板指针(单文档)     pDocTemplate = new CSingleDocTemplate(//创建文档模板对象         IDR_MAINFRAME,//文档模板使用的资源ID         RUNTIME_CLASS(CNOTED

JS学习笔记-OO疑问之对象创建

问一.引入工厂,解决重复代码 前面已经提到,JS中创建对象的方法,不难发现,基本的创建方法中,创建一个对象还算简单,如果创建多个类似的对象的话就会产生大量重复的代码. 解决:工厂模式方法(添加一个专门创建对象的方法,传入参数避免重复) function createObject(name,age){ var obj =new Object(); //创建对象 obj.name = name; obj.age = age; obj.run = function(){ return this.nam

Javascript理解面向对象(一)--对象创建模式

1)工厂模式  通俗来讲就是把原料进厂加工后出厂的一系列流程.在这里只是把原料换成了数据. 以下代码创建了一个createPerson()函数,函数中存在两个属性一个方法,可用于添加并打印person的姓名和年龄信息. <script> function createPerson(name, age) { //1:原料 var obj = new Object(); //2:加工 obj.name = name; obj.age = age; obj.showInfo = function (

js学习对象创建

Object.extend = function(destination, source) {for (var property in source) {    destination[property] = source[property];}return destination;} Prototype 对Object类进行的扩展主要通过一个静态函数Object.extend(destination, source)实现了JavaScript中的继承. 从语义的角度,Object.extend

【JavaScript回顾】对象创建的几种模式

组合使用构造函数模式和原型模式 创建自定义类型的常见方式,就是组合使用构造函数模式与原型模式.构造函数模式用于定义实 例属性,而原型模式用于定义方法和共享的属性.结果,每个实例都会有自己的一份实例属性的副本, 但同时又共享着对方法的引用,大限度地节省了内存.另外,这种混成模式还支持向构造函数传递参 数:可谓是集两种模式之长. <script> function Person(name, age, job) { this.name = name; this.age = age; this.job

【深入理解Java虚拟机】Java内存区域模型、对象创建过程、常见OOM

本文内容来源于<深入理解Java虚拟机>一书,非常推荐大家去看一下这本书.最近开始看这本书,打算再开一个相关系列,来总结一下这本书中的重要知识点.呃呃呃,说好的那个图片请求框架呢~  不要急哈,因为这个请求框架设计的内容还是比较广的,目前业余时间正在编写当中,弄好了之后就会放上来.在完成之前,咱还是先来学习一下其他知识. 1.内存模型 java虚拟机在执行java程序的过程中会把它说管理的内存划分为若干个不同的数据区域,如下图所示: 图片来源于网络 (1)程序计数器(Program Count