这是一个学习项目,所以请提供任何额外的建议。
我试图通过重新实现一些STL容器/算法来学习数据结构,并且我已经开始使用链表。如果我尝试列出缺少默认构造函数的类,我会得到编译器错误“没有合适的默认构造函数”:
#include "list.h"
#include <list>
class testA
{
private:
int mInt;
public:
testA(int i) : mInt(i) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
std::list<testA> wS; // Fine
wS.push_back(1); // Fine
MTL::list<testA> wM; // 'testA' has no appropriate default constructor
wM.push_back(1);
return 0;
}
问题是我在列表中使用虚拟节点来存储指向列表开头和结尾的链接。我主要使用它来使.end()函数正常工作,并将一个可递减的迭代器提供给列表末尾的迭代器。当我声明一个空列表时,其中一个函数需要模板化类型的默认构造函数,以便它可以创建我的虚拟节点!如果不存在默认构造函数,则会给出错误消息。
从积极的方面来看,看起来有几个STL实现实际上使用了虚拟头节点的想法。从消极方面来看,看起来他们会采取一些令人讨厌的技巧来解决这个问题。我的visual studio 2010 STL实现最终调用raw operator new并删除而不调用构造函数。我基本上复制了它的内容(查找:: operator new和delete),然后我就可以编译了。这是完整的代码:
#include <cassert>
#include <iostream>
namespace MTL
{
template< typename T >
class list
{
private:
// If ListNode is in the private part of list, clients
// can't mess around with ListNodes.
template <typename T>
class ListNode
{
private:
ListNode<T>* p_mNextNode;
ListNode<T>* p_mPreviousNode;
T mNodeVal;
public:
// ListNode() :
// p_mNextNode(0),
// p_mPreviousNode(0) {}
ListNode(T const & aVal) :
p_mNextNode(0),
p_mPreviousNode(0),
mNodeVal(aVal) {}
ListNode<T>* GetNextNode() {return p_mNextNode;}
ListNode<T>* GetPreviousNode() {return p_mPreviousNode;}
void SetNextNode(ListNode<T>* const aNode) {p_mNextNode = aNode;}
void SetPreviousNode(ListNode<T>* const aNode) {p_mPreviousNode = aNode;}
T& GetNodeVal() {return mNodeVal;}
};
public:
class iterator
{
private:
ListNode<T>* mIteratorNode;
public:
iterator() : mIteratorNode(0) {}
iterator(ListNode<T>* aListNode) {mIteratorNode = aListNode;}
T& operator*() {return mIteratorNode->GetNodeVal();}
iterator& operator++() {mIteratorNode = mIteratorNode->GetNextNode(); return *this;}
iterator& operator--() {mIteratorNode = mIteratorNode->GetPreviousNode(); return *this;}
bool operator==(iterator const& aIterator) {return mIteratorNode==aIterator.mIteratorNode;}
bool operator!=(iterator const& aIterator) {return !(mIteratorNode==aIterator.mIteratorNode);}
ListNode<T>* GetNode() {return mIteratorNode;}
ListNode<T>* GetNextNode() {return mIteratorNode->GetNextNode();}
ListNode<T>* GetPreviousNode() {return mIteratorNode->GetPreviousNode();}
};
private:
ListNode<T>* p_mHeadNode;
void insert(ListNode<T>* const aNewNode, iterator& aIterator)
{
ListNode<T>* currentNode = aIterator.GetNode();
ListNode<T>* currentsPreviousNode = currentNode->GetPreviousNode();
currentsPreviousNode->SetNextNode(aNewNode);
aNewNode->SetPreviousNode(currentsPreviousNode);
aNewNode->SetNextNode(currentNode);
currentNode->SetPreviousNode(aNewNode);
}
void eraseNode(ListNode<T>* aListNode)
{
ListNode<T>* previousNode = aListNode->GetPreviousNode();
ListNode<T>* nextNode = aListNode->GetNextNode();
previousNode->SetNextNode(aListNode->GetNextNode());
nextNode->SetPreviousNode(aListNode->GetPreviousNode());
if (p_mHeadNode != aListNode)
{
delete aListNode;
}
}
protected:
public:
list() : p_mHeadNode(static_cast<ListNode<T>*>(::operator new (sizeof(ListNode<T>))))
{
// To get .begin or .end to work immediately after construction
p_mHeadNode->SetNextNode(p_mHeadNode);
p_mHeadNode->SetPreviousNode(p_mHeadNode);
}
list(list const& aList) : p_mHeadNode(static_cast<ListNode<T>*>(::operator new (sizeof(ListNode<T>))))
{
p_mHeadNode->SetNextNode(p_mHeadNode);
p_mHeadNode->SetPreviousNode(p_mHeadNode);
ListNode<T>* pCurrent = (aList.p_mHeadNode)->GetNextNode();
while (pCurrent != aList.p_mHeadNode)
{
this->push_back(pCurrent);
pCurrent = pCurrent->GetNextNode();
}
}
void push_front(T const& aNewVal)
{
ListNode<T>* newNode = new ListNode<T>(aNewVal);
this->insert(newNode,this->begin());
}
void push_back(T const& aNewVal)
{
ListNode<T>* newNode = new ListNode<T>(aNewVal);
this->insert(newNode,this->end());
}
void push_back(ListNode<T>* const aListNode)
{
this->push_back(aListNode->GetNodeVal());
}
void push_front(ListNode<T>* const aListNode)
{
this->push_front(aListNode->GetNodeVal());
}
T& front(){ return p_mHeadNode->GetNextNode()->GetNodeVal(); }
T& back(){ return p_mHeadNode->GetPreviousNode()->GetNodeVal(); }
void pop_front() {this->eraseNode(p_mHeadNode->GetNextNode());}
void pop_back() {this->eraseNode(p_mHeadNode->GetPreviousNode());}
const T& front() const { return (p_mHeadNode->GetNextNode())->GetNodeVal(); }
const T& back() const { return (p_mHeadNode->GetPreviousNode())->GetNodeVal(); }
iterator begin() {return iterator(p_mHeadNode->GetNextNode());}
iterator end() {return iterator(p_mHeadNode);}
iterator insert(iterator aPosition, const T& aVal)
{
assert(0);
return iterator();
}
iterator insert(iterator aPosition, unsigned int n, const T& aVal)
{
ListNode<T>* newNode = 0;
for (unsigned int i = 0; i < n; ++i)
{
newNode = new ListNode<T>(aVal);
this->insert(newNode,aPosition);
++aPosition;
}
return iterator(newNode->GetNextNode());
}
iterator insert(iterator aPosition, iterator aFirst, iterator aLast)
{
assert(0);
return iterator();
}
unsigned int size()
{
unsigned int counter = 0;
ListNode<T>* pCurrent = p_mHeadNode->GetNextNode();
while (pCurrent != p_mHeadNode)
{
++counter;
pCurrent = pCurrent->GetNextNode();
}
return counter;
}
~list()
{
this->clear();
::operator delete(p_mHeadNode);
}
void clear()
{
ListNode<T>* pCurrent = p_mHeadNode->GetNextNode();
ListNode<T>* pNext = pCurrent->GetNextNode();
while (pNext != p_mHeadNode)
{
this->eraseNode(pCurrent);
pCurrent = pNext;
pNext = pCurrent->GetNextNode();
}
// All but the last has been deleted
this->eraseNode(pCurrent);
}
bool empty() {return (p_mHeadNode->GetNextNode() != p_mHeadNode);}
friend std::ostream& operator<<(std::ostream& os, list<T> const& aList)
{
ListNode<T>* pCurrent = (aList.p_mHeadNode)->GetNextNode();
std::cout << "List Contents are:\n";
std::cout << "{";
while (pCurrent != aList.p_mHeadNode)
{
std::cout << pCurrent->GetNodeVal();
pCurrent = pCurrent->GetNextNode();
if (pCurrent != aList.p_mHeadNode)
{
std::cout << ",";
}
}
std::cout << "}\n";
return os;
}
};
};
当然,必须有一种更清洁的方法来实现这一目标。我似乎无法将ListNode拆分为仅包含前一个和下一个指针的基类以及包含数据的派生类,因为GetNodeVal()需要模板化值的返回类型。所以我再次需要至少一个适当的构造函数来获取基类中的虚拟值。我无法创建这个纯虚拟虚拟因为我的虚拟节点无法实例化为基类。
此: http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-3.3/stl__list_8h-source.html 版本使用静态强制转换,似乎不那么讨厌,但我无法将其应用于我的代码。
那么,如何在不调用raw operator new / deletes的情况下使这段代码工作?它会更干净吗?
答案 0 :(得分:0)
延迟/可选构造值的一个选项可能是:
template <typename T>
class ListNode
{
private:
ListNode<T>* p_mNextNode;
ListNode<T>* p_mPreviousNode;
union {
char dummy;
T mNodeVal;
};
ListNode() :
p_mNextNode(0),
p_mPreviousNode(0),
dummy() {}
ListNode(T const & aVal) :
p_mNextNode(0),
p_mPreviousNode(0),
mNodeVal(aVal) {}
...
};
但是,您需要显式调用其析构函数,因为编译器不再知道哪个变体成员处于活动状态。
但对ListNode
内的虚拟List
执行此操作会更好。我想使用::operator new
的实现也是特殊的 - 包含假人。像这样:
template< typename T >
class List
{
private:
class ListNode
{
private:
ListNode* p_mNextNode;
ListNode* p_mPreviousNode;
T mNodeVal;
};
class DummyListNode
{
public:
ListNode* p_mNextNode;
ListNode* p_mPreviousNode;
};
这些都是&#34;标准布局&#34;和9.2:
如果标准布局联合包含两个或多个共享公共初始序列的标准布局结构,并且标准布局联合对象当前包含这些标准布局结构中的一个,则允许检查公共初始其中任何一部分。
现在让我们利用它:
union {
DummyListNode dummy_head_tail;
ListNode head_tail
};
// ... iterators and stuff, using &head_tail as the first and last ListNode*
public:
List() : dummy_head_tail{ nullptr, nullptr } { }
~List() { /* free all nodes, then */ dummy_head_tail->~DummyListNode(); }
};