C++ new 解析重载
C++ new 解析重载
new的三种形式:
(1)operator new(运算符new)
(2)new operator(new 操作)
(3)placement new(特殊的new操作)(不分配内存 + 构造函数的调用)
operator new
重载时体现运算符new
++++++++++++++++++++++++++++++++++++++++++++++++++++++
void * opertor new (size_t size ,const char * file ,long line)
{
cout << file << “:” << line << endl;
void *p = malloc(size);
return p
}
+++++++++++++++++++++++++++++++++++++++++
(1)只分配所要求的空间,不调用相关对象的构造函数。当无法满足所要求分配的空间时,则如果有new_handler,则调用new_handler,否则如果没要求不抛出异常(以nothrow参数表达),则执行bad_alloc异常,否则返回回0
(2) 可以被重载
(3) 重载时,返回类型必须声明为void*
(4) 重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t
(5) 重载时,可以带其它参数
new operator
String *str = new String(”hello”);
(1) 调用operator new分配足够的空间,并调用相关对象的构造函数 .
(2) 不可以被重载(作为运算符,new和sizeof一样,是C++内置的,你不能对它做任何的改变,除了使用它。)
palacement new
在已经分配的内存(可以是堆或者栈)上重新分配空间,但不会分配新的空间,只是构造函数的调用。
+++++++++++++++++++++++++++++++++++++++++
void * operator new(size_t size,void *p)
{
return p;
}
cahr buffer[1024]
Test *p2 = new(buffer) Test(200);//operator new(sizo_t ,void *p)
cout << p2->n_ <
+++++++++++++++++++++++++++++++++++++++++
C++ new 解析重载
最常用的是作为运算符的new,比如:
string *str = new string(“test new”);
作为运算符,new和sizeof一样,是C++内置的,你不能对它做任何的改变,除了使用它。
new会在堆上分配一块内存,并会自动调用类的构造函数。
C++ new用法之二 new函数
第二种就是new函数,其实new运算符内部分配内存使用的就是new函数,原型是:
void *operator new(size_t size);
new函数返回的是一个void指针,一块未经初始化的内存。如你所见,这和C语言的malloc行为相似,你可以重载new函数,并且增加额外的参数,但是必须保证第一个参数必须是size_t类型,它指明了分配内存块的大小,C++允许你这么做,当然一般情况下这是不必要的。如果重载了new函数,在使用new操作符时调用的就是你重载后的new函数了。
如果使用new函数,和语句string *str = new string(“test new”)相对的代码大概是如下的样子:
- string *str = (string*)operator new(sizeof(string));
- str.string(“test new”);
- // 当然这个调用时非法的,但是编译器是没有这个限制的
这还不算完,还有第三种的new存在。
C++ new用法之三 placement new
第三种,placement new,这也是new作为函数的一种用法,它允许你在一块已存在的内存上分配一个对象,而内存上的数据不会被覆盖或者被你主动改写,placement new同样由new操作符调用,调用格式是:
new (buffer) type(size_t size);
先看看下面的代码:
- char str[22];
- int data = 123;
- int *pa = new (&data) int;
- int *pb = new (str) int(9);
结果*pa = 123(未覆盖原数据),而*pb = 9(覆盖原数据),可以看到placement new 并没有分配新的内存,也可以使用在栈上分配的内存,而不限于堆。
为了使用placement new 你必须包含<new>或者<new.h>
其实placement new和第二种一样,只不过多了参数,是函数new的重载,语法格式为:
void *operator new(size_t, void* buffer);
它看起来可能是这个样子:
void *operator new(size_t, void* buffer) { return buffer;}
和new对应的就是delete了,需要回收内存啊,不然就泄漏了,这个下次再写吧,回忆一下今天的内容先。
总结
1. 函数new
void *operator new(size_t size); 在堆上分配一块内存,和placement new(void *operator new(size_t, void* buffer)); 在一块已经存在的内存上创建对象,如果你已经有一块内存,placement new会非常有用,事实上,它STL中有着广泛的使用。
2. 运算符new
最常用的new,没什么可说的。
3. 函数new不会自动调用类的构造函数,因为它对分配的内存类型一无所知;而运算符new会自动调用类的构造函数。
4. 函数new允许重载,而运算符new不能被重载。
5. 紧接着就是对应的delete。
new的六种重载形式
p = new P();
这样的代码的时候, 实际上有两步操作, 首先分配内存,
然后在分配好的内存之上初始化类成员.
第二步是有构造函数完成的, 第一步就是new函数的工作.
全局的new有六种重载形式,
void *operator new(std::size_t count)
throw(std::bad_alloc); //一般的版本
void *operator new(std::size_t count, //兼容早版本的new
const std::nothrow_t&) throw(); //内存分配失败不会抛出异常
void *operator new(std::size_t count, void *ptr) throw();
//placement版本
void *operator new[](std::size_t count) //
throw(std::bad_alloc);
void *operator new[](std::size_t count, //
const std::nothrow_t&) throw();
void *operator new[](std::size_t count, void *ptr) throw();
所以, 刚才的用法, 就是使用new函数的一种重载形式.
如果A这个对象以同样实行重载了new函数的化, 作为成员函数
会被优先调用.
C++的各种new简介
1.new T
第一种new最简单,调用类的(如果重载了的话)或者全局的operator new分配空间,然后用
类型后面列的参数来调用构造函数,用法是
new TypeName(initial_args_list). 如果没有参数,括号一般可以省略.例如
int *p=new int;
int *p=new int(10);
int *p=new foo("hello");
通过调用delete来销毁:
delete p;
2. new T[]
这种new用来创建一个动态的对象数组,他会调用对象的operator new[]来分配内存(如果
没有则调用operator new,搜索顺序同上),然后调用对象的默认构造函数初始化每个对象
用法:
new TypeName[num_of_objects];
例如
int *p= new int[10];
销毁时使用operator delete[]
3.new()T 和new() T[]
这是个带参数的new,这种形式的new会调用operator new(size_t,OtherType)来分配内存
这里的OtherType要和new括号里的参数的类型兼容,
这种语法通常用来在某个特定的地址构件对象,称为placement new,前提是operator new
(size_t,void*)已经定义,通常编译器已经提供了一个实现,包含<new>头文件即可,这个
实现只是简单的把参数的指定的地址返回,因而new()运算符就会在括号里的地址上创建
对象
需要说明的是,第二个参数不是一定要是void*,可以识别的合法类型,这时候由C++的重载
机制来决定调用那个operator new
当然,我们可以提供自己的operator new(size_,Type),来决定new的行为,比如
char data[1000][sizeof(foo)];
inline void* operator new(size_t ,int n)
{
return data[n];
}
就可以使用这样有趣的语法来创建对象:
foo *p=new(6) foo(); //把对象创建在data的第六个单元上
的确很有意思
标准库还提供了一个nothrow的实现:
void* operator new(std::size_t, const std::nothrow_t&) throw();
void* operator new[](std::size_t, const std::nothrow_t&) throw();
就可以实现调用new失败时不抛出异常
new(nothrow) int(10);
// nothrow 是std::nothrow_t的一个实例
placement new 创建的对象不能直接delete来销毁,而是要调用对象的析够函数来销毁对
象,至于对象所占的内存如何处理,要看这块内存的具体来源
4. operator new(size_t)
这个的运算符分配参数指定大小的内存并返回首地址,可以为自定义的类重载这个运算符,
方法就是在类里面声明加上
void *operator new(size_t size)
{
//在这里分配内存并返回其地址
}
无论是否声明,类里面重载的各种operator new和operator delete都是具有static属性的
一般不需要直接调用operator new,除非直接分配原始内存(这一点类似于C的malloc)
在冲突的情况下要调用全局的operator加上::作用域运算符:
::operator new(1000); // 分配1000个字节
返回的内存需要回收的话,调用对应的operator delete
5.operator new[](size_t)
这个也是分配内存,,只不过是专门针对数组,也就是new T[]这种形式,当然,需要时可以
显式调用
6.operator new(size_t size, OtherType other_value)
和operator new[](size_t size, OtherType other_value)
参见上面的new()
需要强调的是,new用来创建对象并分配内存,它的行为是不可改变的,可以改变的是各种
operator new,我们就可以通过重载operator new来实现我们的内存分配方案.