I am new to c++ and am trying to figure out how to display the contents of my stack after running it through a program.
int main(){
int userInput;
Stack st1,st2;
cin>>userInput;
int i,topStack;
while(userInput!=-9){
while(userInput>0){
st1.push(userInput);
topStack=st1.pop()
st2.push(topStack);
}
if(userInput<0){
for(i=0;i<(-userInput);++i){
st2.pop();
}
}
}
}
I want to see what st2 looks like after I input values. Not quite sure how to do that and/or if it is possible. Resources would be more than appreciated!
Here is the custom class definition:
class ListNode{
public:
int content;
ListNode* pNext;
};
class Stack{
private:
ListNode* top;
public:
Stack();
~Stack();
int pop();
void push(int);
};
Stack::Stack(){
top=nullptr;
}
void Stack::push(int x){
ListNode *newNode;
newNode=new ListNode;
newNode->content=x;
newNode->pNext=top;
top=newNode;
}
int Stack::pop(){
int fR=-17; //empty stack
if(top!=nullptr){
ListNode* newNode;
newNode=top;
top=top->pNext;
fR=newNode->content;
delete newNode;
}
return fR;
}
Stack::~Stack(){
while(top!=nullptr){
pop();
}
}
Here is a fairly minimal implementation of an iterator class to allow viewing the contents of a Stack object.
I've also made some changes in the existing code to be more in line with modern C++ best practices. In particular, you should hardly ever be using raw new and delete directly, so I've updated the code to use std::unique_ptr instead.
stack_iterator.hpp:
#include <iterator>
#include <memory>
#pragma once
class Stack {
private:
struct ListNode {
int content;
std::unique_ptr<ListNode> pNext;
};
public:
Stack() = default;
Stack(const Stack&) = delete;
Stack(Stack&&) = delete;
Stack& operator=(const Stack&) = delete;
Stack& operator=(Stack&&) = delete;
void push(int);
int pop();
class const_iterator {
public:
using difference_type = std::ptrdiff_t;
using value_type = const int;
using pointer = const int*;
using reference = const int&;
using iterator_category = std::forward_iterator_tag;
const_iterator() = delete;
const_iterator(const const_iterator&) = default;
const_iterator& operator=(const const_iterator&) = default;
// Constructor making a const_iterator pointing to a particular
// list node.
const_iterator(ListNode*);
// Pre-increment operator, e.g. called by ++i
const_iterator& operator++();
// Post-increment operator, e.g. called by i++
const_iterator operator++(int);
// Deference operator, e.g. called by *i
reference operator*() const;
// Comparison operators
bool operator==(const const_iterator&) const;
bool operator!=(const const_iterator&) const;
private:
ListNode* pNode;
friend class Stack;
};
// Accessors to create iterators to start and end of stack.
// Note that the stack is traversed from most recent to least
// recent entries. Also note that a pop() operation will invalidate
// any iterator referring to the most recent entry of the stack.
const_iterator begin() const;
const_iterator end() const;
private:
std::unique_ptr<ListNode> top;
};
stack_iterator.cpp:
#include "stack_iterator.hpp"
void Stack::push(int x) {
auto newNode = std::make_unique<ListNode>();
newNode->content = x;
newNode->pNext = std::move(top);
top = std::move(newNode);
}
int Stack::pop() {
static constexpr int retvalOnEmpty = -17;
auto oldTop = std::move(top);
if (oldTop != nullptr) {
top = std::move(oldTop->pNext);
return oldTop->content;
}
return retvalOnEmpty;
}
// pre-increment operator
Stack::const_iterator& Stack::const_iterator::operator++() {
pNode = pNode->pNext.get();
return *this;
}
// post-increment operator
Stack::const_iterator Stack::const_iterator::operator++(int) {
auto ret_iterator = *this;
++(*this);
return ret_iterator;
}
// dereference operator
Stack::const_iterator::reference Stack::const_iterator::operator*() const {
return pNode->content;
}
// comparison operators
bool Stack::const_iterator::operator==(const Stack::const_iterator& other) const {
return this->pNode == other.pNode;
}
bool Stack::const_iterator::operator!=(const Stack::const_iterator& other) const {
return this->pNode != other.pNode;
}
// semi-private constructor making an iterator pointing to a particular ListNode
Stack::const_iterator::const_iterator(ListNode* i_node) :
pNode(i_node)
{ }
Stack::const_iterator Stack::begin() const {
return Stack::const_iterator{top.get()};
}
Stack::const_iterator Stack::end() const {
return Stack::const_iterator{nullptr};
}
Example main program showing how to use the iterators:
#include "stack_iterator.hpp"
#include <iostream>
#include <algorithm>
int main() {
Stack s;
s.push(3);
s.push(5);
s.push(-4);
std::cout << "Contents of stack: ";
for (auto n : s)
std::cout << n << ' ';
std::cout << '\n';
// Alternative
std::cout << "Contents of stack, using ostream_iterator: ";
std::copy(s.begin(), s.end(), std::ostream_iterator<int>{std::cout, " "});
std::cout << '\n';
return 0;
}
Two approaches using the standard library's containers:
Give up your own implementation of a stack entirely, and using the one available as part of the standard library: std::stack. To display the elements, do something like:
void print(std::ostream& os, std::stack s, char delimiter = ' ')
{
if (not s.empty()) {
std::cout << s.top();
}
while (not s.empty()) {
std::cout << delimiter << s.top();
}
}
since you're passing the stack by value, you're really making a copy of it.
Use your own stack implementation, but implement it over an std::list, and expose the list's iterators (similar to Daniel Schepler's answer but without most of the hard work...).
Of course, it's better to implement an operator<< then a print function, but the principle is the same.
Related
Consider a standard implementation of class Link of LinkedList in c++.
I want to know if its a good idea to overload the operator ++ in this class (I noticed that I repeat the line link = link->next; a lot when dealing with linked lists, so I thought it would be easier if I overload this operator). Here's my implementation:
#ifndef LINK_H
#define LINK_H
#include <iostream>
#include "typeinfo.h"
#include "LinkedList.h"
template <class T> class LinkedList; //|Forward declaration of the generic LinkedList.
template <class T>
class Link
{
public:
//|-------------------- Constructors --------------------
Link(T data): m_data(data), next(NULL){}
//|-------------------- Methods --------------------
T getData(){
return m_data;
}
T& getNext(){
return next;
}
void setNext(Link* newLink){
next = newLink;
}
void setData(T data){
m_data = data;
}
//|-------------------- Operator overload --------------------
bool operator==(Link& other){
if(this->m_data == other.m_data)
return true;
return false;
}
void operator++(){ //Is this okay?
this = this->next;
}
//|-------------------- Friend functions --------------------
friend std::ostream& operator<<(std::ostream& out,const Link<T>& link){
out<<link.m_data;
return out;
}
//|-------------------- Destructor --------------------
virtual ~Link(){}
protected:
public:
//|-------------------- Private fields --------------------
T m_data;
Link<T>* next;
friend class LinkedList<T>;
};
#endif // LINK_H
I guess the way that I tried to do it is not good (it does work as I expected). I tried to use this because I want it to work on pointer that is pointing to a certain link.
So, is it a good idea? if it is, what is the right way to implement it?
Thanks.
Maybe you should refactor your design and the code.
The link, or better said the Node, is normally implemented as an own class. An this class is embedded in the LinkedList class. And that Node class should be completely encapsulated and not shown to the outside world.
The user of the class will just deal with the data value. All the pointer stuff should be hidden.
And to be able to iterate over your class, which is similar to a std::forward_list, you can add ultra simple iterator functionality. Most functions can be implemented using a one liner.
I will show you below an ultra simple implementation example. This may be extended easily according to your needs.
From that you may take over some ideas to improve your design.
With the added iterator functionality, you may use range based for loops and std:: algorithms and all the like.
Please have a look and ask questions, if there should be something unclear.
#include <iostream>
#include <iterator>
#include <initializer_list>
#include <algorithm>
// Very simple implementation of a forward list
template <class T>
class SinglyLinkedList {
// The node
struct Node {
T data{}; // Data. Would normally be a templated argument
Node* next{}; // And the pointer to the next node
Node(const T& i, Node* n = nullptr) : data(i), next(n) {}; // Simple constructor to set a value and next pointer
};
Node* head{}; // This is the start of the list
// It would be advisable to have a tail pointer. We use the more inefficient approach here
Node* getLast() const { Node* n{ head }; while (n and n->next) n = n->next; return n; }
public:
// Constructor / Destructor --------------------------------------------------------------------------------------------------------
~SinglyLinkedList() { clear(); }
// Default constuctor
SinglyLinkedList() {} // Default
// From an initialization list
SinglyLinkedList(const std::initializer_list<T>& il) { clear(); for (const T& i : il) push_back(i); } // From initializer list
// Copy constructor
SinglyLinkedList(const SinglyLinkedList& other) { clear(); for (const T &i : other) push_back(i); }
// Move constructor. Will steal the elements from the other
SinglyLinkedList(SinglyLinkedList&& other) noexcept { head = other.head; other.head = nullptr; }
// Assignment operator
SinglyLinkedList& operator = (const SinglyLinkedList& other) { clear(); for (const T &i : other) push_back(i); }
// Move assignment operator
SinglyLinkedList& operator = (SinglyLinkedList&& other) { head = other.head; other.head = nullptr; }
// Housekeeping --------------------------------------------------------------------------------------------------------------
void clear() { Node* tmp{ head }; while (tmp) { Node* toDelete{ tmp }; tmp = tmp->next; delete toDelete; } head = nullptr; }
int empty() const { return head == nullptr; }
int size() const { int k{}; Node* n{ head }; while (n) { ++k; n = n->next; } return k; }
// Modify content --------------------------------------------------------------------------------------------------------------
void push_front(const T& i) { Node* n = new Node(i); n->next = head; head = n; };
void push_back(const T& i) { Node* n = new Node(i); Node* l = getLast(); if (l) l->next = n; else head = n; }
void pop_front() { if (head) { Node* tmp = head->next; delete head; head = tmp; } }
void pop_back() { // This is a little bit more difficult in a singly linked list
if (head) {
Node* n{ head }, * previous{};
while (n and n->next) {
previous = n;
n = n->next;
}
delete n;
if (previous)
previous->next = nullptr;
else
head->next = nullptr;
}
}
// Access elements --------------------------------------------------------------------------------
T front() const { return head ? head->data : 0; };
T back() const { Node* n = getLast(); return n ? n->data : 0; }
// Add iterator properties to class ---------------------------------------------------------------
struct iterator { // Local class for iterator
Node* iter{}; // Iterator is basically a pointer to the node
// Define alias names necessary for the iterator functionality
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
// Constructor
iterator() {}
iterator(Node* n) : iter(n) {}
// Dereferencing
reference operator *() const { return iter->data; }
pointer operator ->() const { return &iter->data; }
// Aithmetic operations
iterator& operator ++() { if (iter) iter = iter->next; return *this; }
iterator operator ++(int) { iterator temp{ *this }; ++* this; return temp; }
iterator operator +(const difference_type& n) const { iterator temp{ *this }; difference_type k{ n }; while (k--)++temp; return temp; }
iterator& operator +=(const difference_type& n) { difference_type k{ n }; while (k--)++* this; return *this; };
// Comparison
bool operator != (const iterator& other) const { return iter != other.iter; }
bool operator == (const iterator& other) const { return iter == other.iter; }
bool operator < (const iterator& other) const { return iter < other.iter; }
bool operator > (const iterator& other) const { return iter > other.iter; }
bool operator <= (const iterator& other) const { return iter <= other.iter; }
bool operator >= (const iterator& other) const { return iter >= other.iter; }
// Difference. Also complicated, because no random access
difference_type operator-(const iterator& other) const {
difference_type result{};
Node* n{ iter };
while (n and n != other.iter) {
++result;
n = n->next;
}
return result;
}
};
// Begin and end function to initialize an iterator
iterator begin() const { return iterator(head); }
iterator end() const { return iterator(); }
// Functions typcical for forward lists ----------------------------------------------------------------------
// Easy, becuase we can operate form the current iterator and do not need the "previous" element
iterator insertAfter(iterator& pos, const T& i) {
iterator result{};
if (pos.iter and pos.iter->next) {
Node* n = new Node(i, pos.iter->next);
pos.iter->next = n;
result = n;
}
return result;
}
iterator eraseAfter(iterator& pos) {
iterator result{};
if (pos.iter and pos.iter->next) {
Node* tmp = pos.iter->next->next;
delete pos.iter->next;
pos.iter->next = tmp;
result = pos.iter->next;
}
return result;
}
};
// Test/Driver Code
int main() {
// Example for initilizer list
SinglyLinkedList<int> sllbase{ 5,6,7,8,9,10,11,12,13,14,15 };
// Show move constructor
SinglyLinkedList<int> sll(std::move(sllbase));
// Add some values in the front
sll.push_front(4);
sll.push_front(3);
sll.push_front(2);
sll.push_front(1);
// Delete 1st element (Number 1)
sll.pop_front();
// Delete last element
sll.pop_back();
// Use a std::algorithm on our custom linked list. Works because we have an interator
SinglyLinkedList<int>::iterator iter = std::find(sll.begin(), sll.end(), 8);
// Now add an element after 8
iter = sll.insertAfter(iter, 88);
// End delete the 9
iter = sll.eraseAfter(iter);
// Use range based for loop. Works because, we have iterators
for (int i : sll)
std::cout << i << ' ';
}
Many posts about const iterators (example), but none in the context of loops like:
for (const auto it: container) { ... }
When I started implementing, I was encouraged by the compiler's complaints about missing begin and end, hoping such complaints can guide me to fill in the missing pieces. I was wrong. The following code compiles fine though it surely lacks the equivalent of both operator++ and operator!=:
#include <iostream>
template<class T> class List { public:
const T *head;
const List<T> *tail;
List(const T *h, const List<T> *t):head(h),tail(t){}
const T *operator*() const { return head; } };
template<class T> const List<T> *begin(const List<T> *l) { return l; }
template<class T> const List<T> *end (const List<T> *l) { return nullptr; }
class Person { public: int age; Person(int a):age(a){} };
typedef List<Person> Persons;
int main(int argc, char **argv) {
Person *p1 = new Person(16);
Person *p2 = new Person(27);
Person *p3 = new Person(38);
Persons *persons = new Persons(p1,
new Persons(p2,
new Persons(p3, nullptr)));
for (const auto p: persons) { std::cout << (*p)->age << "\n"; }
return 0; }
How come this code compiles?
You have not defined ++ and !=, but they are perfectly well-defined for your iterator type, const List<T>*, because is a pointer type. However, the default behavior of ++ does not do what you want, which would be to follow the tail pointer.
To imbue your iterator with special knowledge of how ++ should be implemented, you would want to use a separate, custom iterator class, rather than using a pointer.
I also made some other changes to your code:
The List type is made iterable, not List*. That means the begin and end functions are not defined on pointers, but on the List object itself, and we iterate over *persons rather than persons.
In for (const auto p : *persons), p is a Person, not a pointer.
godbolt.org link
#include <iostream>
template<class T> class List { public:
const T *head;
const List<T> *tail;
List(const T *h, const List<T> *t):head(h),tail(t){}
const T *operator*() const { return head; }
class const_iterator {
const List<T>* cur = nullptr;
public:
explicit const_iterator(const List<T>* list) : cur(list) {}
const_iterator& operator++() {
cur = cur->tail;
return *this;
}
bool operator!=(const const_iterator& other) const {
return cur != other.cur;
}
const T& operator*() const {
return *cur->head;
}
};
const_iterator begin() const {
return const_iterator(this);
}
const_iterator end() const {
return const_iterator(nullptr);
}
};
class Person { public: int age; Person(int a):age(a){} };
typedef List<Person> Persons;
int main(int argc, char **argv) {
Person *p1 = new Person(16);
Person *p2 = new Person(27);
Person *p3 = new Person(38);
Persons *persons = new Persons(p1,
new Persons(p2,
new Persons(p3, nullptr)));
for (const auto p: *persons) {
std::cout << p.age << "\n";
}
return 0;
}
I'm trying to create a class template for a link-list implementation of a Stack. Now I've gotten the push, pop, peek, and tested the destructor. But I'm wondering how should I add the copy constructor, overloaded assignment operator, and the deepCopy on my code. Here is what I got so far:
// Lab3.cpp
//
// Created by IvanChak on 4/3/16.
// Copyright © 2016 Space. All rights reserved.
#include <iostream>
using namespace std;
template<class T>
struct Node {
T item;
Node* next = NULL;
Node(T t = {}, Node* link = nullptr) :item{t}, next{link} { }
~Node() { delete next; }
};
template<class T>
class Stack {
public:
bool empty() const { return n == 0; }
void push(const T&);
T pop();
T peek();
~Stack();
private:
Node<T>* top = NULL;
size_t n;
};
template <class T>
class A
{
public:
A(const A &){}
A & operator=(const A& a){return *this;}
};
template<class T>
Stack<T>::~Stack() {
cout<<"Destructor, deallocate..."<<endl;
}
template<class T>
void Stack<T>::push(const T& t) {
Node<T>* previous{top};
top = new Node<T>{t,previous};
++n;
}
template<class T>
T Stack<T>::pop() {
if (empty()) {
cout << "Empty" << endl;
}
Node<T>* oldnode = top;
T t = top->item;
top = top->next;
--n;
delete oldnode;
return t;
}
template<class T>
T Stack<T>::peek() {
if (empty()) {
cout << "Empty" << endl;
}
return top->item;
}
int main(int argc, const char * argv[]) {
Stack<string> x{};
x.push("Hello");
x.push("Second");
x.push("Bye");
cout << x.peek() << endl;
x.pop();
cout << x.peek() << endl;
}
I don't know if you still need an answer or not, but if you do, you would want something like this for you copy constructor and your assignment operator:
template<class T>
Stack<T>::Stack(const Stack<T>& rhs)
{
if (rhs.top)
{
this->top = new Node<T>(rhs.top->item);
Node<T>* rhsNode = rhs.top; // This pointer is used for iterating through the "rhs" argument.
Node<T>* currentNode = this->top; // This pointer is used for iterating through "this".
n = 1;
while (rhsNode->next) // Loop untill the end of the stack has been reached.
{
++n;
currentNode->next = new Node<T>(rhsNode->next->item);
currentNode = currentNode->next;
rhsNode = rhsNode->next;
}
}
else // "rhs" is empty
{
n = 0;
this->top = nullptr;
}
}
Note: I wrote this code in a "deep copy" fashion. I can't think of any situation where it would be good to do a "shallow copy" on this type of data structure; in fact, I think it would be a very bad idea to do so. I don't automatically assume that you are planning on making your copy constructor do a "shallow copy" on your stack, but since you named the copy constructor and something about "deep copying" separately, I see it as entirely possible that you are.
I tested this code, and it worked.
Im trying to write an efficient list and node classes that have minimal code to iterate over them. However I'm having difficulty fulfilling all my criteria. I want this code to be used safely in a multi-threaded environment and so if possible I want all the functions and arguments for iteration to be const to ensure no state is being written to ie its all read only. So I wrote the below classes and iteration code:
template<typename _Type_>
class Node
{
template<typename _Type_> friend class List;
public:
Node() : m_Next(NULL) {}
Node(const _Type_& value) : m_Value(value), m_Next(nullptr()) {}
const _Type_& Get() const { return m_Value; }
Node&& Next() const { return std::move(*m_Next); }
operator _Type_ () const { return m_Value; }
_Type_& operator->() { return m_Value; }
const _Type_& operator->() const { return m_Value; }
operator bool() const { return m_Next != nullptr(); }
private:
_Type_ m_Value;
Node* m_Next;
};
template<typename _Type_>
class List
{
public:
typedef Node<_Type_> Node;
List() : m_Head(NULL) {}
void AddHead( const _Type_& value )
{
Node* pNode = GE_NEW(Node, value);
pNode->m_Next = &m_Head;
m_Head = *pNode;
}
Node&& Head() const { return std::move(m_Head); }
private:
Node m_Head;
};
and the all important iteration code:
RenderTargetList::Node&& renderTarget = m_Targets.Head();
while( renderTarget )
{
if(renderTarget->Format() == format)
{
return renderTarget.Get();
}
renderTarget = renderTarget.Next();
}
But this doesn't compile as:
Node&& Head() const { return std::move(m_Head); }
returns a non const rvalue reference in a const function function ie it has to be:
const Node&& Head() const { return std::move(m_Head); }
instead, but then this doesn't work as the iteration code fails on the assignment:
renderTarget = renderTarget.Next();
because renderTarget must now be defined as:
const RenderTargetList::Node&& renderTarget = m_Targets.Head();
because head returns a const. Basically I seem to be in a right mess of const, references, lvalues and rvalues. Someone please help!
Thanks
Here's the basic version of a list class. Note that the iterator type and the node type are two distinct types. This way, the node type can own the value, and the iterator type can have pointer semantics.
I'll post this as a community wiki, as it's rather a comment than a direct answer to the question.
template<typename Type>
class List
{
private:
// note that for immutable nodes, we could store `Type const`
// and for an immutable list, `Node const* const`
struct Node
{
Type m_Value;
Node* m_pNext;
};
Node* m_pHead;
public:
class const_iterator
{
private:
Node const* m_pNode;
friend List;
const_iterator(Node* p_pNode) : m_pNode(p_pNode) {}
public:
const_iterator() : m_pNode(nullptr) {}
explicit operator bool() const
{ return m_pNode; }
const_iterator Next() const
{ return {m_pNode->m_pNext}; }
Type const& Get() const
{ return m_pNode->m_Value; }
friend bool operator!=(const_iterator const& lhs,
const_iterator const& rhs)
{
return lhs.m_pNode != rhs.m_pNode;
}
};
List() : m_pHead(nullptr) {}
~List()
{
// delete nodes
}
List(List const&) = delete; // needs to be customized
List& operator=(List const&) = delete; // this one, too
// the only function that modifies the list:
void AddHead( Type const& value )
{
Node* pNode = new Node{value, m_pHead};
m_pHead = pNode;
}
const_iterator Head() const
{ return {m_pHead}; }
};
Usage example:
#include <iostream>
int main()
{
List<int> l;
for(int i = 0; i < 10; ++i) l.AddHead(i);
auto it = l.Head();
while(it)
{
std::cout << it.Get() << ", ";
it = it.Next();
}
}
I think you are confused at to the point of const if your goal is thread safety -- const does not give you thread safe code, only considerate and well understood access patterns will give you thread safe code.
I have lost of code which is thread sfae which does not make use of const, and you will need to as well if you want to be able to keep the "AddHead" method which certainly in its current form is not thread safe.
If your code is threaded you will need to add mutex locks around the code where you are accessing shared data which could be modified or read by another thread -- the critical zones -- from your code structure these critical zones could be very short lived, so not much of a penalty.
This is a learning project so please give any additional advice that comes to mind.
I am trying to learn data structures by re-implementing some STL containers/algorithms and I've started with linked lists. If I try to make a list of a class lacking a default constructor I would get a compiler error of "no appropriate default constructor":
#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;
}
The problem is I'm using a dummy node in the list to store a link to the beginning and the end of the list. I use this mainly to get the .end() function to work properly and give a decrement-able iterator to one past the end of the list. When I declare an empty list one of the functions wants the default constructor for the templated type so it can make my dummy node! If no default constructor exists, it gives the error message.
On the positive side, it looks like a couple of STL implementations actually use the dummy head node idea. On the negative side, it looks like they pull some nasty tricks to get around this issue. My visual studio 2010 STL implementation eventually calls raw operator new and delete without calling the constructor. I've basically copied what it has (look for ::operator new and delete) and I get it to compile. Here is the full code:
#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;
}
};
};
Surely, there must be a cleaner way to get this to work. I can't seem to split my ListNode into a base class of just previous and next pointers and a derived class of the contained data because GetNodeVal() needs a return type of the templated value. So again I would need at least an appropriate constructor to get a dummy value in the base class. I could not make this pure virtual because then my dummy node can not be instantiated as a base class.
This:
http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-3.3/stl__list_8h-source.html
version is using static casts which seem to be less nasty but I haven't been able to apply it to my code.
So, how can I get this code to work without calling raw operator new/deletes? And will it be any cleaner?
One option for delayed/optional construction of the value could be:
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) {}
...
};
You will need to explicitly call its destructor, however, since the compiler no longer knows which of the variant members is active.
But it's better to do this for the dummy ListNode inside the List. I guess that the implementation with ::operator new was also special-casing the dummy. Something like this:
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;
};
These are both "standard-layout", and by 9.2:
If a standard-layout union contains two or more standard-layout structs that share a common initial sequence, and if the standard-layout union object currently contains one of these standard-layout structs, it is permitted to inspect the common initial part of any of them.
Let's take advantage of that now:
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(); }
};