Debugging of segmentation fault is one of the key issues I am facing as beginner in C++. I had tried to implement Depth First Search in Directed Graphs using C++ STL in the following lines of code (based on Steven Skienna's Algorithm Design Manual) :
#include <iostream>
#include <list>
#include <cstdio>
using namespace std;
#define TREE 0 /* tree edge */
#define BACK 1 /* back edge */
#define CROSS 2 /* cross edge */
#define FORWARD 3 /* forward edge */
class Graph
{
int V; //no of vertices
int time;
list <int> *adj; //Pointer to an array containeing the adjacency list
public:
Graph(int V); //A constructor
int entry_time[] ,exit_time[] , parent[] ;
bool processed[] , discovered[] ;
void addEdge(int v , int w ) ; // a function to add an edge to graph
void DFS(int v); // print DFS transversal of the complete graph
void initializeGraph () ; // a function used by DFS
void process_edge(int x , int y);
void process_vertex_early(int x);
void process_vertex_late(int x);
int edge_classification(int x , int y);
};
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V]; // dynamic allocation of V lists to an array named adj
}
void Graph::addEdge(int v, int w )
{
adj[v].push_back(w); //Add w to v's list
}
void Graph::initializeGraph ()
{
time = 0;
for (int j=0;j<V;j++)
{
processed[j]=discovered[j] = false;
parent[j]=-1;
}
// Recur for all the vertices adjacent to this vertex
}
void Graph::DFS(int v)
{
process_vertex_early(v);
list <int>::iterator i ;
for (i=(adj[v].begin());i!=adj[v].end();++i)
{ cout << *i ;
if (discovered[*i]==false)
{
parent[*i] = v ;
process_edge(v,*i);
DFS(*i);
}
else if (processed[*i]==false)
process_edge(v,*i);
}
process_vertex_late(v);
}
void Graph::process_vertex_early(int v)
{
discovered[v] = true;
time = time +1 ;
entry_time[v] = time ;
printf("discovered vertex %d at time %d\n",v, entry_time[v]);
}
void Graph::process_vertex_late(int v)
{
time = time + 1 ;
exit_time[v] = time;
processed[v] = true;
//printf("processed vertex %d at time %d\n",v, exit_time[v]);
}
int Graph::edge_classification (int x , int y )
{
if (parent[y]==x) return (TREE);
if (discovered[y] && !processed[y]) return (BACK);
//cout << " Warning : self loop " << x << y ;
}
void Graph::process_edge(int x , int y)
{
int type ;
type = edge_classification(x,y);
//if (type== BACK) cout << "Back Edge" << x << " -> " << y << endl;
//else if (type== TREE) cout << "Tree Edge" << x << " -> " << y << endl;
//else cout << " Not in the type " ;
}
int main()
{
Graph g(4);
g.initializeGraph();
g.addEdge(0,1);
g.addEdge(0,2);
g.addEdge(1,2);
g.addEdge(2,0);
g.addEdge(2,3);
g.addEdge(3,1);
cout << " Following is a DFS transversal \n " ;
g.DFS(0);
return 0;
}
Segmentation Fault occurs after the search operation reaches a depth of one or two. I had tried applying Breadth First Search using a similar syntax which worked . Please help me in debugging this code . Thanks .
Step one is to read all compiler warnings (and compile with warnings switched on).
For example:
int entry_time[] ,exit_time[] , parent[] ;
These arrays are defined with no size - but you are putting data in them. This means you are writing outside the array boundaries which cause Undefined Behavior (such as the crashing and double-frees you are seeing). Either allocate space for these arrays like you do for adj, or use another container (such as vector) which you can resize as needed.
Also edge_classification doesn't always return a value - your compiler should have warned you about this.
Edit: More about std::vector
You can't declare your arrays to be as entry_time[V] because the value of V isn't known at compile time. You could have many different Graph objects with different sizes.
If you change your arrays to std::vector you can then allocate their size in the Graph constructor and let the std::vector class worry about allocating and freeing memory.
For example:
In the class declare entry_time as a std:vector:
std::vector<int> entry_time;
In the constructor, set the size of the entry_time vector.
entry_time.resize(V);
Note that you can use V here as a parameter to resize as this is at run-time so it now has a value.
std::vector has a normal array-like accessor, so you can assign values into the entries of the vector as you would an array. For example, your existing code will still work:
entry_time[v] = time ;
Related
I have made a class Graph when I have made a pointer in private access in the class named visited. In the constructor I have initialized the array with zero at all places but when I am checking if all the values are zero in another method ,It is showing garbage values in the array but when I print it in the constructor itself then it showing all zeroes.
#include<iostream>
#include<vector>
#include<list>
using namespace std;
class Graph {
private:
int vertices,edges;
vector <list<int>> graph;
vector <int> vs;
int *visited;
public:
Graph (int vertices)
{
this->vertices = vertices;
list <int>l;
for (size_t i = 0; i < vertices; i++) {
graph.push_back(l);
vs.push_back(i);
}
edges=0;
// ####### made a new array, initialized all values with zeroes and assigned it to the instance variable visited #########
int a[vertices]={0};
this->visited = a;
// ######## in the constructor it is showing correct values below #######
for (size_t i = 0; i < vertices; i++) {
std::cout << this->visited[i] << ' ';
}
std::cout << '\n';
}
virtual ~Graph ()
{
}
void showList()
{
// just printing the graph in the form of adjacency list
// it is working fine
for (size_t i = 0; i < vertices; i++)
{
list <int>::iterator p = graph[i].begin();
std::cout << i ;
for (; p != graph[i].end() ; p++)
{
std::cout << " -> " << *p ;
}
std::cout << " -> NULL" << '\n';
}
// ######## when I am checking the values here then it is printing garbage values
for (size_t i = 0; i < this->vertices; i++) {
std::cout << this->visited[i] << ' ';
}
}
void addEdge(int source, int destination)
{
graph[source].push_back(destination);
}
};
int main()
{
Graph g(6);
g.addEdge(0,1);
g.addEdge(0,2);
g.addEdge(1,0);
g.addEdge(1,3);
g.addEdge(1,4);
g.addEdge(2,0);
g.addEdge(2,4);
g.showList();
return 0;
}
when i call the showList method the it should print the adjacenct list and all zeroes(contents of array named visited)
I have made a class Graph.
Yes you have.
class Graph {
I have made a pointer in private access in the class named visited.
Yes you have.
private:
int *visited;
In the constructor I have initialized the array with zero at all places.
Yes you have.
int a[vertices]={0};
But I would note that this is a variable that is local to the constructor. It is not visible to any other methods. Also when the constructor finishes the lifespan of this object ends. Any attempt to accesses this array after its lifetime ends is undefined behavior. So accessing this by some sneaky mechanism (like savings its address in a pointer) is going to cause bad things to happen.
Here you are doing something sneaky (and very bad):
this->visited = a;
but when I am checking if all the values are zero in another method
You are accessing the array through a pointer visited. This is pointing to an array that no longer exists because the array is local to another function.
std::cout << this->visited[i] << ' '; // This is broken code.
It is showing garbage values in the array
You are very unlucky. It would have been nicer if the program had crashed and made it more obvious that something bad is happening. Unfortunately you have discovered that undefined behavior can do anything (including simply return some random values).
but when I print it in the constructor itself then it showing all zeroes.
It is still valid in the constructor so accessing it is not a problem.
So what is the solution.
In general pointers you should avoid pointers (especially when brand new). You need to get some basic concepts down first.
In this case simply replace:
int* visited;
With
std::vector<int> visited;
In the constructor fill this with the appropriate zero values.
I'm building a sparse matrix class that holds two arrays (row and column) of pointers to doubly linked lists (down and right). Sort of like this:
rows
c0123456789
o1
l2
u3
m4 A-->B-->
n5 | |
s6 | V
7 V D-->
8 C-->
9
Both arrays are initialized to have nullptr in every space until something is inserted in that place.
I have a function "readFile" that reads in objects from a text file and inserts them into this sparse matrix. For some reason, before this function returns, all of the data in it is fine, but after I return, I get random memory locations in my arrays. Here is main.cpp
#include <iostream>
#include <string>
#include <fstream>
#include "sparseMatrix.h"
using namespace std;
class basic
{
private:
int x, y;
string word;
basic *down;
basic *right;
public:
basic(int x, int y, string word)
{
this->x = x;
this->y = y;
this->word = word;
down = nullptr;
right = nullptr;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
basic *getRight()
{
return right;
}
void setRight(basic *newRight)
{
right = newRight;
}
basic *getDown()
{
return down;
}
void setDown(basic *newDown)
{
down = newDown;
}
void print()
{
cout << "X: " << x << ", Y: " << y << ", word: " << word << ".\n";
}
};
sparseMatrix<basic> readFileBROKEN(string pathToFile);
sparseMatrix<basic> *readFile(string pathToFile);
int main()
{
cout << "Working:\n\n";
sparseMatrix<basic> *workingMatrix = readFile("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
workingMatrix->printyArray();
cin.get();
cout << "Not working:\n\n";
sparseMatrix<basic> brokenMatrix = readFileBROKEN("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
brokenMatrix.printyArray();
cin.get();
delete workingMatrix;
}
sparseMatrix<basic> readFileBROKEN(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> matrix(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix.insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix.printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
sparseMatrix<basic> *readFile(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> *matrix = new sparseMatrix<basic>(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix->insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix->printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
and here is sparseMatrix.h:
template <class dataType>
class sparseMatrix
{
private:
//The dimensions of the sparse matrix.
int width;
int height;
//Dynamic array of pointers to heads of linked lists.
dataType** xArray;
dataType** yArray;
public:
//Constructor. Sets everything in the two arrays to nullptr.
sparseMatrix(int height, int width)
{
this->width = width;
this->height = height;
xArray = new dataType*[width];
yArray = new dataType*[height];
for (int row = 0; row < height; row++)
{
this->yArray[row] = nullptr;
}
for (int col = 0; col < width; col++)
{
this->xArray[col] = nullptr;
}
}
//Deconstructor. First goes through the matrix and looks for every city it can find, and deletes
//all of those. Then when it's done, it deletes the two dynamic arrays.
~sparseMatrix()
{
dataType *currentdataType;
dataType *next;
for (int row = 0; row < height; row++)
{
currentdataType = yArray[row];
while (currentdataType != nullptr)
{
next = currentdataType->getRight();
delete currentdataType;
currentdataType = next;
}
}
delete [] yArray;
delete [] xArray;
}
//Creates a copy of the data we are passed, then creates links to this copy.
void insert(dataType data)
{
//Make sure the data is valid.
if (data.getX() < 0 || data.getX() >= width || data.getY() < 0 || data.getY() >= height)
{
std::cout << "That dataType doesn't fit into the sparse matrix!\n";
data.print();
std::cin.get();
}
else
{
//Copy the data we were passed.
dataType *newData = new dataType(data);
//Easy case. If nothing is in this row, set yArray[row] to the address of this data.
if (yArray[data.getY()] == nullptr)
{
yArray[data.getY()] = newData;
}
//Not so easy case. Move forward (right) until we find the right location, then set links.
else
{
dataType *current = yArray[data.getY()];
while (current->getRight() != nullptr)
{
current = current->getRight();
}
current->setRight(newData);
}
//Easy case. If nothing is in this col, set xArray[col] to the address of this data.
if (xArray[data.getX()] == nullptr)
{
xArray[data.getX()] = newData;
}
//Not so easy case. Move forward (down) until we find the right location, then set links.
else
{
dataType *current = xArray[data.getX()];
while (current->getDown() != nullptr)
{
current = current->getDown();
}
current->setDown(newData);
}
}
}
void printyArray()
{
for (int r = 0; r < height; r++)
{
if (yArray[r] != nullptr)
{
std::cout << r << ' ';
//yArray[r]->print();
}
}
}
};
readFile reads everything in from a file that looks like this:
0 0 hello
5 2 world
6 8 foo
9 5 bar
...
As expected, before returning, the only locations that are NOT nullptr are the ones that I have inserted into. (0, 2, 8 and 5). However when the function returns, EVERY SINGLE location in the array is not nullptr. I added a second function which returns a pointer to dynamically allocated sparseMatrix object, rather then returning the object itself, and this fixed it. However, I don't understand why. It seems like these two functions should behave identically the same way.
Also, the part that is most confusing to me, why does this run perfectly fine in Xcode, but not in Visual Studio?
tomse's answer is correct and gives the why and a fix, but it's an unnecessarily expensive fix for this problem. His suggestion of the copy constructor also solves numerous future problems such as the classics Why did my vector eat my data? and Dude, where's my segfault? Make the copy constructor. Don't use it unless you have to.
I think Andras Fekete got the problem right, but his post is kind of garbled. His solution is bang on, though.
Define your function like this:
bool readFile(string pathToFile, sparseMatrix<basic> & matrix)
Remove the definition of matrix inside the function in favour of the one passed in.
Return false on error so you know the matrix is bad (or use exceptions).
Create the matrix in the calling function and pass it into the revised reader function.
sparseMatrix<basic> matrix(100, 100);
if readFile("C:/users/jmhjr/desktop/testdata.txt", matrix);
That puts you right back where you were with the pointer version, but without the pointer and without having to do the extra work of copying data you didn't need to copy.
Your function:
sparseMatrix<basic> readFileBROKEN(string pathToFile)
returns a copy of the object (which is OK), but sparseMatrix does not define a copy constructor, so the default generated will be used which creates a shallow copy by just copying the adresses inside the returned object.
But the memory where the address points to is deleted when you leave your function (because the destructor of the locally created object is called).
To solve this you have to define your own copy contructor in sparseMatrix which copies all the content of the object.
sparseMatrix(const sparseMatrix& rhs) :
width(rhs.width),
height(rhs.height),
xArray(nullptr),
yArray(nullptr)
{
... and now copy all the content from rhs.xArray to this->xArray,
(same for yArray)
}
The problem is that you're allocating 'matrix' inside both of the readFile functions. Upon returning from the function, both variables are deallocated. However, returning the value (eradFile) the matrix is copied into your variable of the calling function, whereas returning the pointer (readFileBROKEN) is just returning the address where the matrix used to be stored.
To fix this, you should allocate the 'matrix' variable, and pass in a reference to the function. Then the function can return a void while stuffing the matrix properly.
Been wracking my mind all day trying to hammer out the underlying data structures for a challenge assignment in one of my programming classes.
The problem is as follows:
Given an assortment of objects (each of which includes an identifier and a weight) and a supply of containers (which have a fixed weight capacity), pack all the items using as few containers as possible without overloading any of them.
I have the logic aspects hammered out using a hodgepodge of arrays, but the dynamic nature of this assignment has me wanting to optimize things by using vectors and/or linked lists.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <math.h>
#include <time.h>
#include <conio.h>
#include <vector>
#include <algorithm>
using namespace std;
struct Item
{
int number;
double weight;
bool operator < (const Item& str) const
{
return (weight < str.weight);
}
};
class Node
{
int number;
double weight;
Node* next;
public:
Node()
{};
void SetID(int iNum)
{
number = iNum;
};
void SetWeight(double iWeight)
{
weight = iWeight;
};
void SetNext(Node* iNext)
{
next = iNext;
}
int GetID()
{
return number;
};
double GetWeight()
{
return weight;
};
Node* Next()
{
return next;
};
};
class List
{
Node* head;
double weight;
public:
List()
{
head = NULL;
weight = 0;
};
int Size()
{
Node* tmp;
int count = 0;
for (tmp = head; tmp != NULL; tmp = tmp->Next())
{
count++;
}
return count;
};
double Weight()
{
return weight;
};
void Print()
{
Node *tmp = head;
if ( tmp == NULL )
{
cout << " E M P T Y" << endl;
return;
}
do
{
cout << setw(8) << tmp->GetID() << " | " << setw(8) << tmp->GetWeight() << endl;
tmp = tmp->Next();
} while ( tmp != NULL );
};
void Append(int iNum, double iWeight)
{
Node* newNode = new Node();
newNode->SetID(iNum);
newNode->SetWeight(iWeight);
newNode->SetNext(NULL);
Node *tmp = head;
if ( tmp != NULL )
{
while ( tmp->Next() != NULL )
{
tmp = tmp->Next();
}
tmp->SetNext(newNode);
}
else
{
head = newNode;
}
weight += iWeight;
};
};
double ItemWeights(vector<Item> iVect)
{
double total = 0;
for(int i = 0; i < iVect.size(); i++)
{
total += iVect[i].weight;
}
return total;
}
int main()
{
const double MAX_WEIGHT = 20;
vector< Item > source;
//
// Segment of code which propagates the vector data
// works fine, but is excluded for the sake of brevity
//
double totalWeight = ItemWeights(source);
// Duplicate vector of items
vector< Item > items(source);
for(int i = 0; i < items.size(); i++)
{
cout << setw(8) << items[i].number << setw(8) << items[i].weight << endl;
}
cout << "\n Total weight = " << totalWeight << endl;
cout << "\n\n Press any key to continue... ";
getch();
// Solution A-Original
// vector< vector< Item > > boxesAO( vector< Item >);
// boxesAO[0].push_back({items[items.size()].number, items[items.size()].weight});
vector< List > boxesAO;
// boxesAO[0].Append(items[items.size()].number, items[items.size()].weight);
return 0;
}
I've left some of the methods I've tried in the code (commented out) - none of which worked. As I mentioned above, I've got it working with arrays of linked lists and with 2D arrays, but the vast range of potential input makes these problematic at best. Either a bunch of empty lists taking up space or, worse, not having enough.
I'm thinking that vector< List > is my best option, but I can't figure out how I'm supposed to access any of the List functionality.
If someone would be so helpful as to offer a suggestion for how to create a "dynamic 2D array" as well as a code example of how to access it, I would be most greatly appreciative. My deepest thanks in advance.
EDIT:
#jaredad7 ~ That's what I've been trying, but it keeps causing the program to crash.
List box;
box.Append(items[items.size()].number, items[items.size()].weight);
This works just fine - no problems whatsoever.
The earlier code propagates a 1D vector of Item structs, which also works properly.
vector< List > boxes;
boxes[0].Append(items[items.size()].number, items[items.size()].weight);
This compiles fine but crashes during execution, no matter what index is used. (I'm also using couts for debugging, and the issue most definitely lies with trying to access the List functions.)
I'm suspecting that .push_back or somesuch may be needed, but I haven't been able to find much information concerning vectors of List objects.
If you can, my first suggestion would be to go with the vector (if that is allowed). As for accessing functions/attributes of a member of a vector, it's done the same way as an array, that is:
vectorname[i].functionname(a,b,c);
The best way to do this without vectors would be to use your nodes as the item container (a struct), and handle node-creation, deletion, etc. in your list class. Then, you would only really need one container for as many objects of one type as you need. You can make the type dynamic (although it appears you only need doubles for this project) by adding a class template (use google if you are unfamiliar with templates in C++). This will allow your user to make a container for each type of data (much like a vector).
last week i posted a code to calculate the shortest path in a graph using Dijkastra algorithm but it was very long and nobody was interesting in reading it completely so i deleted it, and now i am trying to simplify the code by going part by part, the code isn't complete yet, and i will cut part of the code here to focus on the first problem that i am facing so far.
briefly i have a class Graph it is going to be constructed by two other classes a vector of elements are instances of a class Edge , and another vector of elements of class Vertex , every vertex has an id , and every edge has two vertices and weight .
class Graph has a method its name is shortest takes two vertices as arguments the first one for the source of the graph and the second is for the destination.
So far i am trying to eliminate the edges that are connected to the source vertex , but i am getting an extra edge still in the vector edges it is connected to the source while all the other edges related to the source are removed.
to demonstrate the result , i initialized a graph has five vertices vers[0], vers[1], vers[2], vers[3], vers[4], and there are 10 edges connecting those vertices starting from eds[0], eds[1], ....eds[9].
the source vertex is vers[2] is connected by 4 edges , so when applying the method shortest as it is shown in the code below i should get rid of all those 4 edges and end with 6 edges , but the result was that i got rid of 3 edges and i have 7 edges remained, the result is as follows:
Hello, This is a graph
0____1 5
0____3 4
0____4 6
1____3 5
1____4 7
2____4 8
3____4 3
size of edges 7
size of vertices 5
as you can notice , there still an edge connected to the source which is 2 , the problem is in this edge (by the way 8 is the weight of the edge)
2____4 8
there is something wrong in the method shortest and specifically in the for loop , i hope you can help in finding my mistake.
Thanks in advance.
Here is the code
#include<iostream>
#include<vector>
#include <stdlib.h> // for rand()
using namespace std;
const unsigned int N = 5;
class Vertex
{
private:
unsigned int id; // the name of the vertex
public:
unsigned int get_id(){return id;};
void set_id(unsigned int value) {id = value;};
Vertex(unsigned int init_val = 0) :id (init_val){} // constructor
~Vertex() {}; // destructor
};
class Edge
{
private:
Vertex first_vertex; // a vertex on one side of the edge
Vertex second_vertex; // a vertex on the other side of the edge
unsigned int weight; // the value of the edge ( or its weight )
public:
unsigned int get_weight() {return weight;};
void set_weight(unsigned int value) {weight = value;};
Vertex get_ver_1(){return first_vertex;};
Vertex get_ver_2(){return second_vertex;};
void set_first_vertex(Vertex v1) {first_vertex = v1;};
void set_second_vertex(Vertex v2) {second_vertex = v2;};
Edge(const Vertex& vertex_1 = 0, const Vertex& vertex_2 = 0, unsigned int init_weight = 0)
: first_vertex(vertex_1), second_vertex(vertex_2), weight(init_weight)
{
}
~Edge() {} ; // destructor
};
class Graph
{
private:
std::vector<Vertex> vertices;
std::vector<Edge> edges;
public:
Graph(vector<Vertex> ver_vector, vector<Edge> edg_vector)
: vertices(ver_vector), edges(edg_vector){}
~Graph() {}
vector<Vertex> get_vertices(){return vertices;}
vector<Edge> get_edges(){return edges;}
void set_vertices(vector<Vertex> vector_value) {vertices = vector_value;}
void set_edges(vector<Edge> vector_ed_value) {edges = vector_ed_value;}
unsigned int shortest(Vertex src, Vertex dis);
};
unsigned int Graph::shortest(Vertex src, Vertex dis) {
vector<Vertex> ver_out;
vector<Edge> track;
for(unsigned int i = 0; i < edges.size(); ++i)
{
if((edges[i].get_ver_1().get_id() == src.get_id()) || (edges[i].get_ver_2().get_id() == src.get_id()))
{
track.push_back (edges[i]);
if(edges[i].get_ver_1().get_id() == src.get_id())
{ver_out.push_back (edges[i].get_ver_1());}
else
{ver_out.push_back (edges[i].get_ver_2());}
edges.erase(edges.begin() + i ); //****
}
};
}
int main()
{
cout<< "Hello, This is a graph"<< endl;
vector<Vertex> vers(5);
vers[0].set_id(0);
vers[1].set_id(1);
vers[2].set_id(2);
vers[3].set_id(3);
vers[4].set_id(4);
vector<Edge> eds(10);
eds[0].set_first_vertex(vers[0]);
eds[0].set_second_vertex(vers[1]);
eds[0].set_weight(5);
eds[1].set_first_vertex(vers[0]);
eds[1].set_second_vertex(vers[2]);
eds[1].set_weight(9);
eds[2].set_first_vertex(vers[0]);
eds[2].set_second_vertex(vers[3]);
eds[2].set_weight(4);
eds[3].set_first_vertex(vers[0]);
eds[3].set_second_vertex(vers[4]);
eds[3].set_weight(6);
eds[4].set_first_vertex(vers[1]);
eds[4].set_second_vertex(vers[2]);
eds[4].set_weight(2);
eds[5].set_first_vertex(vers[1]);
eds[5].set_second_vertex(vers[3]);
eds[5].set_weight(5);
eds[6].set_first_vertex(vers[1]);
eds[6].set_second_vertex(vers[4]);
eds[6].set_weight(7);
eds[7].set_first_vertex(vers[2]);
eds[7].set_second_vertex(vers[3]);
eds[7].set_weight(1);
eds[8].set_first_vertex(vers[2]);
eds[8].set_second_vertex(vers[4]);
eds[8].set_weight(8);
eds[9].set_first_vertex(vers[3]);
eds[9].set_second_vertex(vers[4]);
eds[9].set_weight(3);
unsigned int path;
Graph graf(vers, eds);
path = graf.shortest(vers[2], vers[4]);
cout<<graf.get_edges()[0].get_ver_1().get_id() <<"____"<<graf.get_edges()[0].get_ver_2().get_id() <<" "<<graf.get_edges()[0].get_weight()<< endl; //test
cout<<graf.get_edges()[1].get_ver_1().get_id() <<"____"<<graf.get_edges()[1].get_ver_2().get_id() <<" "<<graf.get_edges()[1].get_weight()<< endl; //test
cout<<graf.get_edges()[2].get_ver_1().get_id() <<"____"<<graf.get_edges()[2].get_ver_2().get_id() <<" "<<graf.get_edges()[2].get_weight()<< endl; //test
cout<<graf.get_edges()[3].get_ver_1().get_id() <<"____"<<graf.get_edges()[3].get_ver_2().get_id() <<" "<<graf.get_edges()[3].get_weight()<< endl; //test
cout<<graf.get_edges()[4].get_ver_1().get_id() <<"____"<<graf.get_edges()[4].get_ver_2().get_id() <<" "<<graf.get_edges()[4].get_weight()<< endl; //test
cout<<graf.get_edges()[5].get_ver_1().get_id() <<"____"<<graf.get_edges()[5].get_ver_2().get_id() <<" "<<graf.get_edges()[5].get_weight()<< endl; //test
cout<<graf.get_edges()[6].get_ver_1().get_id() <<"____"<<graf.get_edges()[6].get_ver_2().get_id() <<" "<<graf.get_edges()[6].get_weight()<< endl; //test
//cout<<graf.get_edges()[7].get_ver_1().get_id() <<"____"<<graf.get_edges()[7].get_ver_2().get_id() <<" "<<graf.get_edges()[7].get_weight()<< endl; //test
//cout<<graf.get_edges()[8].get_ver_1().get_id() <<"____"<<graf.get_edges()[8].get_ver_2().get_id() <<" "<<graf.get_edges()[8].get_weight()<< endl; //test
//cout<<graf.get_edges()[9].get_ver_1().get_id() <<"____"<<graf.get_edges()[9].get_ver_2().get_id() <<" "<<graf.get_edges()[9].get_weight()<< endl; //test
cout<<"size of edges"<<graf.get_edges().size()<< endl;
cout<<"size of vertices"<<graf.get_vertices().size()<< endl;
return 0;
}
This is because you are effectively skipping some vector elements in your Graph::shortest for loop because you are incrementing i even when you erase current element. Change it to something like this to fix the problem:
for (unsigned int i = 0; i < edges.size();) { // no ++i here
if ((edges[i].get_ver_1().get_id() == src.get_id()) || (edges[i].get_ver_2().get_id() == src.get_id())) {
track.push_back(edges[i]);
if (edges[i].get_ver_1().get_id() == src.get_id()) {
ver_out.push_back(edges[i].get_ver_1());
} else {
ver_out.push_back(edges[i].get_ver_2());
}
edges.erase(edges.begin() + i);
} else {
++i; // increment only if not erasing
}
}
Alternatively as per comment, using iterators:
for (auto i = edges.begin(); i != edges.end();) {
if ((i->get_ver_1().get_id() == src.get_id()) || (i->get_ver_2().get_id() == src.get_id())) {
track.push_back(*i);
if (i->get_ver_1().get_id() == src.get_id()) {
ver_out.push_back(i->get_ver_1());
} else {
ver_out.push_back(i->get_ver_2());
}
i = edges.erase(i);
} else {
i++;
}
}
You are also missing a return statement in that function.
I wrote a simple program to test STL list performance against a simple C list-like data structure. It shows bad performance at "push_back()" line. Any comments on it?
$ ./test2
Build the type list : time consumed -> 0.311465
Iterate over all items: time consumed -> 0.00898
Build the simple C List: time consumed -> 0.020275
Iterate over all items: time consumed -> 0.008755
The source code is:
#include <stdexcept>
#include "high_resolution_timer.hpp"
#include <list>
#include <algorithm>
#include <iostream>
#define TESTNUM 1000000
/* The test struct */
struct MyType {
int num;
};
/*
* C++ STL::list Test
*/
typedef struct MyType* mytype_t;
void myfunction(MyType t) {
}
int test_stl_list()
{
std::list<mytype_t> mylist;
util::high_resolution_timer t;
/*
* Build the type list
*/
t.restart();
for(int i = 0; i < TESTNUM; i++) {
mytype_t aItem;
aItem->num = i;
mylist.push_back(aItem);
}
std::cout << " Build the type list : time consumed -> " << t.elapsed() << std::endl;
/*
* Iterate over all item
*/
t.restart();
std::for_each(mylist.begin(), mylist.end(), myfunction);
std::cout << " Iterate over all items: time consumed -> " << t.elapsed() << std::endl;
return 0;
}
/*
* a simple C list
*/
struct MyCList;
struct MyCList{
struct MyType m;
struct MyCList* p_next;
};
int test_simple_c_list()
{
struct MyCList* p_list_head = NULL;
util::high_resolution_timer t;
/*
* Build it
*/
t.restart();
struct MyCList* p_new_item = NULL;
for(int i = 0; i < TESTNUM; i++) {
p_new_item = (struct MyCList*) malloc(sizeof(struct MyCList));
if(p_new_item == NULL) {
printf("ERROR : while malloc\n");
return -1;
}
p_new_item->m.num = i;
p_new_item->p_next = p_list_head;
p_list_head = p_new_item;
}
std::cout << " Build the simple C List: time consumed -> " << t.elapsed() << std::endl;
/*
* Iterate all items
*/
t.restart();
p_new_item = p_list_head;
while(p_new_item->p_next != NULL) {
p_new_item = p_new_item->p_next;
}
std::cout << " Iterate over all items: time consumed -> " << t.elapsed() << std::endl;
return 0;
}
int main(int argc, char** argv)
{
if(test_stl_list() != 0) {
printf("ERROR: error at testcase1\n");
return -1;
}
if(test_simple_c_list() != 0) {
printf("ERROR: error at testcase2\n");
return -1;
}
return 0;
}
Oops, Yes.
I modified the code, and it show:
$ ./test2
Build the type list : time consumed -> 0.163724
Iterate over all items: time consumed -> 0.005427
Build the simple C List: time consumed -> 0.018797
Iterate over all items: time consumed -> 0.004778
So, my question is, why my "push_back" code got bad performance?
Well one thing is that in C, you have a linked list of objects but in C++, you have a linked list of pointers (so for one thing, you are doing twice as many allocations). To compare apples to apples, your STL code should be:
int test_stl_list()
{
std::list<MyType> mylist;
util::high_resolution_timer t;
/*
* Build the type list
*/
t.restart();
for(int i = 0; i < TESTNUM; i++) {
MyItem aItem;
aItem.num = i;
mylist.push_back(aItem);
}
std::cout << " Build the type list : time consumed -> " << t.elapsed() << std::endl;
return 0;
}
Your STL codes create a memory piece twice for each cell.
The following is from STL 4.1.1 on x86_64
void push_back(const value_type& __x)
{
this->_M_insert(end(), __x);
}
// Inserts new element at position given and with value given.
void _M_insert(iterator __position, const value_type& __x)
{
_Node* __tmp = _M_create_node(__x); // Allocate a new space ####
__tmp->hook(__position._M_node);
}
As you can see, also, push_back() function calls several more functions before returning to the caller, and
few pointer-value copying occurs everytime one of the functions is called.
Might be neligible because all the parameters are passed by const-reference though.
First, it looks like you're doing a push_front, not a push_back (in your own implementation, that is).
Second, you should also compare std::slist for a fair comparison as the std::list is double-linked.
Third, you need to use right compiler flags for a fair comparison. With gcc you should at least compile with -O2. Without optimization, STL always sucks because no inlining is done and there is lots of function call overhead.
It would seem your high_resolution_timer class is measuring more than just the routines you are trying to measure. I would refactor the code such that the only code between t.restart() and t.elapsed() is what you are keen on measuring. All other code therein could have unknown performance implications that could skew your results.