C++ 没有合适的默认构造函数
这是一个学习项目,所以请给任何额外的建议,出现在脑海中 我试图通过重新实现一些STL容器/算法来学习数据结构,我从链表开始。如果我试图列出一个缺少默认构造函数的类,我会得到一个编译器错误,即没有合适的默认构造函数:C++ 没有合适的默认构造函数,c++,stl,constructor,linked-list,doubly-linked-list,C++,Stl,Constructor,Linked List,Doubly Linked List,这是一个学习项目,所以请给任何额外的建议,出现在脑海中 我试图通过重新实现一些STL容器/算法来学习数据结构,我从链表开始。如果我试图列出一个缺少默认构造函数的类,我会得到一个编译器错误,即没有合适的默认构造函数: #include "list.h" #include <list> class testA { private: int mInt; public: testA(int i) : mInt(i) {} }; int _tmain(int argc, _
#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实现实际上使用了虚拟头节点的思想。从消极的一面看,他们似乎耍了一些卑鄙的伎俩来回避这个问题。我的VisualStudio2010STL实现最终会调用原始操作符new和delete,而不调用构造函数。我基本上复制了它的look for::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需要模板化值的返回类型。因此,我需要至少一个合适的构造函数来获取基类中的伪值。我不能使这个纯虚拟的,因为这样我的虚拟节点就不能被实例化为基类
这:
这个版本使用的是静态强制转换,看起来不那么糟糕,但我还没能将它应用到我的代码中
那么,在不调用raw操作符new/deletes的情况下,如何让代码正常工作呢?它会更干净吗?延迟/可选构造值的一个选项可以是:
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) {}
...
};
这些都是标准布局,按照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(); }
};
@VaughnCato:因为0是int类型,而不是T类型?@BenVoigt:啊,是的-我没有抓住要点。因为我不能保证我选择的任何类型都存在构造函数。我承认我不知道那里到底发生了什么。但我会调查的。谢谢。我只需将mNodeVal更改为指针,并为虚拟节点将其初始化为NULL。我不得不添加一些解引用,但我认为它是有效的。睡眠让一切变得容易多了!我会接受这个答案,因为使用union进行的初步测试似乎也不错。@DSM:这涉及到一些折衷:较差的局部性和增加的内存使用。我想我从您现有的代码中假设您知道指针选项。@DSM:请注意,您可以在列表本身中使用union技术来存储标题列表项。这与::运算符的新技术非常相似,因为它分配内存而不调用构造函数。但是,它将直接位于您的列表对象中,大小适当且对齐,这使一切变得更容易。我可能应该知道折衷,但我没有想到这两件事。不过这是一个很好的观点。我不知道如何用并集正确地破坏对象,所以我放弃了。也许我会再调查一下。谢谢你的帮助。
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(); }
};