c ++ operator new [] / delete []是否调用operator new / delete?

时间:2018-01-06 17:22:44

标签: c++

c ++ operator new [] / delete [] (不是我的)调用operator new / delete吗?

在我用自己的实现替换operator newoperator delete后,以下代码将调用它们:

int *array = new int[3];
delete[] array;

当我同时替换operator new[]operator delete[]时,上面的代码只会调用

我的运营商实施:

void *operator new(std::size_t blockSize) {
    std::cout << "allocate bytes: " << blockSize << std::endl;
    return malloc(blockSize);
}

void *operator new[](std::size_t blockSize) {
    std::cout << "[] allocate: " << blockSize << std::endl;
    return malloc(blockSize);
}

void operator delete(void *block) throw() {
    int *blockSize = static_cast<int *>(block);
    blockSize = blockSize - sizeof(int);
    std::cout << "deallocate bytes: " << *blockSize << std::endl;
    free(block);
}

void operator delete[](void *block) throw() {
    int *blockSize = static_cast<int *>(block);
    blockSize = blockSize - sizeof(int);
    std::cout << "[] deallocate bytes: " << *blockSize << std::endl;
    free(block);
}

我还有第二个问题可能没那么相关,为什么代码打印出来:

[] allocate: 12
[] deallocate bytes: 0

而不是:

[] allocate: 16
[] deallocate bytes: 16

1 个答案:

答案 0 :(得分:2)

由于分配运算符newnew[]几乎都做同样的事情(a),因此根据另一个来定义一个是有意义的。它们都用于分配给定大小的块,无论您打算使用它。同样适用于deletedelete[]

事实上,标准是必需。 C ++ 11 18.6.1.2 /4(例如)声明operator new[]的默认行为是返回operator new(size)/13 operator delete[]void *operator new(std::size_t sz) { return malloc(sz); } void operator delete(void *mem) throw() { free(mem); } void *operator new[](std::size_t sz) { return operator new(sz); } void operator delete[](void *mem) throw() { return operator delete(mem); } 有类似的限制。

所以示例默认实现类似于:

new

当您替换deletenew[]个功能时,delete[]new[]功能仍然会使用它们。但是,将delete[]new替换为您自己的调用deleteint[3]的函数会导致它们断开连接。

这就是为什么你会看到问题第一部分所描述的行为。

根据问题的第二部分,您会看到我期望看到的内容。 12的分配要求三个整数,每个大小四个字节(在您的环境中)。这显然是0xa55a个字节。

为什么它似乎释放零字节有点复杂。您似乎认为紧接在您给出的地址之前的四个字节是块的大小,但不一定如此。

实现可以自由地在内存领域中存储他们喜欢的任何控制信息(b),包括以下可能性(这绝不是详尽无遗的):

  • 当前内存分配的大小;
  • 指向下一个(可能是之前的)控制块的链接;
  • 一个标记值(例如#include <iostream> #include <memory> // Need to check this is enough to maintain alignment. namespace { const int buffSz = 16; } // New will allocate more than needed, store size, return adjusted address. void *operator new(std::size_t blockSize) { std::cout << "Allocating size " << blockSize << '\n'; auto mem = static_cast<std::size_t*>(std::malloc(blockSize + buffSz)); *mem = blockSize; return reinterpret_cast<char*>(mem) + buffSz; } // Delete will unadjust address, use that stored size and free. void operator delete(void *block) throw() { auto mem = reinterpret_cast<std::size_t*>(static_cast<char*>(block) - buffSz); std::cout << "Deallocating size " << *mem << '\n'; std::free(mem); } // Leave new[] and delete[] alone, they'll use our functions above. // Test harness. int main() { int *x = new int; *x = 7; int *y = new int[3]; y[0] = y[1] = y[2] = 42; std::cout << *x << ' ' << y[1] << '\n'; delete[] y; delete x; } 或控制块的校验和)以捕捉竞技场损坏。

除非你知道并控制内存分配函数如何使用它们的控制块,否则你不应该做出假设。首先,为了确保正确对齐,可以用其他无用的数据填充控制块。如果您想保存/使用所需的大小,您需要自己完成以下操作:

Allocating size 4
Allocating size 12
7 42
Deallocating size 12
Deallocating size 4

运行该代码会导致打印成功的值:

new MyClass

(a)在构造对象时,new MyClass[7]NULL之间的差异以后比分配阶段。基本上,它们都会分配所需的内存一次,然后根据需要在该内存中构造尽可能多的对象(前者一次,后者七次)。

(b)并且允许实现不将任何控件信息内联存储。我记得在嵌入式系统上工作,我们知道没有任何分配超过1K。所以我们基本上创建了一个没有内联控制块的竞技场。相反,它有一点内存,几百个这样的1K块,并使用位图来决定哪些是正在使用的,哪些是免费的。

如果有人要求更多而不是1K,那么得到{{1}}。那些要求小于或等于1K的人无论如何得到1K。不用说,它比实现提供的通用分配功能快得多。