为什么这个 C++ 构造函数在这个链表的实现中在同一个内存位置被调用两次?
Why is this C++ constructor called twice at the same memory location in this implementation of a linked list?
我正在阅读 Shaffer 的《Data Structures and Algorithm Analysis in C++》(可在 https://people.cs.vt.edu/shaffer/Book/ 免费获取),因为我看到它使用了模板,我正在尝试学习更多关于 C++ 和 C++ 泛型编程的知识.
作者在介绍Linked Lists时,给出了一个使用freelist的变体。 (这是大约第 111 页,pdf 第 130 页)。在他的实现中,他为 Link
创建了一个 class 并覆盖了 operator new
以从空闲列表中拉出。然后有一个 class for LinkedList
.
然而,事实证明,在此实现中,调用 new LinkedList
将导致同一对象调用构造函数两次,我无法弄清楚原因。
此代码显示了示例的相关部分。
#include <cstddef>
#include <iostream>
struct S {
S() { std::cout << "ctor at " << this << "\n"; }
~S() { std::cout << "dtor at " << this << "\n"; }
};
class Link {
private:
static Link * freelist;
S elem;
Link * next;
public:
void * operator new(size_t) {
if (freelist == nullptr) { return ::new Link; }
Link * tmp = freelist;
freelist = freelist->next;
return tmp;
}
// other logic
};
Link * Link::freelist = nullptr;
class LinkedList {
private:
Link * head;
public:
LinkedList() { head = new Link; }
// other logic
};
int main() { LinkedList ll; }
此代码的示例输出是
ctor at 0x458bbd954eb0
ctor at 0x458bbd954eb0
所以我们可以看到结构 S
的构造函数被同一个对象调用了两次。
这是我所期望的。
- 在
main
中,我们构造一个LinkedList
.
- 在这个 LinkedList 中,我们调用
new Link
。我相信这会调用 Link::operator new
,我们已经覆盖了它。
- 在
Link::operator new
中,我们看到Link
发现它的freelist是空的,于是调用::new Link
.
::new Link
构造一个Link,包括它的私有成员变量S elem
,构造一个S
结构。
显然这是不正确的。我敢打赌我从编译器中遗漏了一个隐式 new
--- 但即便如此,我还是很惊讶单个 S
实例的构造函数被调用了两次。所以我的心智模型肯定是不完整的。
你能帮忙解释一下为什么构造函数在内存中的同一位置被调用两次吗?
operator new
的任务是分配内存;它必须 而不是 调用构造函数。 operator new
returns.
后自动调用构造函数
因此,您的实施应如下所示:
void * operator new(size_t size) {
if (freelist == nullptr) {
// here is the difference:
return ::operator new(size);
}
Link * tmp = freelist;
freelist = freelist->next;
return tmp;
}
在您的实现中,构造函数是从 ::new Link
调用的,然后在返回指针后,在同一地址上像往常一样再次调用它。
当然,你应该实现互补operator delete
。这个不能调用析构函数(已经调用过),而只是将内存放入 freelist
.
我正在阅读 Shaffer 的《Data Structures and Algorithm Analysis in C++》(可在 https://people.cs.vt.edu/shaffer/Book/ 免费获取),因为我看到它使用了模板,我正在尝试学习更多关于 C++ 和 C++ 泛型编程的知识.
作者在介绍Linked Lists时,给出了一个使用freelist的变体。 (这是大约第 111 页,pdf 第 130 页)。在他的实现中,他为 Link
创建了一个 class 并覆盖了 operator new
以从空闲列表中拉出。然后有一个 class for LinkedList
.
然而,事实证明,在此实现中,调用 new LinkedList
将导致同一对象调用构造函数两次,我无法弄清楚原因。
此代码显示了示例的相关部分。
#include <cstddef>
#include <iostream>
struct S {
S() { std::cout << "ctor at " << this << "\n"; }
~S() { std::cout << "dtor at " << this << "\n"; }
};
class Link {
private:
static Link * freelist;
S elem;
Link * next;
public:
void * operator new(size_t) {
if (freelist == nullptr) { return ::new Link; }
Link * tmp = freelist;
freelist = freelist->next;
return tmp;
}
// other logic
};
Link * Link::freelist = nullptr;
class LinkedList {
private:
Link * head;
public:
LinkedList() { head = new Link; }
// other logic
};
int main() { LinkedList ll; }
此代码的示例输出是
ctor at 0x458bbd954eb0
ctor at 0x458bbd954eb0
所以我们可以看到结构 S
的构造函数被同一个对象调用了两次。
这是我所期望的。
- 在
main
中,我们构造一个LinkedList
. - 在这个 LinkedList 中,我们调用
new Link
。我相信这会调用Link::operator new
,我们已经覆盖了它。 - 在
Link::operator new
中,我们看到Link
发现它的freelist是空的,于是调用::new Link
. ::new Link
构造一个Link,包括它的私有成员变量S elem
,构造一个S
结构。
显然这是不正确的。我敢打赌我从编译器中遗漏了一个隐式 new
--- 但即便如此,我还是很惊讶单个 S
实例的构造函数被调用了两次。所以我的心智模型肯定是不完整的。
你能帮忙解释一下为什么构造函数在内存中的同一位置被调用两次吗?
operator new
的任务是分配内存;它必须 而不是 调用构造函数。 operator new
returns.
因此,您的实施应如下所示:
void * operator new(size_t size) {
if (freelist == nullptr) {
// here is the difference:
return ::operator new(size);
}
Link * tmp = freelist;
freelist = freelist->next;
return tmp;
}
在您的实现中,构造函数是从 ::new Link
调用的,然后在返回指针后,在同一地址上像往常一样再次调用它。
当然,你应该实现互补operator delete
。这个不能调用析构函数(已经调用过),而只是将内存放入 freelist
.