为什么在这个链表的实现中,这个 C++ 构造函数在相同的内存位置被调用两次?

dav*_*uda 7 c++ constructor new-operator

我正在阅读 Shaffer 的 Data Structures and Algorithm Analysis in C++(可在https://people.cs.vt.edu/shaffer/Book/免费获得),因为我看到它使用模板,我正在尝试了解更多关于C++ 和 C++ 中的泛型编程。

当作者介绍链表时,他给出了一个使用自由列表的变体。(这是大约第 111 页,pdf 第 130 页)。在他的实现中,他为 a 创建了一个类Link并覆盖operator new以从空闲列表中提取。然后有一个班级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; }
Run Code Online (Sandbox Code Playgroud)

此代码的示例输出是

ctor at 0x458bbd954eb0
ctor at 0x458bbd954eb0
Run Code Online (Sandbox Code Playgroud)

所以我们可以看到结构的构造函数S被同一个对象调用了两次。

这是我所期望的。

  1. 在 中main,我们构造了一个LinkedList.
  2. 在这个 LinkedList 中,我们调用new Link. 我相信这会调用Link::operator new,我们已经覆盖了它。
  3. 在 中Link::operator new,我们看到Link看到其空闲列表为空,因此调用::new Link
  4. ::new Link构造一个Link,包括它的私有成员变量S elem,构造一个Sstruct。

显然这是不正确的。我敢打赌,我错过new了编译器中的一个隐式--- 但即便如此,我还是很惊讶单个S实例的构造函数被调用了两次。所以我的心智模型肯定是不完整的。

你能帮助解释为什么构造函数在内存中的同一位置被调用两次吗?

j6t*_*j6t 6

operator new的任务是分配内存;它不能调用构造函数。operator new返回后自动调用构造函数。

因此,您的实现应如下所示:

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;
}
Run Code Online (Sandbox Code Playgroud)

在您的实现中,构造函数是从 调用的::new Link,然后在返回指针后,它会像往常一样在同一地址上再次调用。

当然,您应该实现互补的operator delete. 这个不能调用析构函数(已经被调用了),而只是将内存放入freelist.