C++ Q&A(四)new 运算符

来源:互联网 发布:编程大师访谈录 编辑:程序博客网 时间:2024/05/17 21:38

声明:Q&A系列的文章是我在平时自己遇到的或者看到的一些问题,本着再小的问题也需要有人解答的想法,将这些问题和答案整理出来。

欢迎和我讨论问题,同时也欢迎转载Q&A系列的文章。


将内存分配和初始化分开:
C++允许把内存的分配和初始化分开。从原则上说,初始化由构造函数完成。
静态函数在程序连接时完成分配,局部对象在堆栈上完成分配,由new运算符创建的的对象通过适当的operator new()分配。释放的方式与此类似。

例如:
class X
{
    //.....
public :
    void *operator new(size_t, sz); //alloc ate sz type
    void operator delete(void *p); //free p

    X();                //initialize
    X(int i);         //initialize
    ~X();             //cleanup
    //.....
}:

类型size_t是由实现定义的一个整型,用于保存对象大小。operate new(size_t sz)中的sz参数不用在调用的时候显式的提供,而是由系统自动赋值,我们只需要声明这个参数就行了。
在这里,new运算符的作用是保证相互分离的存储分配和初始化能正确地放在一起使用。例如,编译系统的一个工作是对分配系统的一个调用
X::operator new(),并在为X所用的new中产生一个对X的构造函数的调用。从逻辑上看,X::operator new()是在构造函数之前调用的。
所以,它要返回一个void*而不是X*。在分配了内存之后,构造函数就会在这片内存上构造起一个X对象。
与此相对应的,在析构函数“消解”(不是释放)一个对象,给operator delete()一个没有特征的内存,让它去释放。因此给operate delete()的是一个void *参数,而不是X*。
在继承的情况下,一个派生类的对象也会调用基类的operator new()进行分配内存。需要注意的是,不管派生类有没定义自己的operator new()函数,基类的operator new()函数都会被调用。

具体代码:
#include <iostream>class A{public : void *operator new (size_t sz) {  std::cout << "A's sz is " << sz << std::endl;  return nullptr; }  void *operator new (size_t sz, char *s) {  std::cout << "A's sz is " << sz << std::endl;  std::cout << s << std::endl;  return nullptr; }public : void f(){ std::cout << "this is A" << std::endl; }};int main(){ A *pa = new A; A *pb = new("hello world") A; system("PAUSE"); return 0;}

数组的分配:

在很多情况下,我们都需要用到数组。而X:;operator new()和X::operator delete(),只能用来生成一个对象。所以C++又提供了一对新的函数
operator new[](size_t sz) 和 operator delete[](void *)。和所有分配内存的程序一样,operator new[]的作用就是提供所需的一定长度的存储,它本身并不关心这些内存将会被怎么使用,它也不需要知道这个数组的维数和元素个数,它只负责给出内存。

放置:
问题:
1) 我们需要一种机制把一个对象安放到某个特定的地址。例如,把一个表示进程的对象放到特定硬件所要求的特定的地址去。
2) 我们需要一种机制在某个特定分配区里分配对象。例如,在一个多处理器系统的共享存储中。
为了解决上面两个问题,C++允许我们对new函数进行重载,以便为new运算符提供一种允许有附加参数的的语法形式。
例如,为了把一个对象分配到到特定的分配区的operator new()可能被定义为;
void *operator new(size_t sz, void *p)
{
    return p;//place object at 'p'
}
它的调用可能是:
void *buf = (void *)0xF00F;    //significant address
X *p2 = new(buf) X;               //construct an X at 'buf'
                                               //invokes : operator new (sizeof(X), buf)   
这种提供附加信息的语法形式被称为放置语法形式。值得注意的是,在重载的时候size_t 都要作为函数的第一个参数,这是根据被分配的对象自动提供的。
在这种形式之下,new不再是一种简单的内存分配机制,因为我们可以给特定的存储位置关联上任意的逻辑性质,这就使new能够起到一种通用资源管理的作用。
为某个特定分配场地定义的operator new()可能为:
先定义一个类fast_arena
{    //...    char *maxp;    char  *freep;    char *expand(size_t); //get more memory from general purpose allocatorpublic :    void *alloc(size_t s)    {        char *p = freep;        return (freep += s < maxp) ? p : expand(s);    }    void free(void *){}            //ignore    clear();                              //free all allocated memory};void *operator new(size_t s, fast_arena& a){    return a.alloc(s);}

这个new运算符可能会被这样调用:
void f(fast_arena &arena){    X *p = new (arena) X; // alloc X in arena}

参考资料:《C++设计与演化》

PS:这本书很值得一读,说了很多关于C++的秘辛。

0 0
原创粉丝点击