i was confused with a problem of nested class
#ifndef MINIGRAPH_H_
#define MINIGRAPH_H_
#include<vector>
#include<list>
#include<iostream>
template <typename VEX,typename EDGE>
class MiniGraph
{
public:
class _Node
{
public:
VEX _Vex;
EDGE _Edge;
_Node* next;
};
MiniGraph() {};
MiniGraph(int vex_num);
void add(VEX from, VEX to, EDGE Edge);
//void display();
private:
std::vector<void*> _VecNode;
_Node* NewNode(VEX vex) { _Node* ptr = new _Node;ptr->_Vex = vex;return ptr; }
_Node* NewNode(VEX vex, EDGE edge) { _Node* ptr = new _Node;ptr->_Vex = vex;ptr->_Edge = edge;return ptr; }
};
template <typename VEX, typename EDGE>
MiniGraph<VEX, EDGE>::MiniGraph(int vex_num)
{
int i = 0;
for (i = 0;i < vex_num;i++)
{
struct Node *ptr_tmp = new _Node;
ptr_tmp->next = NULL;
_VecNode.push_back(ptr_tmp);
}
}
template <typename VEX, typename EDGE>
void MiniGraph<VEX, EDGE>::add(VEX from, VEX to, EDGE edge)
{
int i;
_Node* ptr_node = NULL;
for (i = 1;i < _VecNode.size();i++)
{
ptr_node = (_Node*)_VecNode[i];
if (ptr_node->_Vex == from)
break;
}
if (i == _VecNode.size())
{
ptr_node = NewNode(from);
_VecNode.push_back(ptr_node);
}
ptr_node = NewNode(from, edge);
**ptr_node->next = (_Node*)_VecNode[i]->next;//insert node from head**
_VecNode[i]->next = ptr_node;
}
when compiled with
#include "MiniGraph.h"
void main()
{
MiniGraph<int, double> hh;
hh.add(1, 2, 0.1);
hh.add(1, 3, 0.2);
hh.add(2, 3, 0.3);
}
error triggered in ptr_node->next = (_Node*)_VecNode[i]->next;//insert node from head
saying ->next left must point to class/struct/union/generic type;
but _Node was declared 。i was tring to substitute class by struct,but it triggered the same error.
it seems that declaration is not visible.how should i solve this problem?
As #WhozCraig mentioned in the comments above, you have a vector of void*, not a vector of Node*. Therefore there is no member called next. You can type_cast the void* to a Node* to "fix" the problem, but you would be in for a surprise.
The problem start from your main function.
int main()
{
MiniGraph<int, double> hh;
This code will call the default constructor of the MiniGraph class. This means that no Nodes will be created and your vector of Node* will be empty (i.e. std::vector<Node*>::size = 0.
Then you call the add function:
hh.add(1, 2, 0.1);
Inside the add function the following happens:
void add(VEX from_, VEX to_, EDGE edge_)
{
std::size_t i = 0;
for (i=1; i<node_ptrs.size(); i++)
{
if (node_ptrs[i]->vex == from_)
break;
}
After the first call to add(), the vector size = 0, but i = 1. That's because you initialize i=1 inside the for-loop.
Then you check if i == vector::size.
Node *tmp = nullptr;
if (i == node_ptrs.size())
{
tmp = NewNode(from_);
node_ptrs.push_back(tmp);
}
The if-statement above will never be executed, because i = 1 and vector::size = 0.
Finally, you do the following:
tmp = NewNode(from_, edge_);
tmp->next = node_ptrs[i]->next; // this fails because your vector size is 0!
node_ptrs[i]->next = tmp;
The first line works. tmp is a Node* which points to a newly created Node. ok. Then the program fails with a segmentaion fault because tmp->next is trying to shallow copy the pointer in the i element of the vector. Remember now that i = 1 and the vector is still empty, i.e. vector::size = 0. There is no node_ptrs[1] element to copy from. And that's why it crashes.
Example of the code above: https://rextester.com/DTODK86272
There are more problems to consider in your original code... For example:
if only one pointer is meant to point at a certain Node, then use a unique_ptr. You can still have other pointer reading from a unique_ptr, but they won't be able to do anything else.
write constructors that initialize your pointers to nullptr and call a zero initialization on the vex and edge members.
Avoid using int as array indices. What if someone decides to use a negative number as a function argument?
In your implementation you must check if the vector is empty before you start doing anything else. What should happen if the vector is empty?
Related
I'm having trouble figuring out the destructor for my hashTable class, the destructor is like this:
template <typename ElementType>
HashSet<ElementType>::~HashSet() noexcept
{
for (unsigned int i=0;i<hashCapacity;i++)
{
Node* current = hashTable[i];
while(current != nullptr)
{
Node* entry = current;
current = current->next;
delete[] entry;
}
}
delete[] hashTable;
}
No matter I use either delete[] or delete, it gives me either double-free errors or segmentation fault.
The class template is below:
template <typename ElementType>
class HashSet : public Set<ElementType>
{
public:
// The default capacity of the HashSet before anything has been
// added to it.
static constexpr unsigned int DEFAULT_CAPACITY = 10;
// A HashFunction is a function that takes a reference to a const
// ElementType and returns an unsigned int.
using HashFunction = std::function<unsigned int(const ElementType&)>;
public:
// Initializes a HashSet to be empty so that it will use the given
// hash function whenever it needs to hash an element.
explicit HashSet(HashFunction hashFunction);
// Cleans up the HashSet so that it leaks no memory.
~HashSet() noexcept override;
// add() adds an element to the set. If the element is already in the set,
// this function has no effect. This function triggers a resizing of the
// array when the ratio of size to capacity would exceed 0.8, in which case
// the new capacity should be determined by this formula:
//
// capacity * 2 + 1
//
// In the case where the array is resized, this function runs in linear
// time (with respect to the number of elements, assuming a good hash
// function); otherwise, it runs in constant time (again, assuming a good
// hash function). The amortized running time is also constant.
void add(const ElementType& element) override;
Where my add function and default constructor implementation is like this:
template <typename ElementType>
HashSet<ElementType>::HashSet(HashFunction hashFunction)
: hashFunction{hashFunction}
{
hashCapacity = DEFAULT_CAPACITY;
hashSize = 0;
hashTable = new Node* [hashCapacity];
for (int i=0;i<hashCapacity;++i)
{
hashTable[i] = nullptr;
}
}
template <typename ElementType>
void HashSet<ElementType>::add(const ElementType& element)
{
if (contains(element)==false)
{
if ((hashSize/hashCapacity) > 0.8)
{
}
else
{
unsigned int index = hashFunction(element) % hashCapacity;
hashSize += 1;
Node* add = new Node;
add->next = nullptr;
add->value = element;
if (hashTable[index]==nullptr)
{
hashTable[index] = add;
}
else
{
Node* addNode = hashTable[index];
while(addNode->next != nullptr)
{
addNode = addNode->next;
}
addNode->next = add;
}
}
}
}
Note: that resize hashtable part is incomplete because I'm examining the functionality for my hash table to hold a small amount of value first.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I am new to coding, so please forgive me if this question seems stupid. I was writing my own List class to get a better understanding of how Lists are structured, but I ran into an issue. I dynamically allocated my list as I added more items to it, and the deconstructor on my program ran just fine with ints. However, as I was testing with std::string, I ran into an issue. It keeps throwing exceptions after my deconstructor is called(, even though (I'm fairly certain) I deleted the memory I allotted alone, and not theirs (read access violation).
I've tried using smart pointers instead of deleting the allocated memory in my deconstuctor, but that ends up having the same issue. Looking online, all I can seem to find is, "only delete with deconstructors," and, "don't have exception handling in deconstructors." Both of which are not even issues with what I've written.
Here is firstly, the relevant code (in my mind) to solving this.
#include <string>
#include <iostream>
using std::cout;
using std::cin;
using std::string;
template <class type>
class List
{
struct Node
{
type data;
Node* next;
};
public:
List();
~List();
void addToList(type var);
private:
Node head;
Node *last, *lastAcc;
unsigned int length, prevPos;
};
template <class type>
List<type>::~List()
{
Node *prevPtr;
lastAcc = head.next;
while (lastAcc->next) // While the next pointer leads to something
{
// Go to that something, and delete the item you were on
prevPtr = lastAcc;
lastAcc = lastAcc->next;
delete prevPtr;
}
delete lastAcc;
}
template <class type>
void List<type>::addToList(type var)
{
if (length)
{
last->next = new Node;
last = last->next;
last->data = var;
}
else
{
head.data = var;
}
lastAcc = last;
prevPos = length++;
}
template <class type>
List<type>::List()
{
head.next = 0;
prevPos = 0;
lastAcc = last = &head;
length = 0;
}
int main()
{
string boi[] = { "Today is a good day", "I had an apple", "It tasted delicious" };
List<string> multiString;
for (int i = 0; i < 3; i++)
{
multiString.addToList(boi[i]);
}
return 0;
}
I expected the code to run just fine, and if I made an error, I thought the error would show up on my code. Not on std::string. Any help would be greatly appreciated.
[Edit] On an added note, [lastAcc] is abbreviated for last accessed; it's just something I implemented to make going through the lists faster than just having to start from 0 every time. [prevPos] shows the position of [lastAcc] in the list. Let me know if you need to see more of my code or explain anything~!
you aren't initialising last->next in addToList so iteration in your destructor falls off the end of the list. The correct code is:
void List<type>::addToList(type var)
{
if (length)
{
last->next = new Node();
last = last->next;
last->data = var;
}
else
{
head.data = var;
}
lastAcc = last;
prevPos = length++;
}
The difference is new Node() rather than new Node. The first value initialises POD types, the second doesn't.
Alternatively if you define a constructor for Node then new Node and new Node() will be equivalent:
struct Node
{
Node(): next( 0 ) {}
type data;
Node* next;
};
For a small efficiency gain you could move your value into your node to prevent copies:
struct Node
{
Node(): next( 0 ) {}
Node(type && data): data( std::move( data ) ), next( 0 ) {}
type data;
Node* next;
};
template <typename T>
void addToList(T&& var)
{
if (length)
{
last->next = new Node(std::move(var));
last = last->next;
}
else
{
head.data = std::move(var);
}
lastAcc = last;
prevPos = length++;
}
I'm not sure how to describe this. I have to iterate through objects which are connected by pointers with each other, However the first element shouldn't have values stored in its class just the pointer to the next element.
I came up with a small class hierarchy. If I use it like that I have to cast the base class to the derived element. It only seems to work with a dirty cast.
Is there a better solution?
Here the example code:
#include <iostream>
struct Basenode {
Basenode* next;
};
struct Skipnode : Basenode {
Skipnode(int in_key, int in_value);
int key;
int value;
};
Skipnode::Skipnode(int in_key, int in_value)
: key{ in_key }, value{ in_value }
{
}
int main()
try {
Basenode head; // no key and value
Skipnode first(4, 2); // key and value
Skipnode second(8, 2);
Basenode* p = &head;
head.next = &first; // fine
first.next = &second; // fine
// p = p->next->key; // not accesible because is Basenode not derrived Skipnode
std::cout << static_cast<Skipnode*>(p->next)->key; // fine but diryt cast slicing ?
std::cin.get();
}
catch (std::runtime_error& e) {
std::cerr << e.what() << "\n";
std::cin.get();
}
catch (...) {
std::cerr << "unknown error " << "\n";
std::cin.get();
}
Edit: it was asked int the comments why i need this anyway. I think i gave a to limited example.
I need it to implement a skiplist. Many algorithms of it require to start on a element before the first element. The head element. I could make it a normal node and put in dummy values for the values but it didnt seem right. So now i came up with this ugly solution from youre suggestions to start on the head elements.
Heres a snippet with the copy constructor as example.
class Skiplist {
public:
//...
Skiplist(const Skiplist& other); // copy constructor
//...
private:
struct Skipnode; // forward declaration so Basenode can have Skiplist*
struct Basenode { // Empty node, mainly created to represent head element.
// Is there a way to get a empty head with no key / values without using this ?
Basenode(int in_level);
Basenode(const std::vector<Skipnode*>& in_next);
std::vector <Skipnode*> next;
};
struct Skipnode : Basenode { // derived so with Basenode* we can start the iteration of the node on head
Skipnode(value_type val, int in_level);
Skipnode(value_type val, const std::vector<Skipnode*>& in_next);
value_type value; // first key / second mapped type = value
//key_type key;
//mapped_type value;
};
Basenode head{ 0 }; // element before first element containg pointers to all the first elements of each level
//...
};
Skiplist::Skiplist(const Skiplist& other) // copy constructor
:head{ other.head }, top_level{ other.top_level }, random_engine{ other.random_engine }
// on the first level let the other Skiplist present its elements and make a deep copy of them
// now still the higher levels point to the other node so this is fixed in the second part
// then the next level pointers are installed linked to the elements of the new node
{
if (top_level == 0) return; // no elements are present so dont bother to allocate nodes
{
// installment of lowest level, each element is located here
Skipnode* other_node = other.head.next[0];
Basenode* current_position = &head;
while (other_node != nullptr) {
Skipnode* new_node = new Skipnode{ other_node->value,other_node->next };
current_position->next[0] = new_node;
current_position = current_position->next[0];
other_node = other_node->next[0];
}
current_position->next[0] = nullptr;
}
// installment of the other levels
for (size_type curr = 1; curr < top_level; ++curr) {
Basenode* current_position = &head; // the current position of the level[curr]
Skipnode* next_position = current_position->next[curr]; // next position after curr containing still pointers to the other skiplist
Basenode* lowest_position = &head; // lowest level position used to find the new pointers and attach them "behind" current
while (lowest_position != nullptr && next_position != nullptr) {
if (lowest_position->next[0]->value.first == next_position->value.first) { // check by unique key, address of next pos is still of the other skiplist
current_position->next[curr] = lowest_position->next[0]; // lowest is the valid address of new node
current_position = current_position->next[curr];
next_position = next_position->next[curr]; // go to next element of other node
if (next_position == nullptr) { // case end is reached
current_position->next[curr] = nullptr;
current_position = current_position->next[curr];
}
}
else { // forward position of lowest level until other key == next position key
lowest_position = lowest_position->next[0];
}
}
}
}
See here for a basic explanation how a skiplist is organized:
https://en.wikipedia.org/wiki/Skip_list
The whole code is on codereview:
https://codereview.stackexchange.com/questions/197752/non-generic-skip-list-implementation-in-c-version-2
All the things #SomeProgrammerDude is saying, or:
I don't see a need for class BaseNode at all. Why can't we just have (all other things being equal):
SkipNode *head = &first;
...
Or better yet a class called (for example) SkipNodeList that handles all aspects of managing and iterating through a list of SkipNodes.
Of course, this is all a bit silly anyway, just use std::list (or std::forward_list) for this and benefit from all that STL goodness.
Or you can derive from one of these to add your own functionality (such as a mutex to make the list threadsafe or keeping track of the number of elements currently in the list, as suggested by #iMajuscule).
Yes, there is a better way:
Forward-declare Skipnode, and in BaseNode, use a pointer to SkipNode, this way you don't have to cast:
struct Skipnode;
struct Basenode {
Skipnode* next;
};
Also, to illustrate how this design where Skipnode inherits from Basenode could make sense (related to the discussion in the comments), we can imagine having a member in Basenode counting how many elements are next (counting the one in the next member and its successors)
I'm trying to implement some basic Tree structure for sea navigation algorithm.
I've got something like this:
class Point {
float lng;
float lat;
};
class Node {
public:
Node *parent;
std::list<Node> *childern;
Point *point;
Node::Node(Node *prnt, Point *point);
void Node::calcChildrens();
};
Node::Node(Node *prnt, Point *point) {
this->parent = prnt;
this->point = point;
this->childern = nullptr;
}
int counter = 0;
void Node::calcChildrens() {
for (int i = 0; i < 5; i++) {
Point *p = new Point(someValX, someValY);
Node n = Node(this, p);
if (this->childern == NULL) this->childern = new list<Node>;
this->childern->push_back(n);
if (counter < 4) {
counter++;
n.calcChildrens();
}
}
This should create 4 level's of recursion tree, but creates just one level of tree.
I think it's the problem with parent pointers but i can't realize what really is happening.
There are several issues with your code
struct Point { // we want public access, hence struct not class
float lng;
float lat;
};
struct Node { // if all members a public, use struct
Node*parent = nullptr; // provide default argument
std::list<Node> children; // hold a list, not a pointer to one
Point point; // hold a Point, not a pointer to one
Node(Node*p, const Point&x)
: parent(p), point(x) {} // use initialization list
void calcChildren(size_t levels); // avoid global variable counter; use correct English
};
void Node::calcChildren(size_t levels)
{
if(levels--)
for(int i = 0; i < 5; i++) { // really 5? 4 children seems more logical
// construct child in place, avoid copying a Node
children.emplace_back(this, Point{someValX, someValY});
children.back().calcChildren(levels);
}
}
You may also keep track of the tree depth as a data member for each node. Unfortunately, as you failed to provide a Minimal Complete and Verifiable Example, I cannot test this here.
Note also that your code had no destructor for Node, leaking all memory allocated with a node. This problem disappears when avoiding those pointers is favour of objects. As Nodes are allocated on the heap anyway, this is the logical and correct way of doing things in C++.
Note further that you may want to avoid keeping the children in a linked list (linked lists are to be avoided if efficiency is important). You may instead use an array or vector. In this case
struct Node { // if all members a public, use struct
Node*parent = nullptr; // provide default argument
std::vector<Node> children; // hold a vector, not a pointer to one
Point point; // hold a Point, not a pointer to one
Node(Node*p, const Point&x)
: parent(p), point(x) {} // use initialization list
void calcChildren(size_t levels); // avoid global variable counter; use correct English
};
void Node::calcChildren(size_t levels)
{
if(levels--) {
children.reserve(5);
for(int i = 0; i < 5; i++) {
// construct child in place, avoid copying a Node
children.emplace_back(this, Point{someValX, someValY});
children.back().calcChildren(levels);
}
}
}
I am trying to write a code for a linked list but when I try to add a node the code gets stuck at the line immediately after the while loop in the add() function, reporting an access violation error. What's wrong?
#include<iostream>
template <class T>
class linkedlist
{
struct node
{
T data;
node *lp;
}*p;
public:
linkedlist();
void add(T t);
};
template<class T>
void linkedlist<T>::add(T t)
{
node *r,*q;
r = q = p;
while(p!= NULL)
{
q = p;
p = p->lp;
}
q->lp = new node;
q->lp->data = t;
p = r;
}
template<class T>
linkedlist<T>::linkedlist()
{
p = NULL;
}
int main()
{
linkedlist<int> l1;
l1.add(3);
}
You initialize p to NULL in the constructor, then try to dereference it in add (via q):
r = q = p;
// the while loop will not be executed as p == NULL
q->lp = new node;
You must initialize p first - either during construction (in which case your "empty" list won't be physically empty, so you must deal with this specifically, e.g. when iterating / removing elements), or you should check in add for the case when p == null and handle it differently.
Side note: p is supposed to point to your head element, so it is risky to use it also to iterate through the list in add, then restore its original value (stored in r). Why not simply leave it always intact, and use r for iterating? One less chance for bugs.
You should initialize the node in the constructor:
p = new Node;
Because here:
while(p!= NULL)
{
q = p;
p = p->lp;
}
q->lp = new node;
p is initially NULL, so q will also be NULL, ergo q->lp leads to undefined behavior and a crash.
Either initialize p or re-think your logic.