c++中优化内存分配:new/delete操作符;allocator类对象的使用;operator new/operator delete函数及定位new表达式

1、C语言、c++中动态分配内存用什么?

  C语言中动态分配/释放内存用malloc( )和free( )函数,c++中使用new和delete表达式。


2、既然有了malloc( )和free( ),为什么还要new/delete呢?(以下为C++ C 编程指南中提到原因)

1)首先,malloc和free是C语言的库函数,而new和delete是C++的运算符

2)对于非内部数据类型的对象而言(比如说用户自定义类类型),只是使用malloc和free函数无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡的时候要自动执行析构函数。由于malloc和free是库函数而不是运算符,不在编译器控制权限之内,不能把执行构造函数和析构函数的任务强加于malloc、free

因此,C++需要一个能完成动态内存分配和初始化工作的操作符new,以及一个能完成对象析构和动态内存释放的运算符delete。


3、既然new/delete的功能完全覆盖malloc/free,为什么c++没有淘汰malloc/free出局?

这是因为c++程序经常需要调用C函数,而C程序只能用malloc和free函数管理动态内存。


4、如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错;如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错(c++primer中讲到,对于未构造的内存中的对象进行赋值而不是初始化,其行为是未定义的。因为赋值设计删除现存对象,如果没有现存对象,赋值操作符中的动作就会有灾难性效果),但是该程序的可读性差。因此,new/delete配对,malloc/free配对使用,不要混用。

以下是笔者好奇,在vs2010中测试混用malloc函数和delete操作符的程序:

#include <iostream>
using namespace std;


class Base
{
public:
Base(int val) : age(val){}
~Base()
{
cout << "~Base()" << endl;
}


private:


int age;
};


int main(void)
{
Base *pBase;
pBase = (Base *)malloc(sizeof(Base));
delete pBase;
return 0;
}

程序运行结果:

~Base()
请按任意键继续. . .


c++内存分配采用new/delete操作符,new表达式在分配适当内存后,自动运行合适的构造函数来初始化每个动态分配的类类型对象。

1、不是所有需求都要求在分配内存后,立即对该内存的对象初始化!一些情况往往需求将内存的分配和对象的初始化(对象的构造)分离开

1)new基于每个对象分配内存的事实可能会对某些类强加不可接受的运行时开销,这样的类可能需要使用户级的类类型对象分配的能够更快一些。这样的类使用的通用策略是,预先分配用于创建对象的内存,需要时在预先分配的内存中构造每个新对象。

2)另外,一些类希望按最小尺寸为自己的数据成员分配需要的内存。例如,标准库中的vector类预先分配额外的内存以保存加入的附加元素,将新元素加入到这个保留容器中。将元素保持在连续的内存中的时候,预先分配的空间可以是vector能够高效的加入元素。

这些都需要将内存分配与对象构造分离,在预先分配的内存中构造对象很浪费,可能会创建从不使用的对象。更微妙的是,如果预先分配的内存必须构造,某些类就不能使用它。例如,考虑vector,它使用预先分配策略。如果必须构造预先分配的内存中的对象,就不能有基类型为没有默认构造函数的vector——vector没有办法知道怎样构造这些对象。

说了这么多,就是说明白为什么要将内存分配与对象构造分离开来。


2、学习c++primer中,C++提供两种方法分配和释放未构造的原始内存

(1)第一种方法:使用allocator类

allocator类是一个类模板,使用它定义的具体对象来分配、释放未构造的原始内存

allocator<T> a;     定义名为a的allocator对象,可以分配内存和构造T类型的对象

1.1 分配、释放未构造的内存:

a.allocate(n)   用对象a分配原始的未构造内存的以保存T类型的n个对象,这个成员函数的返回值是分配内存的首地址

a.deallocate(p, n)    释放内存,在名为p的T *指针中包含的地址处保存T类型的n个对象。运行调用deallocator之前在该内存中构造的任意的对象的析构(使用destroy成员函数)是用户自己的责任。

1.2在原始内存中构造、析构对象

a.construct(p,t) 在T*指针p所指内存中构造一个新元素,运行T类型的复制构造函数用t初始化该对象。

a.destroy(p) 运行T*指针p所指对象的析构函数,当然也可以直接调用的对象的析构函数来撤销对象。

1.3一些情况下,原有内存不足以保存跟多对象时,需要开辟跟大的内存时,需要使用一些算法函数。算法uninitialized_fill和uninitialize_copy向fill和copy算法一样执行,除了它们在目的地构造对象而不是给对象赋值之外

uninitialized_copy(b,e,b2) 从迭代器b和e指出的输入范围将元素复制到从迭代器b2开始的未构造的原始内存中。该函数在目的地构造元素,而不是给他们赋值。假定b2指出的目的地足以保存输入范围中元素的副本

uninitialized_fill(b, e, t) 将由迭代器b和e指出的范围中的对象初始化为t的副本。假定该范围是未构造的原始内存。使用复制构造函数构造对象。

uninitialized_fill_n(b, e, t, n) 将由迭代器b和e指出的范围中至多n个对象初始化为t的副本。假定范围至少为n个元素大小。使用复制构造函数构造对象。


(2)第二种方法:使用operator new函数和operator delete函数

首先对new和delete操作符工作机制进行说明:

例如有如下 string *pstr =  new string("aaaoooeeeyyy");

实际上发生了三个步骤。首先,该表达式调用名为operator new的标准库函数,分配足够大的原始的为类型化的内存,以保存指定类型的对象;接着,运行该类型的一个构造函数,用指定初始化式构造对象;最后,返回分配内存的首地址。


new运算符和operator new函数:标准库函数operator new和operator delete的命名容易让人误解,与其他operator函数(如operator=)不同,这些函数没有重载new或delete表达式,实际上,我们不能重定义new和delete表达式的行为。


2.1 分配、释放未构造的内存:operator new 和 operator delete接口

void *operator new(size_t);  //分配size_t大小的未构造内存

void *operator new[ ](size_t);  //开辟数组

void *operator delete(void *); //释放内存

void *operator delete[ ](void *);  // 释放数组

operator new函数和operator delete表现功能与allocator类的allocate和deallocate成员类似,但他们有一个重要的不同点:operator new和operator delete函数是在void * 指针上操作,而不是在类型化的指针上操作。allocate成员分配类型化的内存,所以使用它的程序可以不必计算以字节为典韦的所需的内存量,也可以避免进行强制类型转换。一般而言,allocator更为类型安全。

2.2在未构造内存中构造对象:定位new表达式

定位new(placement new)表达式在已分配的原始内存中初始化一个对象,它不分配内存。它指向已分配但未构造内存的指针,并在该内存中初始化一个对象。

定位new表达式的形式是:

new (place_address) type

new (place_address) type (initializer-list)

定位new表达式初始化一个对象的时候,它可以使用任何构造函数,并直接建立对象。而construct函数总是使用复制构造函数。

2.3析构函数

直接调用析构函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值