原文地址:http://www.cnblogs.com/jamesmile/archive/2010/04/17/1714311.html,在此感谢

C++中的operator new与new operator,看上去挺像的两姐妹,却有天壤之别。

重载的 operator new 必须是类成员函数或全局函数,而不可以是某一名空间
之内的函数或是全局静态函数。此外,还要多加注意的是,重载 operator new 时需要兼容默
认的 operator new 的错误处理方式,并且要满足 C++ 的标准规定 :当要求的内存大小为 0
byte 时也应该返回有效的内存地址。

如果使用不同的参数类型重载 operator new/delete,则请采用如下函数声明形式:
// 返回的指针必须能被普通的 ::operator delete(void*) 释放
void* operator new(size_t size, const char* file, int line);
// 析构函数抛异常时被调用
void operator delete(void* p, const char* file, int line);
调用时采用以下方式:
string* pStr = new (__FILE, __LINE__) string;
这样就能跟踪内存分配的具体位置,定位这个动作发生在哪个文件的哪一行代码中了。
在“检测内存错误”和“统计内存使用数据”时通常会用这种方式重载。

operator new

(1) 只分配所要求的空间,不调用相关对象的构造函数。当无法满足所要求分配的空间时,则

        ->如果有new_handler,则调用new_handler,否则

        ->如果没要求不抛出异常(以nothrow参数表达),则执行bad_alloc异常,否则

        ->返回0

(2) 可以被重载

(3) 重载时,返回类型必须声明为void*

(4) 重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t

(5) 重载时,可以带其它参数

new operator

(1) 调用operator new分配足够的空间,并调用相关对象的构造函数

(2) 不可以被重载

相应地,operator delete与delete operator有相似的特性。

举个例子

class X 
{
public:
…………
    static void* operator new(size_t size)
{
    return ::operator new(size);
}
static void operator delete(void* pointee)
{
    ::operator delete(pointee);
}
…………
};
X* px = new X();

该行代码中的new为new operator,它将调用类X中的operator new,为该类的对象分配空间,然后调用当前实例的构造函数。

delete px;

该行代码中的delete为delete operator,它将调用该实例的析构函数,然后调用类X中的operator delete,以释放该实例占用的空间。

new operator与delete operator的行为是不能够也不应该被改变,这是C++标准作出的承诺。而operator new与operator delete和C语言中的malloc与free对应,只负责分配及释放空间。但使用operator new分配的空间必须使用operator delete来释放,而不能使用free,因为它们对内存使用的登记方式不同。反过来亦是一样。

你可以重载operator new和operator delete以实现对内存管理的不同要求,但你不能重载new operator或delete operator以改变它们的行为。

当重载operator new时,可以提供更多的参数,在new一个对象时,通过在关键字new后的括号传递额外的参数。比如以下的类

class A 
{
public:
    …………
    static void* operator new(size_t size, const string& example)
{
    cout << example << endl;
    return ::operator new(size);
}
…………
};
A* pa = new (“This will be printed out in operator new”) A();

新标准的C++允许以这样的方式传递一个名为nothrow的参数,以表明当为该对象分配空间失败时,不抛出异常,而是返回0,以兼容旧标准new的行为。比如

class B {};
B* pb = new (nothrow) B();

当然这只能对那些使用默认operator new操作符的类。对已经重载了operator new的类(比如上面的X和A),如果不声明能接受nothrow参数,自然无法享受C++标准带来的礼物。

operator 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来实现我们的内存分配方案.

--------------------------------------------

 #include <iostream>
#include <string>
#include <crtdbg.h>
using namespace std; class X
{
public:
X(){cout<<"constructor of X"<<endl;}
~X(){cout<<"deconstructor of X"<<endl;} void* operator new(size_t size,string str)
{
cout<<"operator new size:"<<size<<" with string:"<<str<<endl;
return ::operator new(size);
} void operator delete(void *point)
{
cout<<"operator delete"<<endl; operator delete(point);
//::operator delete(point);
} private:
int num;
};
int main(){
X *px = new("A new class") X;
delete px;
cout<<sizeof("A new class")<<endl;
string strd="A new class";
int len = sizeof("A new class");
for (int i = ;i<len;i++)
{
cout<<strd[i];
}
cout<<"END";
return ;
}

在调用前面的两个冒号很关键,是为了避免进行无限递归(没有它函数将一直调用自己下去)。

关于"::"的小插曲:

     #include <iostream>
using namespace std;
int n = ;// A global variable
int main()
{
int n = ;// A local variable
cout << ::n << endl;// Print the global variable: 12
cout << n << endl;// Print the local variable: 13
}

2019-03-06

是否调用构造函数/析构函数

使用new操作符来分配对象内存时会经历三个步骤:

  • 第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
  • 第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。
  • 第三部:对象构造完成后,返回一个指向该对象的指针。

使用delete操作符来释放对象内存时会经历两个步骤:

  • 第一步:调用对象的析构函数。
  • 第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。

总之来说,new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会。如果你不嫌啰嗦可以看下我的例子:

class A
{
public:
A() :a(1), b(1.11){}
private:
int a;
double b;
};
int main()
{
A * ptr = (A*)malloc(sizeof(A));
return 0;
}

在return处设置断点,观看ptr所指内存的内容:

可以看出A的默认构造函数并没有被调用,因为数据成员a,b的值并没有得到初始化,这也是上面我为什么说使用malloc/free来处理C++的自定义类型不合适,其实不止自定义类型,标准库中凡是需要构造/析构的类型通通不合适。

而使用new来分配对象时:

int main()
{
A * ptr = new A;
}

查看程序生成的汇编代码可以发现,A的默认构造函数被调用了:

from this article

--------------------------------------------

有关于三者的区别

operator new,new operator,placement new的区别的更多相关文章

  1. [C++空间分配]new运算符、operator new、placement new的区别于联系

    先科普一下: 1. new的执行过程: (1)通过operator new申请内存 (2)使用placement new调用构造函数(内置类型忽略此步) (3)返回内存指针 2. new和malloc ...

  2. operator new和operator delete

    从STL源码剖析中看到了operator new的使用 template<class T> inline void _deallocate(T* buffer) { ::operator ...

  3. 定制自己的new和delete:operator new 和 operator delete

    new和delete不同用法 基本用法 int * aptr = new int(10); delete aptr, aptr = nullptr; 上面的代码是我们最基本也是最常见的使用new和de ...

  4. 类型转换运算符、*运算符重载、->运算符重载、operator new 和 operator delete

    一.类型转换运算符 必须是成员函数,不能是友元函数 没有参数 不能指定返回类型 函数原型:operator 类型名();  C++ Code  1 2 3 4 5 6 7 8 9 10 11 12 1 ...

  5. ZT 自定义operator new与operator delete的使用(1)

    http://blog.csdn.net/waken_ma/article/details/4004972 先转两篇文章: 拨开自定义operator new与operator delete的迷雾 C ...

  6. Effective C++ 第二版 8) 写operator new 和operator delete 9) 避免隐藏标准形式的new

    条款8 写operator new 和operator delete 时要遵循常规 重写operator new时, 函数提供的行为要和系统缺省的operator new一致: 1)正确的返回值; 2 ...

  7. 从零开始学C++之重载 operator new 和 operator delete 实现一个简单内存泄漏跟踪器

    先来说下实现思路:可以实现一个Trace类,调用 operator new 的时候就将指向分配内存的指针.当前文件.当前行等信息添加进Trace 成员map容器内,在调用operator delete ...

  8. C++面向对象高级编程(九)Reference与重载operator new和operator delete

    摘要: 技术在于交流.沟通,转载请注明出处并保持作品的完整性. 一 Reference 引用:之前提及过,他的主要作用就是取别名,与指针很相似,实现也是基于指针. 1.引用必须有初值,且不能引用nul ...

  9. operator new 和 operator delete 实现一个简单内存泄漏跟踪器

    先来说下实现思路:可以实现一个Trace类,调用 operator new 的时候就将指向分配内存的指针.当前文件.当前行等信息添加进Trace 成员map容器内,在调用operator delete ...

随机推荐

  1. M2阶段事后总结

    设想和目标 1. 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述?我们的主要任务是将35w+个符合条件的网页,问答,文章放入数据库:爬取功能定义为以下几种:通用型爬取 ...

  2. 第六次作业-my Backlog

    杨灵超小组 My  Backlog                                小学生四则运算自动生成(Backlog) ID Name IMP EST How   to Demo ...

  3. debug网页时小问题The source attachment does not contain the source for the file

    第一次debug总是出现下图问题,提示我没加源码... The source attachment does not contain the source for the file ... 解决方法: ...

  4. Failed to execute goal org.springframework.boot

    报错 [ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:1.4.0.RELEASE:ru ...

  5. linux 终端快捷操作

    终端一些常用快捷键 按键 作用 Ctrl+d 键盘输入结束或退出终端 Ctrl+s 暂停当前程序,暂停后按下任意键恢复运行 Ctrl+z 将当前程序放到后台运行,jobs命令查看后台工作,命令&quo ...

  6. mysql理论结合实际篇(一)

    最近两天做需求,是要将退款和退货报表里使用的临时表改用固定表, 自己建表时,如(只是举例): CREATE TABLE tasks ( task_id INT UNSIGNED NOT NULL AU ...

  7. 2016_NENU_CS_3

    贴一下比赛的代码,  其中 I 题代码源于final大神 ok_again http://acm.hust.edu.cn/vjudge/contest/127444#overview I /***** ...

  8. 关于MySQL中添加数据的两种方法

    下面介绍两种执行SQL命令的方法,并作出相应地总结,第一种介绍一种常规用法,下面进行做简要地分析,首先我们需要执行打开数据库操作首先创建一个MySqlConnection对象,在其构造函数中传入一个连 ...

  9. Get请求,Post请求乱码问题解决方案

    下面以两种常见的请求方式为例讲解乱码问题的解决方法. 1.Post方式请求乱码. 自从Tomcat5.x以来,Get方式和Post方式提交的请求,tomcat会采用不同的方式来处理编码. 对于Post ...

  10. Java代码redis基础操作

    maven依赖包: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis< ...