`
java-mans
  • 浏览: 11525295 次
文章分类
社区版块
存档分类
最新评论

new内部在做些什么----小话c++(6)

 
阅读更多

[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)依赖的库名称:


使用idaq工具打开/usr/lib/libstdc++.6.dylib文件,寻找operator new函数的位置。

可以看得出来,它和call指令对应的__Znwm是对应的。

上面汇编代码的第二个call指令,正是类A的构造函数。这个可以在类A的构造函数处加断点,进入后,反汇编得到此函数的内部名称,和call对应的名称是一致的。

同时,对于operator new的内部,可以发现它会调用malloc函数,如果失败,便会抛出异常。



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


分享到:
评论

相关推荐

    Malloc和new区别

    1 ,malloc 与free 是C++/C 语言的标准库函数,new/delete 是C++ 的运算符。它们都可用于申请动态内存和释放内存。 2 ,对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要...

    C++-boost库总结.doc

    在这里,我们将总结C++-boost库的主要组件和功能。 时间与日期 boost库提供了多种时间和日期相关的类和函数,包括: 1. timer:提供毫秒级的计时精度,内部是通过std::clock取时间的。 2. progress_timer:自动...

    高质量C++-C编程指南

    高质量C++-C编程指南 目 录 前 言... 6 第1章 文件结构... 11 1.1 版权和版本的声明... 11 1.2 头文件的结构... 12 1.3 定义文件的结构... 13 1.4 头文件的作用... 13 1.5 目录结构... 14 第2章 程序的版式... 15 ...

    非常经典的c++ primer视频教程6-10

    本课程是C++ Primer初级教程,课程内容是学习C++语言基础知识,对应着教材的第1章到第8章。 第1章 快速入门 1.1 编写简单的C++程序 1.2 初窥输入/输出 1.2.1 标准输入与输出对象 1.2.2 一个使用IO库的程序 ...

    Effective C++ 中文版

    希望这本书能够帮助您跨越C++的重重险阻,领略高处才有的壮美风光,做一个成功而快乐的C++程序员。 Effective C++中文版(第3版改善程序与设计的55个具体做法)》一共组织55个准则,每一条准则描述一个编写出更好的...

    新手学习C++入门资料

    按常理说,C++编译器能够编译任何C程序,但是C和C++还是有一些小差别。 例如C++增加了C不具有的关键字。这些关键字能作为函数和变量的标识符在C程序中使用,尽管C++包含了所有的C,但显然没有任何C++编译器能编译...

    Visual C++ 2005入门经典--源代码及课后练习答案

    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++

    Effective C++(编程的50个细节)着重讲解了编写C++程序应该注意的50个细节问题,书中的每一条准则描述了一个编写出更好的C++的方式,每一个条款的背后都有具体范例支持,书中讲的都是C++的编程技巧和注意事项,很多都...

    C++智能指针-unique-ptr智能指针详解.pdf

    C++智能指针 智能指针_unique_ptr智能指针详解 智能指针详解 作为智能指针的⼀种,unique_ptr 指针⾃然也具备"在适当时机⾃动释放堆内存空间"的能⼒。和 shared_ptr 指针最⼤的不同之处在 于,unique_ptr 指针指向的...

    C++ Primer第四版【中文高清扫描版】.pdf

    【原书名】 C++ Primer (4th Edition) 【原出版社】 Addison Wesley/Pearson 【作者】 (美)Stanley B.Lippman,Josée LaJoie,Barbara E.Moo 【译者】 李师贤 蒋爱军 梅晓勇 林瑛 【丛书名】 图灵计算机科学丛书 ...

    高质量C++-C编程指南.htm

    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入门经典(第5版)--源代码及课后练习答案

     本书针对visual c++ 2010版本做了全面更新,介绍了最新开发环境,讲述了如何使用visual c++构建真实世界的应用程序。  采用了容易理解的讲授方法,并提供了详尽的示例,旨在帮助读者掌握编程技巧 内容简介  ...

    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...

    高质量C/C++编程指南(PDF)

    7.8 有了MALLOC/FREE 为什么还要NEW/DELETE ?. 7.9 内存耗尽怎么办?. 7.10 MALLOC/FREE 的使用要点 7.11 NEW/DELETE 的使用要点. 7.12 一些心得体会 第8 章 C++函数的高级特性 8.1 函数重载的概念. 8.2 成员函数的...

    数据结构(C++)有关练习题

    &lt;br&gt; &lt;br&gt;习题1 绪论------------------------------------------------------------------------------------6 习题2 线性表-------------------------------------------------------------------------...

    C++ 开发面试题必知必会-V1版.pdf

    不能在类内部初始化,一般在类外部初始化,并且初始化时不加 static ;修饰成员函数时,该函数不接受 this 指针,只能访问类的静态成员;不需要实例化对象即可访问。 四、#define 和 const 的区别: #define 宏是...

    新手必看编程法则C++

    7.8 有了malloc/free为什么还要new/delete ? 7.9 内存耗尽怎么办? 7.10 malloc/free 的使用要点 7.11 new/delete 的使用要点 7.12 一些心得体会 第8章 C++函数的高级特性 8.1 函数重载的概念 8.2 成员函数的重载、...

    C++程序设计与实践:07-第6章 深入类和对象.ppt

    命名的自动对象是在函数内部定义的对象,生命期限于函数的执行期。匿名的自动对象是临时对象,只在函数调用返回时产生。动态对象是利用new运算符创建的对象,生命期限于程序的运行期。 4. 对象的初始化方法 对象的...

    高质量C++编程指南.PDF

    高质量C/C++编程指南.PDF 作者:林锐 目录: 前 言 第1章 文件结构 1.1 版权和版本的声明 1.2 头文件的结构 1.3 定义文件的结构 1.4 头文件的作用 1.5 目录结构 第2章 程序的版式 2.1 空行 2.2 代码行 2.3 代码行内...

Global site tag (gtag.js) - Google Analytics