在阅读Bruce Eckel时,我遇到了以下示例:
#include <cstdio>
#include <cstdlib>
using namespace std;
void* operator new(size_t sz)
{
printf("operator new: %d Bytes\n", sz);
void* m = malloc(sz);
if(!m) puts("out of memory");
return m;
}
void operator delete(void* m)
{
puts("operator delete");
free(m);
}
class S {
int i[100];
public:
S() { puts("S::S()"); }
~S() { puts("S::~S()"); }
};
int main() {
puts("creating & destroying an int");
int* p = new int(47);
delete p;
puts("creating & destroying an s");
S* s = new S;
delete s;
puts("creating & destroying S[3]");
S* sa = new S[3];
delete []sa;
}
我怀疑以下陈述:
请注意,我使用printf( )
和puts( )
而不是iostream。这是因为在创建iostream
对象时(如全局cin
,cout
和cerr
),它会调用operator new
来分配内存。使用printf( )
,您不会陷入僵局,因为它没有调用new
来初始化自己。
但是,当我在用cout
替换put之后运行程序时,我没有遇到这样的死锁。任何人都可以解释一下吗?
operator new
返回一个void指针,但最后我们得到了一个动态分配对象的指针。那么它是一个构造函数,它返回一个指向该对象的指针(虽然构造函数没有返回类型)或者它内部的编译器吗?
答案 0 :(得分:2)
1)C ++标准包含许多通常不使用的“mays”。这是其中之一。运算符&lt;&lt;函数可能使用动态内存,但它没有。通常情况并非如此。
因此,您的示例有效并不意味着它是正确的。它只是意味着它适用于您正在使用的实现。在其他实现中它可能会破坏。
标准定义的“mays”也可以向另一个方向发展。例如,任何STL-Header可以包括任何其他标准头,但不是必须这样做。这通常是iostream标头以及istream和ostream标头的情况。当使用iostream时,几乎任何实现都包括ostream和istream,但从技术上讲它们并不需要。这意味着当你使用ostream并且只包含iostream时,程序可能会起作用,但实际上就标准而言它是不正确的。
对于c和c ++来说,知道什么可能会默默地破坏是非常重要的,但这通常不是很容易。
答案 1 :(得分:1)
使用printf()
来避免递归调用operator new()
是一种安全措施 - 只是为了确保它有效。你怎么知道使用iostream
永远不会调用operator new()
函数?
您使用operator new()
函数混淆了新表达式(一种语言结构)。 新表达式确实会返回一个类型化指针,但是对operator new()
函数的调用是在“引擎盖下”完成的,operator new()
函数返回void*
。编译器为此生成所有必要的代码。