I have created an Octree data structure but it's not perfect yet. I'm struggled with the copy constructor and the destructor.
Here is my header file:
class Octree
{
public:
static int lastbranch;
static bool utolsoelotti;
struct node
{
int value;
node *child[8];
};
Octree();
~Octree();
Octree(const Octree& oct);
void clear(node* node);
node* searchandset(int dec, int value);
node* search(int dec);
node* step(node *node, int k);
node* copy(node *n);
void Print(node *n)const;
void deletebranch(int branch);
node *root;
};
Constructor,destructor,copy contrsuctor
Octree::Octree()
{
root = new node;
root->value = 0;
for (int i = 0; i < 8; i++)
root->child[i] = 0;
}
Octree::~Octree()
{
clear(root);
}
Octree::Octree(const Octree& oct) {
root = copy(oct.root);
}
void Octree::clear(node *node){
for (int i = 0; i < 8; i++)
if (node->child[i])
clear(node->child[i]);
delete node;
}
Octree::node*Octree::copy(node *n) {
node* n2 = new node;
if (n) {
for (int i = 0; i < 8; i++) {
n2->child[i] = copy(n->child[i]);
}
}
return n2;
}
And here is how I created objects in the main:
int main() {
Octree tree;
Octree tree2(tree);
tree.searchandset(8, 2);
tree2.Print(tree2.search(8));
return 0;
}
In the searchandset function I'm giving a value for node number 8 at the first tree. After that I'm calling the copy constructor and print the 8th node of the second tree. The value is the same what I gave for the first tree, but when the desctructor called I always got this exception:
Exception thrown: read access violation.
node was 0xDDDDDDDD.
As I know it means that I tried to delete the nodes which I have already deleted. The object 'tree2' is a different object from 'tree' with the same values and nodes isn't it? Then I don't understand that exception above.
I'm new in c++ and I know it's basic stuff, so if somebody would direct me into the right direction I would very appreciate it.
The problem lies the in copy function. Let's go it through step by step:
node* n2 = new node;
if (n) {
for (int i = 0; i < 8; i++)
n2->child[i] = copy(n->child[i]);
}
return n2;
For an empty Octree oct, constructed with the default constructor, and copied to another Octree:
A new node is created, n2
n is the root of oct, so the condition if true
child[i] of n2 has a value of a copy of the corresponding child, so call copy again
A new node n2 is created
n is nullptr (because the children where all nullptr in oct), so don't execute condition
Return n2
Repeat steps 3 to 6 8 times
Return the root n2
Assign new pointer (n2) to root of the copied object
But wait! Did you notice that in step 6, you are returning a new pointer, even though the child is supposed to be nullptr!
That's the problem, because then, in clear, you will loop through each child. That's still ok, right? But then, you try to access the children, which are uninitialized (they have random value, the condition will evaluate to true), so you get a Read access violation, because it's not your memory.
So the solution? Only allocate memory for n2 if n is not nullptr.
Related
I am new to C++, and I'm sure this question must be easy to handle but I think I miss some main concepts. In the code, a Binary Tree is implemented, and we are asked to determine the number of nodes in that Binary Tree. As you can see in Node class, a Node has two member pointers: left that points to the Node at left, right that points to the Node at right. The input is the root node. Anyway, I found a solution like this:
Make a vector of Nodes, which I named nodes in the code, and append the root Node, and set the number of nodes (howManyNodes in the code) to 1.
While nodes is not empty (at the beginning we add the root node), check if it has a left and a right pointer. If they are available (i.e., not nullptr) , add those pointers to the vector of nodes (I use insert). At the same time, increase the number of nodes (howManyNodes in the code) by one.
After checking a if specific node has a left and right pointers, I remove that node from the list, for which I use pop_back() function.
At the end, the vector will be empty and I will obtain howManyNodes as the answer.
Here is the code, and I only implemented the count function. The rest is from the template.
#include <iostream>
#include <vector>
class Node {
public:
Node *left, *right;
Node() { left = right = nullptr; }
~Node() {
delete left;
left = nullptr;
delete right;
right = nullptr;
}
};
// I implement this part
int count(Node *n) {
int howManyNodes = 1;
std::vector<Node> *nodes = new std::vector<Node>;
nodes->push_back(*n);
while (!nodes->empty()){
Node* trial = new Node(nodes->back());
if (trial->left){
std::cout << trial->left << std::endl;
nodes->insert(nodes->begin(), *trial->left);
howManyNodes += 1;
}
if (trial->right){
std::cout << trial->right << std::endl;
nodes->insert(nodes->begin(), *trial->right);
howManyNodes += 1;
}
nodes->pop_back();
}
return howManyNodes;
}
// I implement this part.
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
// This should print a count of six nodes
std::cout << count(n) << std::endl;
// Deleting n is sufficient to delete the entire tree
// because this will trigger the recursively-defined
// destructor of the Node class.
delete n;
n = nullptr;
return 0;
}
The problem is, I can never get rid of segmentation fault. As I searched, it happens when the code is trying to access a memory it is not supposed to. My code might be easy to fix, but I have the following questions:
If std::vector uses heap memory for its members, why do I need to define a new vector here? In the main function (which I didn't write) everything is written by new, then I assumed I should also use new whenever possible but I don't understand the logic behind.
In my code, I want to use references, because I want to access only the pointers of Nodes and not modifying them - I learned that using the object itself requires making copies and slows down the process, so not preferable -. What part of my code is trying to modify any pointers?
Now that I defined a new vector, should I also delete it and make it equal to nullptr, before returning the value?
Thanks in advance!
The problem with your Node class is that it doesn't follow the rule of 3/5/0 and whenever you make a copy of a Node, and you are making a lot of copies, you have a trouble, because the left right nodes will be deleted once the copied object goes out of scope, and then again when you call delete yourself.
Short summary of bugs in your count implementation:
int count(Node *n) {
int howManyNodes = 1;
// doesn't make sense to allocate vector dynamically
// also you forgot to delete it
std::vector<Node> *nodes = new std::vector<Node>;
// keeping nodes as value will copy them, which leeds to double free
nodes->push_back(*n);
while (!nodes->empty()){
// another copy - thus another problem, but this time "only" a memory leak since you don't delete it
Node* trial = new Node(nodes->back());
if (trial->left){
std::cout << trial->left << std::endl;
// another copy
nodes->insert(nodes->begin(), *trial->left);
howManyNodes += 1;
}
if (trial->right){
std::cout << trial->right << std::endl;
// another copy
nodes->insert(nodes->begin(), *trial->right);
howManyNodes += 1;
}
nodes->pop_back();
}
return howManyNodes;
}
How it might be implemented without copying any objects:
void count(Node* n, int& numNodes)
{
numNodes++;
Node* left = n->left;
Node* right = n->right;
if (left) count(left, numNodes);
if (right) count(right, numNodes);
}
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
int numNodes{0};
count(n, numNodes);
std::cout << numNodes << std::endl;
delete n;
return 0;
}
It is a recursive approach, so maybe not the best. If you really need to implement some kind of a tree by hand, use std::unique_ptr, delete explicitly the copy constructor/assignment, and implement the method count inside your Tree class, if you plan to have sth like that.
As #pptaszni explained, in my code I was making copies of the instances and that was causing problems. The recursive approach suggested by #pptaszni is much easier and preferable, which I couldn't think of before. I also corrected my approach by passing pointers instead of values. This works:
#include <iostream>
#include <vector>
class Node {
public:
Node *left, *right;
Node() { left = right = nullptr; }
~Node() {
delete left;
left = nullptr;
delete right;
right = nullptr;
}
};
int count(Node *n) {
int howManyNodes = 1;
std::vector<Node*> nodes = {};
nodes.push_back(n);
while (!nodes.empty()){
Node* trial = nodes.back();
if (trial->left){
nodes.insert(nodes.begin(), trial->left);
howManyNodes += 1;
}
if (trial->right){
nodes.insert(nodes.begin(), trial->right);
howManyNodes += 1;
}
nodes.pop_back();
}
// Implement count() here.
return howManyNodes;
}
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
// This should print a count of six nodes
std::cout << count(n) << std::endl;
// Deleting n is sufficient to delete the entire tree
// because this will trigger the recursively-defined
// destructor of the Node class.
delete n;
n = nullptr;
return 0;
}
In this particular question I am trying to find the segmentation fault which is occurring. I know there is an error while creating a linked list of individual digits of K.
struct Node
{
int data;
struct Node *next;
};
int n1 = 0;
int n2 = 0;
int k = 1;
int calc(Node *h)
{
int sum=0;
for( ; h != NULL ; h = h->next)
sum=(sum*10)+h->data;
return sum;
}
Node* Lists(Node *headA, Node* headB)
{
n1 = calc(headA);
n2 = calc(headB);
int k = n1 + n2;
Node *temp;
temp->data=k%10;
while(k>0)
{
k=k/10;
Node *t1=new Node;
t1->data=k%10;
t1->next=temp;
temp=t1;
}
return temp;
}
The error resides in the following code:
Node *temp;
temp->data = k % 10;
Specifically, temp is declared to be a pointer that points to a Node struct, but it is never given a valid location in memory to point to, so it contains a garbage address that most likely refers to some non-writeable region.
If you wanted to fix this, you would first allocate a region for your variable, like this:
Node *temp = new Node;
and then mark it as released (by calling delete on it) later on when you were done with it. I would also recommend better formatting your code, as it's not exactly convenient to read from the viewer's perspective.
I'm trying to make a linked list inside another one, here is my code
template<typename T>
class List {
private:
int length;
class Node {
public:
T data;
Node* next;
} *head;
public:
List();
~List();
void insert(T item);
void remove(T item);
void empty();
T* getAll();
int count() const;
};
template<typename T>
void List<T>::insert(T item)
{
if (head == NULL)
{
head = new Node();
head->data = item;
head->next = NULL;
length = 1;
return;
}
Node* p = new Node();
p->data = item;
p->next = head;
head = p;
++length;
}
struct Remainder {
Date dt;
List<int> notes;
};
void getDayEvents(Date dt, List<Remainder> l)
{
Remainder* arr = new Remainder[l.count()];
arr = l.getAll();
for (int i = 0; i < l.count(); i++)
{
if (arr[i].dt.day == dt.day && arr[i].dt.month == dt.month && arr[i].dt.year == dt.year)
{
int* nArr = new int[arr[i].notes.count()];
nArr = arr[i].notes.getAll();
for (int j = 0; j < arr[i].notes.count(); j++)
{
cout << nArr[j] << endl;
}
}
}
}
int _tmain() {
Date dt1, dt2;
dt1.setDate(17, 7, 2015);
dt2.setDate(5, 11, 2015);
Remainder r1, r2;
r1.dt = dt1;
r1.notes.insert(1);
r1.notes.insert(2);
r2.dt = dt2;
r2.notes.insert(5);
List<Remainder> l;
l.insert(r1);
l.insert(r2);
getDayEvents(dt1, l);
//----------------------------------------------------------
int pause;
cin >> pause;
return 0;
}
just when insert r1 or r2 in the list, the data inside lists of notes inside each remainder just disappeared or destroyed
I don't know why? Where is the mistake?
You're passing your objects into the insert function by value.
That means copy constructors and destructors are being called.
You did not declare the copy constructor of List so it's presumably
being generated by the compiler, and presumably is making a
"shallow" copy of the List when the copy constructor
of Remainder makes copies of the members of the input Remainder.
I think this copies the pointer head from one List to another
so that now you have
two List objects whose head pointers point to the same object.
You haven't shown the definition of the List destructor,
but depending on what you do in that destructor it could be deleting
a Node that the destroyed List points to while another List
still has a pointer to the same Node.
That may sound confusing, and frankly I'm not sure I can count the
invocations of constructors and destructors correctly myself,
so best to just make sure they can never be used unsafely.
A good start might be to define your own copy constructor and
operator = for List in such a way that the new List has
newly-allocated copies of everything that was in the old List.
Never let two head pointers point to the same object.
After you've inserted one List into another, you should be able to
confirm in the debugger that the new copy of the List
I'm making a C++ Maze program using disjoint sets and the Union/Find operations.
I have a MakeSet(int x) function which creates a new Node for every integer element x in the maze. (i.e 1 to 16 for a 4x4 maze). Thus initially every element is in its own set.
My MakeSet looks like this
void Maze::MakeSet(int x)
{
Node *root = new Node;
root->label = x;
root->parent = NULL;
}
But in the CreateMaze() function I have to call MakeSet many times to get all the elements into their own set initially. Thus, the root will keep being overwritten. How do I dynamically allocate many different nodes? Should I keep them separated in an array?
You already allocate a new Node, you are just not keeping it. You need to change the function to return Node*, and store the results in an array or a vector.
Node* Maze::MakeSet(int x) {
Node *root = new Node;
root->label = x;
root->parent = NULL;
return root;
}
Node *nodes[16];
for (int i = 0 ; i != 16 ; i++) {
nodes[i] = Maze::MakeSet(i);
}
An even better approach would be to do Node initialization in a constructor:
Node::Node(int x) : label(x), parent(NULL) {
}
You can now create all sets in a loop, and store pointers to them in an array:
Node *nodes[16];
for (int i = 0 ; i != 16 ; i++) {
nodes[i] = new Node(i);
}
You can have a std::vector of Node objects or pointers as a class member:
class Maze
{
std::vector<Node> nodes;
};
or, if you must
class Maze
{
std::vector<Node*> nodes;
}
and add the created nodes:
void Maze::MakeSet(int x)
{
Node *root = new Node;
root->label = x;
root->parent = NULL;
nodes.push_back(root);
}
Note that you'll have to implement a destructor to clean up the memory when you're done. This also means you should have a copy constructor and assignment operator for Maze.
#include <iostream>
#include "Student.h"
#include "SortedList.h"
using namespace std;
#define BOUNDS 100
int main() {
SortedList *list = new SortedList(); // points to the sorted list object
Student *create[BOUNDS]; // array to hold 100 student objects
int num = 100000; // holds different ID numbers
// fills an array with 100 students of various ID numbers
for (int i = 0; i < BOUNDS; i++) {
create[i] = new Student(num);
num += 10;
}
// insert all students into the sorted list
for (int i = 0; i < BOUNDS; i++)
list->insert(create[i]);
// removes each student from the list
num = 100000;
for (int i = 0; i < BOUNDS; i++) {
list->remove(num);
num += 10;
}
delete list;
return 0;
}
I am getting a seg fault with the previous code. Any insight as to why this is or how to possibly fix it would be appreciated. The seg fault is definitely caused by the delete list; line
UPDATE 1: Here is my SortedList destructor
/*
* Destructs this sorted list object
*/
SortedList::~SortedList() {
freeList(head);
}
/*
* Traverses throught the linked list and deallocates each node
*/
void SortedList::freeList(Listnode *L) {
Listnode *tmp = L; //holds the node to be deleted
//traverses the list
while (tmp != NULL) {
Listnode *next = tmp->next; //holds the value of the next node
//delete previous node
delete tmp->student;
delete tmp->next;
delete tmp;
//sets the next node to the node to be deleted
tmp = next;
}
//delete header node
delete L;
}
Well, we can't see SortedList or Student, and I'd guess the problem is in one of those. I note that num never gets reset to its original value after the creation loop, which means that most of the remove calls are going to be passed an id that belongs to no Student; perhaps that case fails. Or perhaps there are simply bugs in the insert or remove methods -- or the constructor or destructor, for that matter. It's totally up in the air.
EDIT: As others have pointed out, that destructor uses a pointer after it's been deleted; that could be the only source of error, or there could easily be more in the code we haven't seen yet.
In freelist(), you delete tmp->next, then set tmp = tmp->next. Now tmp has an invalid pointer. You need to restructure your code so that you do not free a pointer before accessing its members.
Although I hate doing people's homework for them, here's my solution:
/*
* Traverses throught the linked list and deallocates each node
*/
void SortedList::freeList(Listnode *L) {
if(L == NULL) return;
freeList(L->next);
delete L->student;
delete L;
}
This use O(n) stack space for deletion, but I personally find it much clearer than a loop. Your solution can be tweaked to "just work" by removing the call to delete tmp->next.
// removes each student from the list
for (int i = 0; i < BOUNDS; i++) {
list->remove(num);
num += 10;
}
Looks interesting... how does this work exactly? If num is 100000 + BOUNDS*10 at this point in the code (since it is never changed after you add 10 to it for each student you create). Every remove call you make doesn't remove a student by their ID (since the id called is 100000 + BOUNDS*10 + i*10). Was the intent to remove them by ID, if so you should consider resetting num to 100000 before doing the remove loop.
To clarify how this could cause the seg-fault: if your remove function doesn't have proper bounds checking it could go out of the memory looking for the id to remove.
Updated with destructor question:
void SortedList::freeList(Listnode *L) {
Listnode *tmp = L; //holds the node to be deleted
//traverses the list
while (tmp != NULL) {
Listnode *next = tmp->next; //holds the value of the next node
//delete previous node
delete tmp->student;
delete tmp->next;
delete tmp;
//sets the next node to the node to be deleted
//**********
//Check here, you deleted next, but the assigned it to temp. Tmp isn't null, but
//it is however, no longer your memory (since you deleted it)
//**********
tmp = next;
}
//delete header node
delete L;
}