为数据缺少默认构造函数的链表创建节点
Creating a Node for a Linked List where the Data Lacks a Default Constructor
我正在尝试实现一个表示双向链表的类,并且我有一个函数createNode()
它返回一个新的Node
(一个模板化类),其所有成员都已初始化。此函数将用于创建链表,其中大小已知,但没有数据传递给它。对于大多数数据类型,这都有效。但是,这不适用于没有默认构造函数的类,因为它们不能在没有参数的情况下初始化。下面是展示这一点的最小代码:
class Test // A class without a default constructor
{
public:
Test(int value) : value_{ value } { };
private:
int value_;
};
template<typename T>
struct Node
{
Node* prev;
Node* next;
T value;
};
template<typename T>
Node<T>* createNode()
{
return new Node<T>{ nullptr, nullptr, T() }; // How do I change T() so
// that I can use classes
// without default constructors?
}
int main()
{
Node<Test>* testNode = createNode<Test>();
delete testNode;
}
基本上,我的最终目标是能够创建一个链表,该链表可以保存未初始化的节点,同时跟踪哪些节点已初始化或未初始化。我记得在我的一本旧教科书中读到过一种解决这个问题的方法,该方法涉及使用分配器(用于处理物体的构造/破坏),但我根本不记得确切的技术。那么我应该怎么做呢?
如果您有权访问 C++17,请使用std::optional<T>
,如果您无法访问,请使用boost::optional<T>
。
template<typename T>
struct Node
{
Node* prev;
Node* next;
std::optional<T> value; // or boost::optional<T> value;
};
template<typename T>
Node<T>* createNode()
{
return new Node<T>{ nullptr, nullptr, std::nullopt /* or boost::none */ };
}
如果您无法访问 C++17 并且不想包含 boost,您可以使用以下内容滚动自己的optional
模板:
struct nullopt_t {};
nullopt_t nullopt;
template <typename T>
class optional
{
public:
template <typename... Args>
optional(Args&&... args)
: ptr{new ((void*)&storage) T(std::forward<Args>(args)...)}
{}
optional(nullopt_t)
: ptr{nullptr}
{}
~optional()
{
if (ptr) {
ptr->~T();
}
}
optional& operator=(T obj)
{
if (ptr) {
*ptr = std::move(obj);
} else {
ptr = new ((void*)&storage) T(std::move(obj));
}
return *this;
}
explicit operator bool()
{
return ptr != nullptr;
}
T& value()
{
if (!ptr) {
throw std::exception();
}
return *ptr;
}
// Other const-correct and rvalue-correct accessors left
// as an exercise to the reader
private:
std::aligned_storage_t<sizeof(T), alignof(T)> storage;
T* ptr;
};
现场演示
您可以使用放置 new 稍后将对象放置在预分配的内存中。
这只是将内存分配与对象的构造分开。因此,您可以在Node
中声明一个成员,该成员占用内存但不构造对象,因为它需要参数。稍后您可以使用所需的参数构造对象,但不使用new
分配内存,而是使用放置 new 来调用已在Node
中分配内存的构造函数。
所以下面是一个自制std::optional
的例子。在 n3527 中,您可以找到有关std::optional
的更多详细信息。
#include <vector>
#include <functional>
#include <iostream>
#include <algorithm>
#include <string>
#include <memory>
using namespace std;
class Test // A class without a default constructor
{
public:
Test(int value) : value_{ value } { };
//private:
int value_;
};
template<typename T>
struct Node
{
Node* prev;
Node* next;
bool empty = true;
union {
T t;
} value; // Could be replaced with typename std::aligned_storage<sizeof(T), alignof(T)>::type value;
// need a constructor that inits the value union and activate a field
// Node()
~Node() {
if (!empty) {
value.t.~T();
}
}
template<typename... Args>
void setValue(Args... args) {
if (!empty) {
value.t.~T();
}
new (&value.t) T(std::forward<Args...>(args...));
empty = false;
}
T& getValue() {
// TODO:
if (empty) {
//throw
}
return value.t;
}
};
template<typename T>
Node<T>* createNode()
{
return new Node<T>{ nullptr, nullptr }; // How do I change T() so
// that I can use classes
// without default constructors?
}
int main()
{
Node<Test>* testNode = createNode<Test>();
testNode->setValue(42);
if (!testNode->empty) {
std::cout << testNode->getValue().value_;
}
delete testNode;
return 0;
}
现场演示
只需少量小的更改和reinterpret_cas
,您还可以使用typename std::aligned_storage<sizeof(T), alignof(T)>::type value;
-现场演示
分配器管理内存,您将无法在类中包含(聚合)对象,并且必须使用指针和第二次分配,除非使用分配器放置整个Node
。
John Lakos在YouTube上有关于分配器的有趣演示 - CppCon 2017本地"竞技场"内存分配器第1部分和第2部分。
你问的实际上是不可能的——在没有默认构造函数的情况下默认构造一个对象。 也许考虑向createNode()
添加一个T nodeValue
参数?或者更改Node
本身,以便它不是保存对象,而是保存指向对象的指针。这似乎是一场内存管理的噩梦,但它可能会奏效。
- 使用std::list创建循环链表
- C++ 创建包含链表和字符串的对象的链接列表时出错
- 在C++中为链表类创建实例
- C++ 双向链表:使用矩阵类成员创建和填充列表
- 创建一个棋盘格或"Interweave"两个链接列表。IE 更改两个链表的指针
- 创建了一个链表,但如何删除 c++ 中的"所有"节点
- 在解决链表问题时创建一个额外的节点是一个好习惯吗?
- 无法创建或返回反向链表
- 在 C++ 中创建单个链表
- 如果我们不创建一个新节点并使用指针插入数据并建立链接(在链表中)怎么办?
- 程序在为链表创建推送函数时崩溃
- 自定义链表创建 RtlValidateHeap 错误,其结构具有链表
- 为数据缺少默认构造函数的链表创建节点
- 如何为链表C++创建"弹出尾巴"函数
- 如何为双链表创建交换函数
- 在链表创建循环中插入
- 正在为链表c++创建DataType
- 为链表C++创建节点
- 如何使用swap函数为双重链表创建冒泡排序
- 如何为链表创建复制构造函数