C++ - iteration over a n-ary tree - c++

This is the function with iteration algorithm over a n-ary tree, given a name, and use it to find the parent tree, return parent tree's data if found, "ZERO" if no parent is found, and "NA" if the name is not in any tree in the Tree<string>* vector. It works most of the time, but will occasionally give wrong output that "ZERO", which a parent was supposed to be found, mostly in the leaves.
string getSource(const string name) const { // no recursion
if (existInVector(name, trees)) { // vector of Tree<string>*
queue<Tree<string>> treesQueue;
vector<Tree<string>*>::const_iterator it = trees.begin();
for (; it != trees.end(); ++it) { // for each tree
treesQueue.push(**it); // push tree
for (int i = 0; i < (**it).root->numChildren; ++i) // push children
treesQueue.push((**it).root->children[i]);
while (!treesQueue.empty()) {
Tree<string> temp = treesQueue.front(); // pop front
treesQueue.pop();
for (int i = 0; i < temp.root->childCount; ++i) { // check
if (temp.root->children[i].root->data == name)
return temp.root->data;
}
}
if (it == trees.end()-1 && treesQueue.empty())
return "ZERO";
}
}
return "NA";
}
Here is the class template of the tree:
template <class T>
class Tree {
private:
Node<T>* root;
public:
// ... member functions ...
};
template <class T>
class Node {
private:
T data;
int numChildren;
Tree<T>* children; // Tree<T> array
};
What is the possible reason to get the wrong result sometimes?
// example with wrong result
Tree<string> tree; // below is what is inside, root is Node "G", "H" is child of "G" and so on
G
\-H
\-I
\-J
tree.getSource("J") == "ZERO"; // Supposed to be "I"

You should push children of current node/tree you visit.
I also remove some copies.
std::string getSource(const std::string& name) const {
if (!existInVector(name, trees)) { // vector of Tree<string>*
return "NA";
}
std::queue<const Tree<std::string>*> treesQueue;
for (const auto& tree : trees) {
treesQueue.push(&tree);
while (!treesQueue.empty()) {
const auto& current = *treesQueue.front();
treesQueue.pop();
for (int i = 0; i != current.root->childCount; ++i) {
const auto& child = current.root->children[i];
if (child.root->data == name)
return current.root->data;
treesQueue.push(&child);
}
}
}
return "ZERO";
}

Related

How should I write destructor for this class in C++?

I have a class that has this kind of structure:
class myClass(){
public:
myClass(){//empty constructor}
void insertRecursively(string word) {
myClass* node = this;
for (int i = 0; i < word.length(); i++) {
if (node->map.find(word.at(i)) == node->map.end()) {
node->map[word.at(i)] = new myClass();
}
node = node->map[word.at(i)];
}
node->isEnd = true;
}
private:
unordered_map<char, myClass*> map = {};
bool isEnd = false;
}
I tried write destructor in this way but it gives me error 'std::bad_alloc':
~myClass() {
clear(map);
}
void clear(unordered_map<char, myClass*> map) {
for (auto& pair : map) {
if (pair.second != nullptr) {
clear(pair.second->map);
}
delete pair.second;
}
}
From what I known so far, I allocated memory on heap by using new keyword, so I should create destructor for myClass. I need to do this recursively because map contains pointers to other myClass pointers.
I've researched several hours and still cannot figure it out.
Can anyone help me to spot the problem that will cause 'std::bad_alloc' ?
My entire code:
class Trie {
public:
Trie() {
}
void insert(string word) {
Trie* node = this;
for (int i = 0; i < word.length(); i++) {
if (node->map.find(word.at(i)) == node->map.end()) {
node->map[word.at(i)] = new Trie();
}
node = node->map[word.at(i)];
}
node->isEnd = true;
}
bool search(string word) {
Trie* node = this;
for (int i = 0; i < word.length(); i++) {
if (node->map.find(word.at(i)) == node->map.end()) {
return false;
} else {
node = node->map[word.at(i)];
}
}
return node->isEnd;
}
bool startsWith(string prefix) {
Trie* node = this;
for (int i = 0; i < prefix.length(); i++) {
if (node->map.find(prefix.at(i)) == node->map.end()) {
return false;
} else {
node = node->map[prefix.at(i)];
}
}
return true;
}
~Trie() {
clear(map);
}
void clear(unordered_map<char, Trie*> map) {
for (auto& pair : map) {
if (pair.second != nullptr) {
clear(pair.second->map);
}
delete pair.second;
}
}
private:
unordered_map<char, Trie*> map = {};
bool isEnd = false;
};
I draw your attention to the following lines of code.
unordered_map<char, myClass*> map = {};
void clear(unordered_map<char, myClass> map)
Trimming a few characters...
unordered_map<char, myClass*> map
void clear(unordered_map<char, myClass> map)
Observe that you declared the local item map over char and myClass*, but you declared clear() as operating on a map over char and myClass, as opposed to a map over char and myClass*.
The * makes a difference.
Either you start to use smart pointers or you need to bring up a concept of ownership for your myClass instances created with new.
The latter could be:
Instances aof myClass are owned by another instance of myClass (lets call it owner) where &a appears in owner.map (You are doing this already).
Whenever an instance owner becomes destroyed, it has to free all instances it (directly) owns:
~myClass() {
for(const auto& pair : map){
delete pair.second;
}
}
Your Trie::clear() method deletes the same Trie twice.
~Trie() {
clear(map);
}
void clear(unordered_map<char, Trie*> map) {
for (auto& pair : map) {
if (pair.second != nullptr) {
clear(pair.second->map);
}
delete pair.second;
}
}
The destructor ~Trie calls clear(pair.second->map) and then calls delete pair.second which is the same as clear(pair.second->map) again since delete calls the destructor of the pointed-at data. Calling clear() twice on the same map means that the second call is trying to delete already deleted data, which causes the crash. Calling delete on a pointer does not change the value of the pointer, which is why the nullptr check does nothing.
Since the destructor calls clear(), the clear() method does not need to explicitly call itself recursively. Just delete the map pointers.
~Trie() {
clear(map);
}
void clear(unordered_map<char, Trie*> map) {
for (auto& pair : map) {
delete pair.second;
}
}
By the way, calling delete nullptr is fine since it is required to do nothing.

Why the elements disappear after I use push_back() function of vector

I am trying to use std::vector in a class to realize a muti-way tree.
Each time when I want to add a child in on of the member, I use a function addMember. I'm using VS2017 to debug this program. In this function scope the parent's children vector has actually add the elements by push_back(), but after exiting the function, the address of vector will change and the elements I have added will disappear.
Here's my code:
#include <iostream>
#include<string>
#include<vector>
using namespace std;
class member {
public:
string name;
member* parent;
vector<member*> children;
member(string m_name,member* m_parent):name(m_name),parent(m_parent){}
};
class familyTree {
private:
member ancestor;
public:
member* getAncestor() { return &ancestor; }
familyTree(member& m_ancestor):ancestor(m_ancestor){}
member* searchMember(string name,member* node,bool& flag);
void addMember(string name, int children_number,vector<string>& children_name);
};
member* familyTree::searchMember(string name, member* node,bool& flag) {
member* find = NULL;
if (node) {
if (node->name == name)
find = node;
else {
if (!flag) {
for (auto iter = node->children.begin(); iter != node->children.end(); iter++) {
find = searchMember(name, *iter, flag);
if (flag)
break;
}
}
}
}
return find;
}
void familyTree::addMember(string name,int children_number,vector<string>& children_name) {
bool flag = false;
member* parent = searchMember(name, getAncestor(), flag);
for (auto i : children_name) {
member* child = new member(i,parent);
parent->children.push_back(child);
}
}
I suspect that there is something wrong with the familyTree::searchMember function. Here was what you posted:
member* familyTree::searchMember(string name, member* node, bool& flag) {
member* find = NULL;
if (node) {
if (node->name == name)
find = node;
else {
if (!flag) {
for (auto iter = node->children.begin(); iter != node->children.end(); iter++) {
find = searchMember(name, *iter, flag);
if (flag)
break;
}
}
}
}
return find;
}
Notice that there was no method to set the flag = true once you have found the correct node.
The 2nd if statement should be:
if (node->name == name) {
find = node;
flag = true;
}
Otherwise within your for loop,
for (auto iter = node->children.begin(); iter != node->children.end(); iter++) {
find = searchMember(name, *iter, flag);
if (flag)
break;
}
even if there was a successful search, the for loop does not break and so it continues on, and the next child you search is guaranteed to not be a match.
Note that the way your searchMember function is structured results in NULL being returned if you search through a node that does not have a matching name and does not have any children because the for loop is skipped (no children to iterate through). So you end up with a lot of NULL pointers being assigned to parent in addMember.

Autocomplete using Trie

I'm attempting to make some sort of autocomplete feature in c++. First by using a Trie and once that works (and most importantly, I know HOW it all works) I'll try it using a Ternary tree. But as for now I get a segmentation fault when ever I add words starting with a different characte than those already in the Trie.
Eg. we add "abc", "abcd" and "abcde" this is no problem. Later when I want to add (while the "abc" etc are still in the Trie) "xfce", "xfced" a segmentation fault occurs.
I've been debugging this for some while now and can't seem to find the problem.
I think the problem resides somewhere in Trie.cpp so that's the file I'll provide here. However it might be in the main function aswell but I don't wanna get yelled at for posting to much code...
#include "Trie.h"
#include <iostream>
Trie::Trie()
{
this->root = new Node(false);
}
Trie::~Trie()
{
}
Trie::Node::Node(bool isLeaf)
{
this->isLeaf = isLeaf;
}
void Trie::insert(const std::string& word)
{
Node* crawler = this->root;
int index;
for(int i = 0; i < word.length(); ++i)
{
index = CHAR_TO_INDEX(word.at(i));
if(!crawler->children[index])
{
crawler->children[index] = new Node(false);
}
crawler = crawler->children[index];
}
crawler->isLeaf = true;
}
int Trie::contains(const std::string& word)
{
int index;
Node* crawler = this->root;
for(int i = 0; i < word.length(); ++i)
{
index = CHAR_TO_INDEX(word.at(i));
if(!crawler->children[index])
{
return -1;
}
crawler = crawler->children[index];
}
return (crawler != NULL && crawler->isLeaf);
}
std::vector<std::string> Trie::possibleSuffixes(std::string& prefix)
{
Node* crawler = this->root;
int index;
std::vector<std::string> result;
for(int i = 0; i < prefix.length(); ++i)
{
index = CHAR_TO_INDEX(prefix.at(i));
crawler = crawler->children[index];
}
traverse(prefix, crawler, result);
return result;
}
void Trie::traverse(std::string prefix, Node* node, std::vector<std::string>& v)
{
if(node->isLeaf)
{
v.push_back(prefix);
}
for(int i = 0; i < ALPHABET; ++i)
{
if(node->children[i])
{
traverse(prefix + (char)('a' + i), node->children[i], v);
}
}
}
Entire Trie class:
#ifndef TRIE_H
#define TRIE_H
#include <string>
#include <vector>
#define ARRAYSIZE(a) sizeof(a / sizeof(a[0]))
#define ALPHABET 26
#define CHAR_TO_INDEX(c) ((int)c - (int)'a')
class Trie
{
private:
struct Node
{
Node(bool isLeaf);
struct Node *children[ALPHABET];
bool isLeaf;
};
Node *root;
void traverse(std::string prefix, Node* node, std::vector<std::string>& v);
public:
Trie();
~Trie();
int contains(const std::string& word); //Checks the existance of a specific word in the trie
void insert(const std::string& word); //Inserts new word in the trie if not already there
std::vector<std::string> possibleSuffixes(std::string& prefix);
};
Though you didn't mention about your Node class, I am assuming this -
class Node {
public:
bool isLeaf;
// must be >= 25 as you're inserting lowercase letters
// assuming your CHAR_TO_INDEX(ch) returns 0 based index
// e.g. 'a' => 0, 'b' => 1 ... 'z' => 25
Node* children[30];
// default constructor should be like this
Node(): isLeaf(false) {
for(int i = 0; i < 26; i++) {
children[i] = NULL;
}
}
~Node() {
for(int i = 0; i < 26; i++) {
if(children[i]) {
delete children[i];
children[i] = NULL;
}
}
delete this;
}
};
Please compare your Node class/struct whether its something like this.

Segmentation fault after allocating new memory

I am trying to return the root node of a BST. My recursive function involves using std::vector as a container for SPnodes (the tree node object) which during a single call is iterated over and a vector with fewer elements is passed up to the next call. Once the vector is of size 1, my function returns a new SPnode which is created by calling the copy constructor on the first (and only) element in nodeList, the vector container.
This is then passed through the wrapper function and then to the original caller.
Question
I understand that when the vector container goes out of scope it is destroyed, however I'm allocating new memory and copying the SPnodes so I don't understand how this could be causing the problem. So then, what is causing this? I doubt my problem is unique, but I can't seem to find an answer after going through countless SO questions.
Code
SPnode header:
#ifndef SPNODEOBJ_H
#define SPNODEOBJ_H
#include "IntervalObjects.h"
#include "IntervalObj.h"
#include "InVecObj.h"
#include "StandardLib.h"
// --- SPnode Class ------------------------------------------------------------
// SPnode (or Subpaving Node), is a node in a binary tree which contains a
// parameter "box" which is an InVec object, and a left and right child which
// are also SPnode objects. These SPnode objects are the resultant boxes from
// calling the expand method on m_box. This implementation allows for two
// important characteristics: there is a way to determine "where" a given box in
// a subpaving is by iterating over a node's left or right child, which in turn
// allows for greater efficiency; secondly, the tree structure gives a "history"
// of the subpaving, which allows for the determination of parent boxes.
class SPnode
{
private:
InVec m_box;
// left and right children of this SPnode object - note that these must be
// pointers in order to use them in the definition of the class, otherwise
// SPnode would reference itself in its definition
SPnode* m_left;
SPnode* m_right;
// a boolean sepcifying whether the SPnode m_box is in the given subpaving
bool m_inSP;
bool m_hasBeenIterated;
public:
SPnode(InVec box): m_box(box), m_left(NULL), m_right(NULL), m_inSP(true),
m_hasBeenIterated(false) {}
SPnode(const SPnode &ASP)
{
recConstructor(this, ASP);
}
void recConstructor(SPnode* const &parent, const SPnode &ASP);
void recDestructor(SPnode* const &ASP);
~SPnode() {delete m_left;
delete m_right;}
// getters and setters
InVec getBox() const {return m_box;}
SPnode* getLeft() const {return m_left;}
SPnode* getRight() const {return m_right;}
bool getInSP() const {return m_inSP;}
bool getIterated() const {return m_hasBeenIterated;}
void setBox(const InVec box) {m_box = box;}
void setLeft(SPnode* const p_node) {m_left = p_node;}
void setRight(SPnode* const p_node) {m_right = p_node;}
void setInSP(bool truth) {m_inSP = truth;} // when this is called truth
// should only be false
void setIterated(bool truth) {m_hasBeenIterated = truth;}
bool isLeaf() const;
friend std::ostream& operator<< (std::ostream &out, const SPnode &ASP);
SPnode operator=(const SPnode& ASP)
{
std::cout << "assignment called\n";
if (this == &ASP) return *this;
recConstructor(this, ASP);
return *this;
}
friend void expand(SPnode &ASP);
friend InVec lower(const InVec &box, const Interval &width, int axis);
friend InVec upper(const InVec &box, const Interval &width, int axis);
friend void mince(SPnode &ASP);
friend SPnode regularize(ImList &list, InVec box);
friend SPnode* recRegularize(std::vector<InVec> &list, SPnode &parentNode);
};
#endif
SPnode cpp file:
#include "SPnodeObj.h"
void SPnode::recConstructor(SPnode* const &parent, const SPnode &ASP)
{
parent->m_box = ASP.m_box;
parent->m_inSP = ASP.m_inSP;
parent->m_hasBeenIterated = ASP.m_hasBeenIterated;
if (ASP.isLeaf())
{
parent->m_left = NULL;
parent->m_right = NULL;
return;
}
if (ASP.m_left == NULL)
{
parent->m_left = NULL;
}
if (ASP.m_right == NULL)
{
parent->m_right = NULL;
}
if (ASP.m_left != NULL)
{
parent->m_left = new SPnode((ASP.m_left)->m_box);
}
if (ASP.m_right != NULL)
{
parent->m_right = new SPnode((ASP.m_right)->m_box);
}
if (ASP.m_left != NULL)
{
recConstructor(parent->m_left, *(ASP.m_left));
}
if (ASP.m_right != NULL)
{
recConstructor(parent->m_right, *(ASP.m_right));
}
}
bool SPnode::isLeaf() const
{
return (m_left == NULL && m_right == NULL);
}
std::ostream& operator<< (std::ostream &out, const SPnode &ASP)
{
if (ASP.m_right == NULL && ASP.m_left != NULL)
{
out << "SPnode(SPnode, " << ASP.m_box << ", NULL)";
}
else if (ASP.m_left == NULL && ASP.m_right != NULL)
{
out << "SPnode(NULL, " << ASP.m_box << ", SPnode)";
}
else if (ASP.m_left == NULL && ASP.m_right == NULL)
{
out << "SPnode(NULL, " << ASP.m_box << ", NULL)";
}
else
{
out << "SPnode(SPnode, " << ASP.m_box << ", SPnode)";
}
return out;
}
and my functions:
SPnode* listToTree (std::vector<InVec> boxList)
{
int counter = 0;
std::vector<SPnode> nodeList;
for (auto box : boxList)
{
nodeList.push_back(SPnode(box));
counter += 1;
}
//return recListToTree(nodeList);
//return new SPnode(*(recListToTree(nodeList)));
return recListToTree(nodeList);
}
SPnode* recListToTree (std::vector<SPnode> &nodeList)
{
std::cout << "nodelist size is: " << nodeList.size() << "\n";
if (nodeList.size() == 1)
{
return new SPnode(nodeList.at(0));
}
std::vector<SPnode> parentNodeList;
int counter = 0;
for (auto node : nodeList)
{
if (node.getIterated())
{
counter += 1;
continue;
}
if (counter + 1 == nodeList.size())
{
parentNodeList.push_back(SPnode(node.getBox()));
break;
}
if (node.getBox().isAdjacent(nodeList.at(counter + 1).getBox()))
{
SPnode newNode =
SPnode(node.getBox().combine(nodeList.at(counter + 1).getBox()));
if (lessThan(node.getBox(), nodeList.at(counter + 1).getBox()))
{
newNode.setLeft (new SPnode(node));
newNode.setRight(new SPnode((nodeList.at(counter + 1))));
}
else
{
newNode.setRight(new SPnode(node));
newNode.setLeft (new SPnode((nodeList.at(counter + 1))));
}
parentNodeList.push_back(SPnode(newNode));
nodeList.at(counter).setIterated(true);
nodeList.at(counter + 1).setIterated(true);
counter += 1;
}
else
{
parentNodeList.push_back(SPnode(node.getBox()));
nodeList.at(counter + 1).setIterated(true);
counter += 1;
}
}
recListToTree(parentNodeList);
}
Any help is appreciated!
recListToTree does not return a value on all paths, as there is not return at the end of the function (crank up the warning level in the compiler for it to tell you this). This garbage return value is the likely cause of your crashes.
Not related to the crash, but a memory leak problem, is that recConstructor is overly complicated (all those duplicated ifs) and will copy things twice (once via the copy constructor invoked by new, and once later by the recursive call to recConstructor).

Can't get insert and attach functions to work properly for a container class in c++

I'm trying to store the numbers in my driver class by arranging them with insert and attach functions from the implementation file. Insert function is supposed to move the number to the left or a smaller position and attach moves to the right or higher position in the data array.This is the implementation one:
#include "Lab1A.h"
#include <iostream>
#include <cassert>
#include <algorithm>
sequence::sequence() {
used = 0;
current_index = 0;
}
// MUTATOR MEMBER FUNCTIONS
//Postcondition: The first item in the sequence becomes the current item
void sequence::start() {
current_index = 0;
//Precondition: is_item returns true.
//Postcondition: If the current item was already the last item in the
//sequence, then there is no longer any current item. Otherwise, the new
//current item is the item immediately after the original current item.
}
void sequence::advance() {
if (is_item()) {
current_index++;
}
}
//Precondition: size( ) < CAPACITY.
//Postcondition: A new copy of entry has been inserted in the sequence
//before the current item. If there was no current item, then the new entry
//has been inserted at the front of the sequence (position 0). In either //case, the newly inserted item is now the current item of the sequence.
void sequence::insert(const value_type& entry) {
if (size() < CAPACITY) {
data[used] = data[used - 1];
data[used] = entry;
data[current_index] = entry;
used++;
}
if (is_item() == false) {
data[used] = entry;
data[used] = data[used + 1];
}
}
//Precondition: size( ) < CAPACITY.
//Postcondition: A new copy of entry has been inserted in the sequence //after the current item. If there was no current item, then the new entry //has been attached to the end of the sequence. In either case, the newly
//inserted item is now the current item of the sequence.
void sequence::attach(const value_type& entry) {
if (size() < CAPACITY) {
data[used] = data[used + 1];
data[used] = entry;
data[current_index] = entry;
used++;
}
if (is_item() == false) {
data[used] = entry;
data[used] = data[used + 1];
}
}
//Precondition: is_item returns true.
//Postcondition: The current item has been removed from the sequence, and //the item after this (if there is one) is now the new current item.
void sequence::remove_current() {
int i;
if (is_item()) {
current_index--;
data[i] = data[current_index];
}
}
// ACCESSOR MEMBER FUNCTIONS
//Postcondition: The value returned is the number of items in the
//sequence.
int sequence::size() const {
return used;
}
//Postcondition: A true return value indicates that there is a valid
//"current" item that may be retrieved by invoking the current
//member function below. A false return value indicates that
//there is no valid current item.
bool sequence::is_item() const {
return (current_index < used);
}
//Precondition: is_item( ) returns true.
//Postcondition: The item returned is the current item in the sequence.
sequence::value_type sequence::current() const {
return data[current_index];
}
void sequence::print() {
for (int j = 0; j < used; j++) {
cout << data[j] << " ";
}
}
Driver file:
#include <iostream>
#include <cstdlib>
#include "Lab1Aimplementation.cpp"
using namespace std;
int main()
{
sequence numbers;
numbers.insert(21);
numbers.attach(33);
numbers.insert(22);
numbers.print();
return 0;
}
I'm trying to get this output: 21 22 33
Instead I get: 22 33 22
Possible declaration of sequence as OP didn't attach one:
class sequence
{
using index_type = int;
using value_type = size_t;
static const index_type CAPACITY = 1024;
value_type data[CAPACITY];
index_type used;
index_type current_index;
public:
sequence();
void start();
void advance();
void insert(const value_type& entry);
void attach(const value_type& entry);
void remove_current();
int size() const;
bool is_item() const;
value_type current() const;
void print();
};
I'm using here these assumptions:
current_index can be represented by an iterator; when no element is available, it points to past-the-end.
insert is supposed to insert before the current item, attach is supposed to insert after the current item.
Both insertion methods, judging by the expected result (21 22 33) are such that after being called, the current item always refers to the newly inserted item.
This said, if you just wrap around a list, each function is basically a one liner, you just need to know that the list class does. Bonus: O(1) insertion and removal, capacity constraint virtually removed.
The OP resets current_index to 0, so it is reasonable to assume that it could point anywhere in the array. Therefore, it is not possible to achieve insertion just by swapping elements, you need to move a whole block of data. Try this out here.
#include <iostream>
#include <sstream>
#include <list>
#include <cassert>
template <typename T>
class sequence
{
private:
std::list<T> _l;
typename std::list<T>::iterator _i;
public:
using value_type = T;
using size_type = typename std::list<T>::size_type;
size_type size() const {
return _l.size();
}
T current() const {
assert(is_current_valid());
return *_i;
}
size_type current_index() const {
return _i - _l.begin();
}
void increase_current() {
if (is_current_valid()) {
++_i;
}
}
void decrease_current() {
if (_i != _l.begin()) {
--_i;
}
}
void reset_current() {
_i = _l.begin();
}
bool is_current_valid() const {
// "is_item"
return _i != _l.end();
}
void remove_current() {
assert(is_current_valid());
// _i takes the next current element, eventually end()
_i = _l.erase(_i);
}
void insert_before(const value_type &entry) {
// _i is always the newly inserted element
_i = _l.insert(_i, entry);
}
void insert_after(const value_type &entry) {
// _i is always the newly inserted element
assert(is_current_valid());
_i = _l.insert(++_i, entry);
}
friend std::ostream &operator<<(std::ostream &os, sequence const &s) {
for (auto it = s._l.begin(); it != s._l.end(); ++it) {
if (it != s._l.begin()) {
os << " " << *it;
} else {
os << *it;
}
}
return os;
}
sequence() : _l(), _i(_l.end()) {}
};
int main() {
sequence<std::size_t> numbers;
numbers.insert_before(21); // insert 21, then points to 21
numbers.insert_after(33); // 33 after 21, then points to 33
numbers.insert_before(22); // 22 before 21, then points to 22
std::cout << numbers << std::endl;
// Programmatically check if the result is the requested one
const std::string expected = "21 22 33";
std::stringstream output;
output << numbers;
if (output.str() != expected) {
std::cerr << "Error!" << std::endl;
return 1;
}
return 0;
}
Found the answer at http://www.cplusplus.com/forum/beginner/141458/. This code works, however I don't understand the logic behind what is I guess a backwards loop.
void sequence::attach(const value_type& entry) {// value_type is the declared typedef double data
int i;
if(!is_item()) // is_item checks if there's any number in the array
current_index = used - 1; // used keeps track of how many numbers are stored
for (i = used; i > current_index; --i)
data[i]=data[i-1];
data[current_index+1] = entry;
++current_index;
++used;
}
void sequence::insert(const value_type& entry){
int i;
if(!is_item())
current_index = used;
for (i = used; i > current_index; --i)
data[i]=data[i-1];
data[current_index] = entry;
++current_index;
++used;
}