I am trying to implement set as dynamic vector, but I don't know how to increment my iterator.
My code:
#pragma once
class node
{
public:
elem e;
node(){}
node(elem e):e(e){};
node(const node<elem>& n)
{
this->e=n->e;
}
};
class iteratorr
{
public:
node<elem>* current;
iteratorr():current(NULL){};
iteratorr(Node<elem>* c):current(c){};
iteratorr(const Iterator<elem>& it)
{
this->current=it.current;
}
iteratorr<elem>& operator++()
{
}
elem elem(){return current->e;}
};
Both classes are template typename elem. For some reason I can't write it in the code.
If I would add a pointer to a next node in class node, that would become a linked list I guess. How could I increment my iterator?
Related
Working on a tree class with an STL-like interface, I encountered a problem using non default-constructible elements:
As I'm implementing iterators, I need a past-the-last element at any point in time. My approach is to create one in the constructor, which asserts the value type to be default constructible.
Is there an approach to get rid of this constraint?
Pointing past the end can't possibly be nullptr if your iterator is bidirectional, end()-- needs to be legal.
It can be instead implemented by a sentinel, and in this case, the sentinel shouldn't even contain a default constructed element.
This can be done as such
struct link
{
link *parent, *left, *right;
};
template<typename T>
struct node : link
{
T data;
};
template<typename T>
struct tree : link
{
// tree itself serves as the sentinel
// At initialization parent and childs should all point to the sentinel
tree() : parent(this), left(this), right(this) {}
// ...
};
And iterators need no special handling for one past the end case.
// nested within tree
struct iterator
{
explicit iterator(link* l) : n(l) {}
iterator& operator--() { n = n->parent; return *this; } // or something else
auto& operator*() { return reinterpret_cast<node<T>*>(n)->data; }
// ...
link* n;
};
iterator begin() { return {left}; } // or something else
iterator end() { return {this}; }
I solved the problem by storing another pointer to the root node, from which I can recreate the last element in constant time.
Is it possible to create a circular doubly-linked list using smart pointers in C++
struct Node {
int val;
shared_ptr<Node> next;
weak_ptr prev;
};
shared_ptr<Node> head;
But this will have a circular reference of shared pointers and thus not deallocate correctly.
Make the circular linked list a class itself (with whatever operations you need to build it, like append). Have its destructor break the link by setting tail->next = nullptr. It should not matter which link you break, so if you're not using a head and tail, just set any one of them nullptr, and you're good.
In my testing, I made a circular linked list, and the nodes did not destruct. Then at the end, I added tail->next = nullptr before it exited, and all the destructors fired correctly.
My original posted answer was rather light on details. This one gives a proper explanation of how you can achieve a circular linked list without a memory leak and still adhere to the Rule of Zero. The answer is basically the same, using a sentinel, but the mechanism is a little more involved than I had originally let on.
The trick is to use a sentinel type that behaves just like a list node, but in fact does not really have a shared pointer to the head of the list. To achieve this, the node class should be separated into a behavior object and a state object.
class NodeState {
std::shared_ptr<Node> next_;
std::weak_ptr<Node> prev_;
int value_;
NodeState (int v) : value_(v) {}
NodeState (std::shared_ptr<Node> p) : next_(p), prev_(p) {}
//...
};
class Node {
virtual ~Node () = default;
virtual NodeState & state () = 0;
std::shared_ptr<Node> & next () { return state().next_; }
std::weak_ptr<Node> & prev () { return state().prev_; }
int & value () { return state().value_; }
void insert (const std::shared_ptr<Node> &p) {
//...
}
};
Now, you can define a node implementation and a sentinel implementation.
class NodeImplementation : public Node {
NodeState state_;
NodeState & state () { return state_; }
NodeImplementation (int v) : state_(v) {}
//...
};
class NodeSentinel : public Node {
List &list_;
NodeSentinel (List &l) : list_(l) {}
NodeState & state () { return list_.sentinel_state_; }
};
The list itself contains a NodeState used by the sentinel object. Upon initialization, the list creates a sentinel object and initializes its state.
class List {
//...
NodeState sentinel_state_;
std::shared_ptr<Node> head () { return sentinel_state_.next_; }
std::shared_ptr<Node> sentinel () {
return std::shared_ptr<Node>(head()->prev());
}
//...
public:
List () : sentinel_state_(std::make_shared<NodeSentinel>(*this)) {}
//...
void push_front (int value) {
head()->insert(std::make_shared<NodeImplementation>(value));
}
void push_back (int value) {
sentinel()->insert(std::make_shared<NodeImplementation>(value));
}
//...
};
So, what does this organization do? It avoids the issue of a circular reference by using a sentinel node to act as the break. While the tail of the list points to the sentinel object, the sentinel object itself does not point to anything. Instead, it uses the state of the list itself to determine its next and previous neighbors.
Thus, the circular shared pointers only persists as long as the list exists. Once the list is destroyed, Item A loses its reference, and via the domino effect, Sentinel itself will be destroyed.
A fundamental point is that the sentinel object itself must never be exposed to the user of the list interface directly. It should remain internal to the list object at all times. It essentially represents end() in an STL like container, and logically, it can never be removed from the list (until the list itself is destroyed). In practice, this means removal operations on the list need to exit early if the passed in iterator represents the sentinel.
Demo
Try It Online
It is also possible to define a member function next() which can select between a shared or weak pointer.
#include <iostream>
#include <memory>
using namespace std;
struct T {
int n_;
shared_ptr<T> next_;
weak_ptr<T> weaknext_;
T(shared_ptr<T> next, int n) : next_(next), n_(n) {};
auto next() {
if (next_ == nullptr)
return shared_ptr<T>(weaknext_);
return next_;
}
~T() { cout << n_ << "ok\n"; }
};
int main() {
auto p0 = make_shared<T>(nullptr, 1);
auto p1 = make_shared<T>(p0, 2);
auto p2 = make_shared<T>(p1, 3);
p0->weaknext_ = p2; //makes the list circular
auto p = p2;
for (int i = 0; i < 5; ++i) {
cout << p->n_ << "\n";
p = p->next();
}
}
I'm trying to give my generic list class a reverse function. For some reason, my algorithm ain't workin' when I test it. I thought it made sense: swap the pointers to the first and last nodes of the list, then go through the list and for each node swap its pointers to the previous and next node.
Go easy on me, guys. I'm trying to get some practice with generic programming. Teach me the ways of a C++ purist.
Here's the swap function:
template <class T> void swap(T* a, T* b) {
T* tempPtr = a;
a = b;
b = tempPtr;
}
Here's the reverse function:
template <class T> void List<T>::reverse() {
if (size > 1) {
swap(firstNodePtr, lastNodePtr);
node* curNodePtr = firstNodePtr;
while (curNodePtr != NULL) {
swap(curNodePtr->prevNodePtr, curNodePtr->nextNodePtr);
curNodePtr = curNodePtr->nextNodePtr;
}
}
}
Here's the class, its members and prototypes for functions:
template <class T> class List {
public:
List();
~List();
void push_back(T);
void push_front(T);
T get_at(unsigned);
unsigned get_size();
void reverse();
private:
struct node {
T val;
node* prevNodePtr;
node* nextNodePtr;
};
node* firstNodePtr;
node* lastNodePtr;
unsigned size;
};
Your swap<T> function does not work: it exchanges pointers, which are copied by value into local variables of your function, which has no effect in the caller.
Dropping your own swap and replacing it with std::swap will fix this problem.
Since you pass the two pointers by value, the changes to a and b don't propagate out of the swap() function, making it a no-op.
One way to fix it is by passing the pointers by reference:
template <class T> void swap(T*& a, T*& b) {
Alternatively (and preferably) just use std::swap() instead of your own function.
If you exposed your node structure (or at least a bidirectional iterator type for your list), you could avoid the whole issue and just use std::reverse.
List<int> someList;
// fill with data
std::reverse(someList.begin(), someList.end()); // where begin returns a bidirectional iterator for the head, and end returns a bidirectional iterator for 1 element beyond the tail
I keep finding myself wanting to do things the right way™. However, I am a bit confused about data encapsulation (not the principle of it, but how to do it correctly in C++)
Let's say I have the following class:
template <class T, class Alloc = std::allocator<T> >
class Tree
{
public:
class Node
{
public:
T data;
Node** get_children() const { return children; }
Node* get_parent() const { return parent; }
Node* get_right() const { return right; }
friend class Tree;
private:
Node** children;
Node* parent;
Node* right;
};
// typedefs for STL ...
class iterator // linear iterator
{
// ...
};
class const_iterator // linear iterator
{
// ...
};
// Tree operations ...
private:
Node root;
};
I want the tree to be able to modify the structure of the nodes freely, so I made it a friend class to the node.
I also want the user to be able to traverse the tree as a tree (rather than using the tree structure for storage behind the scenes, and just letting the user iterate over it linearly).
The data should be freely modifiable. If the owner of the tree object doesn't want another user to modify its contents, it can pass a const reference.
Now I'm left wondering about the return types of my getter methods. Whatever happens, I don't want the user ever to be able to change the structural information of the node directly. The tree should always be the one modifying the private members. Will this code guarantee that? What if instead of a Node**, we save a std::vector<Node*> and return a const reference to the vector?
These two are secure:
Node* get_parent() const { return parent; }
Node* get_right() const { return right; }
Since these functions return pointers by value, the user cannot modify the member variables.
This one is unsafe:
Node** get_children() const { return children; }
This too returns a pointer by value, so that the user cannot modify children, but the user can modify the elements in the array, which I presume are pointers to children. Here's how to make it safe:
Node * const * get_children() const { return children; }
(And when you're ready, you can advance to STL containers and stop using arrays.)
I don't get what Bjarne has on mind stating:
A list iterator must be something more complicated than a simple
pointer to an element because an element of a list in general does not
know where the next element of that list is. Thus, a list iterator
might be a pointer to a link
I know that list's node has pointers to the next and previous nodes. So how it "in general does not know"?
For contrast, let's consider an iterator for std::vector. Though you probably don't want to, you could basically implement it as a simple pointer:
template <class T>
class vector {
T *data;
size_t current_size;
size_t alloc_size;
public:
typedef T *iterator;
iterator begin() { return data; }
iterator end() { return data+current_size; }
// ...
};
and since the data in the vector is contiguous, when you do (for example) ++ on the iterator, it would do the right thing (get you to the next item in the vector).
With a linked list, it can't be that simple though -- if you used typedef to make iterator an alias for T *, it wouldn't work right. When you tried to do ++ on the iterator, it would just increment the pointer, instead of taking you to the next element in the linked list like it needs to.
You need to build some intelligence into the iterator class so operator++ (for one example) knows to "chase" the pointer instead of just incrementing.
template <class T>
class list {
class node {
T data;
node *next;
node *prev;
};
public:
class iterator {
node *pos;
public:
iterator operator++() {
return pos = pos->next;
}
iterator operator--() {
return pos = pos->prev;
}
};
};
Don't confuse list node with a list element. It might be a pointer, but to a node, which contains element and next/prev links. If it were just a pointer to the element, you wouldn't be able to move around with it. (Though it's unlikely to be just pointer, since it typically has to overload operator++ to fetch next pointer instead of incrementing itself).
This isn't true for intrusive lists, where node == element, but that's not std::list.
// illustrative
template <typename ElementType>
struct list_node {
ElementType element;
list_node *next, *prev;
};