[Mac 10.7.1 Lion Intel-based x64 gcc4.2.1 xcode4.2]
Q: new操作符构造一个对象,它内部究竟调用了什么?
A:如下代码:
#include <iostream>
using namespace std;
#define COUT_ENDL(str) std::cout << #str << " is " << (str) << std::endl;
class A
{
public:
A(int value_one, int value_two):_value_one(value_one), _value_two(value_two) { }
int value_one() const { return _value_one; }
int value_two() const { return _value_two; }
virtual void show() const { std::cout << "show A..." << std::endl; }
private:
int _value_one;
int _value_two;
};
int main (int argc, const char * argv[])
{
A *a = new A(1, 2);
return 0;
}
为了便于查看汇编,先不写delete释放代码了。main函数的汇编代码如下:
0x00001b50 <main+0>: push %ebp
0x00001b51 <main+1>: mov %esp,%ebp
0x00001b53 <main+3>: sub $0x28,%esp
0x00001b56 <main+6>: mov 0xc(%ebp),%eax
0x00001b59 <main+9>: mov 0x8(%ebp),%ecx
0x00001b5c <main+12>: mov %ecx,-0x4(%ebp)
0x00001b5f <main+15>: mov %eax,-0x8(%ebp)
0x00001b62 <main+18>: movl $0xc,(%esp)
0x00001b69 <main+25>: call 0x1dd4 <dyld_stub__Znwm>
0x00001b6e <main+30>: mov %eax,-0x14(%ebp)
0x00001b71 <main+33>: mov -0x14(%ebp),%eax
0x00001b74 <main+36>: mov %eax,(%esp)
0x00001b77 <main+39>: movl $0x1,0x4(%esp)
0x00001b7f <main+47>: movl $0x2,0x8(%esp)
0x00001b87 <main+55>: call 0x1c68 <_ZN1AC1Eii>
0x00001b8c <main+60>: mov -0x14(%ebp),%eax
0x00001b8f <main+63>: mov %eax,-0x18(%ebp)
0x00001b92 <main+66>: movl $0x0,-0x10(%ebp)
0x00001b99 <main+73>: mov -0x10(%ebp),%eax
0x00001b9c <main+76>: mov %eax,-0xc(%ebp)
0x00001b9f <main+79>: mov -0xc(%ebp),%eax
0x00001ba2 <main+82>: add $0x28,%esp
0x00001ba5 <main+85>: pop %ebp
0x00001ba6 <main+86>: ret
对于第一个call, 前面传入的参数0xc, 很可能是类A的大小;第一个call具体调用的是__Znwm例程(它在动态库中)。使用otool命令得到上面生成的可执行文件(假设为testForCpp)依赖的库名称:
![](http://my.csdn.net/uploads/201206/02/1338644891_7707.png)
使用idaq工具打开/usr/lib/libstdc++.6.dylib文件,寻找operator new函数的位置。
![](http://my.csdn.net/uploads/201206/02/1338644948_9638.png)
可以看得出来,它和call指令对应的__Znwm是对应的。
上面汇编代码的第二个call指令,正是类A的构造函数。这个可以在类A的构造函数处加断点,进入后,反汇编得到此函数的内部名称,和call对应的名称是一致的。
同时,对于operator new的内部,可以发现它会调用malloc函数,如果失败,便会抛出异常。
![](http://my.csdn.net/uploads/201206/02/1338645329_1808.png)
Q:这么说来,上面的代码中调用的new可以分为两步来做?
A:是的。如下代码:
int main (int argc, const char * argv[])
{
void *mem = operator new(sizeof(A)); // alloc a mem
A *a = new (mem) A(1, 2); // call construct function in mem
delete a;
return 0;
}
第一步调用operator new申请空间,第二部在第一步申请的空间上调用构造函数。
Q:operator new函数是否可以重载?
A:是的。
void * operator new(size_t size)
{
std::cout << "operator new is called..." << std::endl;
return malloc(size);
}
int main (int argc, const char * argv[])
{
void *mem = operator new(sizeof(A)); // alloc a mem
A *a = new (mem) A(1, 2); // call construct function in mem
delete a;
return 0;
}
执行结果:
operator new is called...
不过这样要小心,因为这改变了调用new操作符的默认行为;同时,也应该提供一个重载的函数operator delete.
Q:可以在类中重载new操作符吗?
A:是的。
#include <iostream>
using namespace std;
#define COUT_ENDL(str) std::cout << #str << " is " << (str) << std::endl;
class A
{
public:
A(int value_one, int value_two):_value_one(value_one), _value_two(value_two) { }
int value_one() const { return _value_one; }
int value_two() const { return _value_two; }
virtual void show() const { std::cout << "show A..." << std::endl; }
void * operator new(size_t size)
{
std::cout << "operator new is called..." << std::endl;
return malloc(size);
}
private:
int _value_one;
int _value_two;
};
class B
{
};
int main (int argc, const char * argv[])
{
A *a = new A(1, 2);
delete a;
B *b = new B;
delete b;
return 0;
}
运行结果:
operator new is called...
可以看到,类A内部重载new的函数,类B是不会调用它的。
Q: A *a = new A(1, 2); 和 A a1(1, 2);这两句代码有什么不同呢?
A: 如下代码:
int main (int argc, const char * argv[])
{
A *a = new A(1, 2);
A a1(1, 2);
delete a;
return 0;
}
它对应的汇编代码:
0x00001a70 <main+0>: push %ebp
0x00001a71 <main+1>: mov %esp,%ebp
0x00001a73 <main+3>: sub $0x38,%esp
0x00001a76 <main+6>: mov 0xc(%ebp),%eax
0x00001a79 <main+9>: mov 0x8(%ebp),%ecx
0x00001a7c <main+12>: mov %ecx,-0x4(%ebp)
0x00001a7f <main+15>: mov %eax,-0x8(%ebp)
0x00001a82 <main+18>: movl $0xc,(%esp)
0x00001a89 <main+25>: call 0x1c50 <_ZN1AnwEm>
0x00001a8e <main+30>: mov %eax,-0x14(%ebp)
0x00001a91 <main+33>: mov -0x14(%ebp),%eax
0x00001a94 <main+36>: mov %eax,(%esp)
0x00001a97 <main+39>: movl $0x1,0x4(%esp)
0x00001a9f <main+47>: movl $0x2,0x8(%esp)
0x00001aa7 <main+55>: call 0x1bb0 <_ZN1AC1Eii>
0x00001aac <main+60>: mov -0x14(%ebp),%eax
0x00001aaf <main+63>: mov %eax,-0x18(%ebp)
0x00001ab2 <main+66>: lea -0x28(%ebp),%eax
0x00001ab5 <main+69>: mov %eax,(%esp)
0x00001ab8 <main+72>: movl $0x1,0x4(%esp)
0x00001ac0 <main+80>: movl $0x2,0x8(%esp)
0x00001ac8 <main+88>: call 0x1bb0 <_ZN1AC1Eii>
0x00001acd <main+93>: mov -0x18(%ebp),%eax
0x00001ad0 <main+96>: mov %eax,(%esp)
0x00001ad3 <main+99>: call 0x1d94 <dyld_stub__ZdlPv>
0x00001ad8 <main+104>: movl $0x0,-0x10(%ebp)
0x00001adf <main+111>: mov -0x10(%ebp),%eax
0x00001ae2 <main+114>: mov %eax,-0xc(%ebp)
0x00001ae5 <main+117>: mov -0xc(%ebp),%eax
0x00001ae8 <main+120>: add $0x38,%esp
0x00001aeb <main+123>: pop %ebp
0x00001aec <main+124>: ret
可以看出,二者的不同之处在于如果是局部变量,不需要执行申请对象所需空间的函数,直接调用构造函数。当然,很容易理解,栈已经为它提供了空间。
Q:既然可以使用operator new单独申请空间,然后单独调用构造函数,那么可否单独调用析构函数,然后释放内存?
A:是的。
#include <iostream>
using namespace std;
#define COUT_ENDL(str) std::cout << #str << " is " << (str) << std::endl;
class A
{
public:
A(int value_one, int value_two):_value_one(value_one), _value_two(value_two) { }
int value_one() const { return _value_one; }
int value_two() const { return _value_two; }
virtual void show() const { std::cout << "show A..." << std::endl; }
void * operator new(size_t size)
{
std::cout << "operator new is called..." << std::endl;
return malloc(size);
}
void operator delete(void *p)
{
free(p);
}
private:
int _value_one;
int _value_two;
};
int main (int argc, const char * argv[])
{
A *a = new A(1, 2);
a->A::~A(); // call deconstruct function
free(a); // free the mem
return 0;
}
运行ok.
xichen
2012-6-2 22:59:05
分享到:
相关推荐
1 ,malloc 与free 是C++/C 语言的标准库函数,new/delete 是C++ 的运算符。它们都可用于申请动态内存和释放内存。 2 ,对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要...
在这里,我们将总结C++-boost库的主要组件和功能。 时间与日期 boost库提供了多种时间和日期相关的类和函数,包括: 1. timer:提供毫秒级的计时精度,内部是通过std::clock取时间的。 2. progress_timer:自动...
高质量C++-C编程指南 目 录 前 言... 6 第1章 文件结构... 11 1.1 版权和版本的声明... 11 1.2 头文件的结构... 12 1.3 定义文件的结构... 13 1.4 头文件的作用... 13 1.5 目录结构... 14 第2章 程序的版式... 15 ...
本课程是C++ Primer初级教程,课程内容是学习C++语言基础知识,对应着教材的第1章到第8章。 第1章 快速入门 1.1 编写简单的C++程序 1.2 初窥输入/输出 1.2.1 标准输入与输出对象 1.2.2 一个使用IO库的程序 ...
希望这本书能够帮助您跨越C++的重重险阻,领略高处才有的壮美风光,做一个成功而快乐的C++程序员。 Effective C++中文版(第3版改善程序与设计的55个具体做法)》一共组织55个准则,每一条准则描述一个编写出更好的...
按常理说,C++编译器能够编译任何C程序,但是C和C++还是有一些小差别。 例如C++增加了C不具有的关键字。这些关键字能作为函数和变量的标识符在C程序中使用,尽管C++包含了所有的C,但显然没有任何C++编译器能编译...
Ivor Horton还著有Beginning Visual C++ 6、Beginning C Programming和Beginning Java 2等多部入门级好书。 目录 封面 -18 前言 -14 目录 -9 第1章 使用Visual C++ 2005编程 1 1.1 .NET Framework 1 1.2 CLR 2...
Effective C++(编程的50个细节)着重讲解了编写C++程序应该注意的50个细节问题,书中的每一条准则描述了一个编写出更好的C++的方式,每一个条款的背后都有具体范例支持,书中讲的都是C++的编程技巧和注意事项,很多都...
C++智能指针 智能指针_unique_ptr智能指针详解 智能指针详解 作为智能指针的⼀种,unique_ptr 指针⾃然也具备"在适当时机⾃动释放堆内存空间"的能⼒。和 shared_ptr 指针最⼤的不同之处在 于,unique_ptr 指针指向的...
【原书名】 C++ Primer (4th Edition) 【原出版社】 Addison Wesley/Pearson 【作者】 (美)Stanley B.Lippman,Josée LaJoie,Barbara E.Moo 【译者】 李师贤 蒋爱军 梅晓勇 林瑛 【丛书名】 图灵计算机科学丛书 ...
7.8 有了malloc/free为什么还要new/delete ?... 52 7.9 内存耗尽怎么办?... 53 7.10 malloc/free 的使用要点... 54 7.11 new/delete 的使用要点... 55 7.12 一些心得体会... 56 第8章 C++函数的高级特性... ...
本书针对visual c++ 2010版本做了全面更新,介绍了最新开发环境,讲述了如何使用visual c++构建真实世界的应用程序。 采用了容易理解的讲授方法,并提供了详尽的示例,旨在帮助读者掌握编程技巧 内容简介 ...
1.2为什么C++会成功 1.2.1较好的C 1.2.2采用渐进的学习方式 1.2.3运行效率 1.2.4系统更容易表达和理解 1.2.5“库”使你事半功倍 1.2.6错误处理 1.2.7大程序设计 1.3方法学介绍 1.3.1复杂性 1.3.2内部原则 1.3.3...
7.8 有了MALLOC/FREE 为什么还要NEW/DELETE ?. 7.9 内存耗尽怎么办?. 7.10 MALLOC/FREE 的使用要点 7.11 NEW/DELETE 的使用要点. 7.12 一些心得体会 第8 章 C++函数的高级特性 8.1 函数重载的概念. 8.2 成员函数的...
<br> <br>习题1 绪论------------------------------------------------------------------------------------6 习题2 线性表-------------------------------------------------------------------------...
不能在类内部初始化,一般在类外部初始化,并且初始化时不加 static ;修饰成员函数时,该函数不接受 this 指针,只能访问类的静态成员;不需要实例化对象即可访问。 四、#define 和 const 的区别: #define 宏是...
7.8 有了malloc/free为什么还要new/delete ? 7.9 内存耗尽怎么办? 7.10 malloc/free 的使用要点 7.11 new/delete 的使用要点 7.12 一些心得体会 第8章 C++函数的高级特性 8.1 函数重载的概念 8.2 成员函数的重载、...
命名的自动对象是在函数内部定义的对象,生命期限于函数的执行期。匿名的自动对象是临时对象,只在函数调用返回时产生。动态对象是利用new运算符创建的对象,生命期限于程序的运行期。 4. 对象的初始化方法 对象的...
高质量C/C++编程指南.PDF 作者:林锐 目录: 前 言 第1章 文件结构 1.1 版权和版本的声明 1.2 头文件的结构 1.3 定义文件的结构 1.4 头文件的作用 1.5 目录结构 第2章 程序的版式 2.1 空行 2.2 代码行 2.3 代码行内...