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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值