I call this code in main()
for (COwnerList l=b1.ListOwners(10); !l.AtEnd(); l.Next())
cout << l.Surname() << ", " << l.Name () << endl;
for (CCarList l=b1.ListCars("Peter","Smith"); !l.AtEnd(); l.Next ())
cout << l.RZ () << ", " << l.VIN() << endl;
for (COwnerList l=b1.ListOwners(10); !l.AtEnd(); l.Next())
cout << l.Surname() << ", " << l.Name() << endl;
I tried to debug and found out seg fault comes from Constructor of COwnerList
COwnerList CRegister::ListOwners (unsigned int vin) const
{
vector<Car>::const_iterator it;
COwnerList tmp;
it = lower_bound(byVINList.begin(), byVINList.end(), Car("",vin), cmpVIN);
if(it != byVINList.end())
tmp.car = &(*it);
tmp.in = it->owners.end() - it->owners.begin();
return tmp;
}
constructor im calling looks like this:
COwnerList::COwnerList(void)
{
here = car->owners.begin();
i = 0;
in = car->owners.end() - car->owners.begin();
}
interesting is it doesnt crash after 1st for in main(), so there must be something wrong in the code between those 2 for cycles in main(), but i have no idea what it could be i am not modifying anything related to ListOwners() there
EDIT1
Car constructor:
Car::Car( const string & pid,
const unsigned int & pvin = 0,
const string & cname = "",
const string & csurname = "")
{
rz = pid;
VIN = pvin;
name = cname;
surname = csurname;
}
EDIT2
class COwnerList
{
public:
COwnerList(void);
string Name ( void ) const;
string Surname ( void ) const;
bool AtEnd ( void ) const;
void Next ( void );
//vector<pair<string, string> > powners;
const Car *car;
int in;
private:
vector<pair<string, string> >::const_iterator here;
int i;
};
The problem is that the car pointer is not initialized during the call in the COwnerList constructor. In the first loop, you might have got lucky. Things like this happens all the time. Sometimes the OS won't throw a seg fault everytime when you are calling a code which is not allocated yet.
Just put a condition guard in your code like this:
if (car != NULL) {
here = car->owners.begin();
i = 0;
in = car->owners.end() - car->owners.begin();
}
The error is more likely you are modifying the vector after saving that tmp.toto pointer to the vector's internal storage.
Note that when you do tmp.car = &(*it) you are making a pointer towards the internal storage of the vector.
If later you call push_back() on the vector, you cannot rely in the pointers that you had before, because the vector can reallocate its contents in other memory addresses after you call the push_* methods.
Also note that a debugger may not tell the exact line that has to be corrected, even if the crash happens there. The debugger may tell you the first line where the problem is evident, but the cause of the problem may have happened several lines before.
Related
I'm having a problem that I haven't found an answer for in a week now. I have a dynamic array class and it has a method to add string values to it. It's supposed to represent an inventory you can add items to. However, I find that the changes made in the method to the private values of the class element aren't "updated" when I try to call for a print-method for the class element "backpack" later in the main(). I think this might be a problem due to referencing issues, but I've seen this work when a class hasn't been in a different module.
My "backpack" module print and add methods:
const int INITIAL_SIZE = 5;
Inventory::Inventory():
array_(new string[INITIAL_SIZE]),
max_space_(INITIAL_SIZE),
used_space_(0) {}
void Inventory::add(string item){
if ( size() == max_space_ ) {
string* new_array = new string[2 * max_space_];
for ( int i = 0; i < size(); ++i ) {
new_array[i] = array_[i];
}
delete [] array_;
array_ = new_array;
max_space_ = 2 * max_space_;
}
array_[used_space_] = item;
++used_space_;
}
void Inventory::print() {
for ( int i = 0; i < size(); ++i ) {
cout << array_[i] << endl;
}
}
The main():
Inventory inv;
string input;
while (cout << "input> "
and getline(cin,input)){
add_to_bag(input,inv);
So the point is you reset the inventory when you give it new contents. The function add_to_bag(); is:
void add_to_bag(string input, Inventory inv){
const string WHITESPACE1_REGEX = "[[:space:]]*";
const string WHITESPACE2_REGEX = "[[:space:]]+";
const string WORD_REGEX = "[[:alpha:]_]+";
const string LINE_REGEX =
WHITESPACE1_REGEX +
WORD_REGEX +
"(" +
WHITESPACE2_REGEX +
WORD_REGEX +
")*" +
WHITESPACE1_REGEX;
regex line_reg(LINE_REGEX);
regex word_regex(WORD_REGEX);
string line = input;
if ( regex_match(line, line_reg) ) {
sregex_iterator iter(line.begin(), line.end(), word_regex);
sregex_iterator end;
while ( iter != end ) {
inv.add(iter->str());
++iter;
}
} else {
cout << "Error: unknown inventory contents." << endl;
}
}
Your problem is:
void add_to_bag(string input, Inventory inv);
You pass a copy of the Inventory object to add_to_bag. You modify that copy ... and then it gets thrown away. The fix is to pass by reference:
void add_to_bag(string input, Inventory &inv);
Incidentally, in real-life code, I would strongly advise the use of std::vector<std::string> rather than "roll your own". There are a number of tricky exception handling issues you have got wrong here - and unless Inventory doesn't have a destructor (implying a memory leak), or does have a correct copy constructor I would have expected you to run into "double free" issues. (Read about "The Rule of Three".)
A simple way to design your class would be as follows:
class Inventory {
private:
std::vector<std::string> items_;
public:
Inventory(){}
~Inventory(){}
void addItem( const std::string& item ) {
items_.push_back( item );
}
void printInventory() const {
int idx = 0;
for (; idx < items_.size(); ++idx ) {
std::cout << items_[idx] << std::endl;
}
}
void clearInventory() {
items_.clear();
}
};
And as for your problem Martin Bonner had already answered it with the modifying of the copy and the removal of it afterwards and the other issues with the memory management.
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.
For a simple assignment to do with dynamic memory and copy constructors, my prof has assigned us a simple assignment, but I get an error during the second time my delete [] happens.
header file:
class Stream {
int len;
char *hold;
char* newmem(int);
public:
Stream ();
Stream (int);
Stream(const char *);
~Stream ( );
void operator=(const Stream &);
Stream(const Stream &);
friend void show(Stream);
void operator<<(const char*);
};
it should be fairly simple. here is my code:
#include <iostream>
#include <new>
#include <cstring>
using namespace std;
#include "stream.h"
char* Stream::newmem(int x) {
char * tmp;
try {
tmp = new char[x];
}
catch(std::bad_alloc) {
tmp = NULL;
}
if(tmp)
cout << "newmem: " << (void *) tmp << endl;
return tmp;
}
Stream::Stream ( ) {
len = 1000;
hold = newmem(len);
if (hold)
strcpy (hold, "");
}
Stream::Stream(int n) {
len = n;
hold = newmem(len);
if (hold)
strcpy (hold,"");
}
Stream::Stream(const char * dat) {
len = strlen(dat) +1;
hold = newmem(len);
if (hold)
strcpy(hold,dat);
}
Stream::Stream(const Stream &from) {
cout << "in the copy constructor, allocating new memory..." << endl;
cout << "original pointer address is: " << (void *) from.hold << endl;
cin.get( );
len=from.len;
hold=newmem(len +1);
cout << "new pointer address is: " << (void *) hold << endl;
cin.get( );
if(hold)
strcpy (hold,from.hold);
}
Stream::~Stream ( ) {
cout << "destruct: " << (void *) hold << endl;
cin.get( );
if (hold)
delete [] hold;
}
void Stream::operator= (const Stream &from) {
if(hold)
delete [ ] hold;
len = from.len;
hold=newmem(len +1);
if (hold)
strcpy(hold,from.hold);
}
void show (Stream prt) {
cout << "String is: " << prt.hold << endl << "Length is: " << prt.len << endl;
}
void Stream::operator<< (const char *data) {
int dlen = strlen(data);
for (int i=0 ; i<=len && i<=dlen ; i++) {
hold[i] = data[i];
}
}
int main( ) {
char data[ ] = "Growing up it all seems so one-sided;"
"Opinions all provided;"
"The future pre-decided;"
"Detached and subdivided;"
"In the mass production zone!"
"-Neil Peart- \"Subdivisions\"";
Stream x1, x2(25), x3;
x1 << data;
x2 << data;
show(x1);
show(x2);
x3 = x2;
show(x3);
return 0;
}
and my output / error:
in the copy constructor, allocating new memory...
original pointer address is: 0x804c008
new pointer address is: 0x804c808
String is: Growing up it all seems so one-sided;Opinions all provided;The future pre-decided;Detached and subdivided;In the mass production zone!-Neil Peart-Subdivisions"
Length is: 1000
destruct: 0x804c808
in the copy constructor, allocating new memory...
original pointer address is: 0x804c3f8
new pointer address is: 0x804c808
String is: Growing up it all seems so
Length is: 25
destruct: 0x804c808
*** glibc detected *** a.out: free(): invalid pointer: 0x0804c808 ***
The for loop in the operator<< has two off-by-one errors:
for (int i=0 ; i<=len
allows i==len, but the only valid indices of hold are 0..(len-1). So, you can write one off the end.
Secondly, as thiton pointed out, it doesn't copy the \0 terminator even if there is space.
A correct implementation might look like:
void Stream::operator<< (const char *data) {
int source_len = strlen(data);
int copy_len = min(source_len, len-1); // allow for terminator
for (int i=0; i<copy_len; i++) {
hold[i] = data[i];
}
hold[copy_len] = '\0';
}
although it'd be better practise to simply use strncpy.
Note that the idiom of using half-open (or one-past-the-end) ranges is standard not only in direct array indexing, but also with C++ iterators. So, you should always expect to see
for (i=0; i<n; ++i) {
or
for (i = begin; i != end; ++i) {
and should generally treat closed-range loops like yours as a smell that warrants further investigation.
First a little self-help advice: The most important tool for catching memory access errors is valgrind. Run it on your program, and you'll get a warning every time you try to access unallocated or uninitialized memory. It's no substitute for knowledge, but the next best thing.
While I get different output than you get, errors seem to interact here:
The operator<< has an off-by-one error in the range check. It writes one byte too much (hold[len]).
operator<< does never write the terminating null byte. Both errors are invoked by x2 << data.
When the copy constructor tries to copy the string from x2, strcpy finds no terminating null byte and both reads right off the end of x2.hold and writes past the end of x3.hold. The latter has the potential for unbounded corruption and probably caused your error.
Whenever you deal with C strings, make very, very sure to get termination right. The fixed version is:
void Stream::operator<< (const char *data) {
int dlen = strlen(data);
hold[len-1] = 0;
for (int i=0 ; i < len-1 && i <= dlen ; i++) {
hold[i] = data[i];
}
}
Or, using the std library:
void Stream::operator<< (const char *data) {
strncpy(hold, data, len);
hold[len-1] = 0;
}
I'm using a vector container to hold instances of an object which contain 3 ints and 2 std::strings, this is created on the stack and populated from a function in another class but running the app through deleaker shows that the std::strings from the object are all leaked. Here's the code:
// Populator function:
void PopulatorClass::populate(std::vector<MyClass>& list) {
// m_MainList contains a list of pointers to the master objects
for( std::vector<MyClass*>::iterator it = m_MainList.begin(); it != m_MainList.end(); it++ ) {
list.push_back(**it);
}
}
// Class definition
class MyClass {
private:
std::string m_Name;
std::string m_Description;
int m_nType;
int m_nCategory;
int m_nSubCategory;
};
// Code causing the problem:
std::vector<MyClass> list;
PopulatorClass.populate(list);
When this is run through deleaker the leaked memory is in the allocator for the std::string classes.
I'm using Visual Studio 2010 (CRT).
Is there anything special I need to do to make the strings delete properly when unwinding the stack and deleting the vector?
Thanks,
J
May be Memory leak with std::vector<std::string> or something like this.
Every time you got a problem with the STL implementation doing something strange or wrong like a memory leak, try this :
Reproduce the most basic example of what you try to achieve. If it runs without a leak, then the problem is in the way you fill the data. It's the most probable source of problem (I mean your own code).
Not tested simple on-the-fly example for your specific problem :
#include <string>
#include <sstream>
// Class definition
struct MyClass { // struct for convenience
std::string m_Name;
std::string m_Description;
int m_nType;
int m_nCategory;
int m_nSubCategory;
};
// Prototype of populator function:
void populate(std::vector<MyClass>& list)
{
const int MAX_TYPE_IDX = 4;
const int MAX_CATEGORY_IDX = 8;
const int MAX_SUB_CATEGORY_IDX = 6;
for( int type_idx = 0; type_idx < MAX_TYPE_IDX ; ++type_idx)
for( int category_idx = 0; category_idx < MAX_CATEGORY_IDX ; ++category_idx)
for( int sub_category_idx = 0; sub_category_idx < MAX_SUB_CATEGORY_IDX ; ++sub_category_idx)
{
std::stringstream name_stream;
name_stream << "object_" << type_idx << "_" << category_idx << "_" << sub_category_idx ;
std::stringstream desc_stream;
desc_stream << "This is an object of the type N°" << type_idx << ".\n";
desc_stream << "It is of category N°" << category_idx << ",\n";
desc_stream << "and of sub-category N°" << category_idx << "!\n";
MyClass object;
object.m_Name = name_stream.str();
object.m_Description = desc_stream.str();
object.m_nType = type_idx;
m_nCategory =
m_nSubCategory =
list.push_back( object );
}
}
int main()
{
// Code causing the problem:
std::vector<MyClass> list;
populate(list);
// memory leak check?
return 0;
}
If you still got the memory leak, first check that it's not a false-positive from your leak detection software.
Then if it's not, google for memory leak problems with your STL implementation (most of the time on the compiler developer website). The implementor might provide a bug tracking tool where you could search in for the same problem and potential solution.
If you still can't find the source of the leak, maybe try to build your project with a different compiler (if you can) and see if it have the same effect. Again if the leak still occurs, the problem have a lot of chances to come from your code.
Probably same root issue as Alexey's link. The shipped version has broken move code for basic_string. MS abandoned us VC10 users, so you must fix it yourself. in xstring file you have this:
_Myt& assign(_Myt&& _Right)
{ // assign by moving _Right
if (this == &_Right)
;
else if (get_allocator() != _Right.get_allocator()
&& this->_BUF_SIZE <= _Right._Myres)
*this = _Right;
else
{ // not same, clear this and steal from _Right
_Tidy(true);
if (_Right._Myres < this->_BUF_SIZE)
_Traits::move(this->_Bx._Buf, _Right._Bx._Buf,
_Right._Mysize + 1);
else
{ // copy pointer
this->_Bx._Ptr = _Right._Bx._Ptr;
_Right._Bx._Ptr = 0;
}
this->_Mysize = _Right._Mysize;
this->_Myres = _Right._Myres;
_Right._Mysize = 0;
_Right._Myres = 0;
}
return (*this);
}
Note the last
_Right._Myres = 0;
that should happen only under the last condition, for the short case _Right should better be left alone.
As the capacity is set to 0 instead of 15, other code will take unintended branch in function Grow() when you assign another small string and will allocate a block of memory just to trample over the pointer with the immediate string content.
I am trying to write a logger class for my C++ calculator, but I'm experiencing a problem while trying to push a string into a list.
I have tried researching this issue and have found some information on this, but nothing that seems to help with my problem. I am using a rather basic C++ compiler, with little debugging utilities and I've not used C++ in quite some time (even then it was only a small amount).
My code:
#ifndef _LOGGER_H_
#define _LOGGER_H_
#include <iostream>
#include <list>
#include <string>
using std::cout;
using std::cin;
using std::endl;
using std::list;
using std::string;
class Logger
{
private:
list<string> mEntries;
public:
Logger() {}
~Logger() {}
// Public Methods
void WriteEntry(const string& entry)
{
mEntries.push_back(entry);
}
void DisplayEntries()
{
cout << endl << "**********************" << endl
<< "* Logger Entries *" << endl
<< "**********************" << endl
<< endl;
for(list<string>::iterator it = mEntries.begin();
it != mEntries.end(); it++)
{
// *** BELOW LINE IS MARKED WITH THE ERROR ***
cout << *it << endl;
}
}
};
#endif
I am calling the WriteEntry method by simply passing in a string, like so:
mLogger->WriteEntry("Testing");
Any advice on this would be greatly appreciated.
* CODE ABOVE HAS BEEN ALTERED TO HOW IT IS NOW *
Now, the line:
cout << *it << endl;
causes the same error. I'm assuming this has something to do with how I am trying to get the string value from the iterator.
The code I am using to call it is in my main.cpp file:
#include <iostream>
#include <string>
#include <sstream>
#include "CommandParser.h"
#include "CommandManager.h"
#include "Exceptions.h"
#include "Logger.h"
using std::string;
using std::stringstream;
using std::cout;
using std::cin;
using std::endl;
#define MSG_QUIT 2384321
#define SHOW_LOGGER true
void RegisterCommands(void);
void UnregisterCommands(void);
int ApplicationLoop(void);
void CheckForLoggingOutput(void);
void ShowDebugLog(void);
// Operations
double Operation_Add(double* params);
double Operation_Subtract(double* params);
double Operation_Multiply(double* params);
double Operation_Divide(double* params);
// Variable
CommandManager *mCommandManager;
CommandParser *mCommandParser;
Logger *mLogger;
int main(int argc, const char **argv)
{
mLogger->WriteEntry("Registering commands...\0");
// Make sure we register all commands first
RegisterCommands();
mLogger->WriteEntry("Command registration complete.\0");
// Check the input to see if we're using the program standalone,
// or not
if(argc == 0)
{
mLogger->WriteEntry("Starting application message pump...\0");
// Full version
int result;
do
{
result = ApplicationLoop();
} while(result != MSG_QUIT);
}
else
{
mLogger->WriteEntry("Starting standalone application...\0");
// Standalone - single use
// Join the args into a string
stringstream joinedStrings(argv[0]);
for(int i = 1; i < argc; i++)
{
joinedStrings << argv[i];
}
mLogger->WriteEntry("Parsing argument '" + joinedStrings.str() + "'...\0");
// Parse the string
mCommandParser->Parse(joinedStrings.str());
// Get the command names from the parser
list<string> commandNames = mCommandParser->GetCommandNames();
// Check that all of the commands have been registered
for(list<string>::iterator it = commandNames.begin();
it != commandNames.end(); it++)
{
mLogger->WriteEntry("Checking command '" + *it + "' is registered...\0");
if(!mCommandManager->IsCommandRegistered(*it))
{
// TODO: Throw exception
mLogger->WriteEntry("Command '" + *it + "' has not been registered.\0");
}
}
// Get each command from the parser and use it's values
// to invoke the relevant command from the manager
double results[commandNames.size()];
int currentResultIndex = 0;
for(list<string>::iterator name_iterator = commandNames.begin();
name_iterator != commandNames.end(); name_iterator++)
{
string paramString = mCommandParser->GetCommandValue(*name_iterator);
list<string> paramStringArray = StringHelper::Split(paramString, ' ');
double params[paramStringArray.size()];
int index = 0;
for(list<string>::iterator param_iterator = paramStringArray.begin();
param_iterator != paramStringArray.end(); param_iterator++)
{
// Parse the current string to a double value
params[index++] = atof(param_iterator->c_str());
}
mLogger->WriteEntry("Invoking command '" + *name_iterator + "'...\0");
results[currentResultIndex++] =
mCommandManager->InvokeCommand(*name_iterator, params);
}
// Output all results
for(int i = 0; i < commandNames.size(); i++)
{
cout << "Result[" << i << "]: " << results[i] << endl;
}
}
mLogger->WriteEntry("Unregistering commands...\0");
// Make sure we clear up our resources
UnregisterCommands();
mLogger->WriteEntry("Command unregistration complete.\0");
if(SHOW_LOGGER)
{
CheckForLoggingOutput();
}
system("PAUSE");
return 0;
}
void RegisterCommands()
{
mCommandManager = new CommandManager();
mCommandParser = new CommandParser();
mLogger = new Logger();
// Known commands
mCommandManager->RegisterCommand("add", &Operation_Add);
mCommandManager->RegisterCommand("sub", &Operation_Subtract);
mCommandManager->RegisterCommand("mul", &Operation_Multiply);
mCommandManager->RegisterCommand("div", &Operation_Divide);
}
void UnregisterCommands()
{
// Unregister each command
mCommandManager->UnregisterCommand("add");
mCommandManager->UnregisterCommand("sub");
mCommandManager->UnregisterCommand("mul");
mCommandManager->UnregisterCommand("div");
// Delete the logger pointer
delete mLogger;
// Delete the command manager pointer
delete mCommandManager;
// Delete the command parser pointer
delete mCommandParser;
}
int ApplicationLoop()
{
return MSG_QUIT;
}
void CheckForLoggingOutput()
{
char answer = 'n';
cout << endl << "Do you wish to view the debug log? [y/n]: ";
cin >> answer;
switch(answer)
{
case 'y':
ShowDebugLog();
break;
}
}
void ShowDebugLog()
{
mLogger->DisplayEntries();
}
// Operation Definitions
double Operation_Add(double* values)
{
double accumulator = 0.0;
// Iterate over all values and accumulate them
for(int i = 0; i < (sizeof values) - 1; i++)
{
accumulator += values[i];
}
// Return the result of the calculation
return accumulator;
}
double Operation_Subtract(double* values)
{
double accumulator = 0.0;
// Iterate over all values and negativel accumulate them
for(int i = 0; i < (sizeof values) - 1; i++)
{
accumulator -= values[i];
}
// Return the result of the calculation
return accumulator;
}
double Operation_Multiply(double* values)
{
double accumulator = 0.0;
for(int i = 0; i < (sizeof values) - 1; i++)
{
accumulator *= values[i];
}
// Return the value of the calculation
return accumulator;
}
double Operation_Divide(double* values)
{
double accumulator = 0.0;
for(int i = 0; i < (sizeof values) - 1; i++)
{
accumulator /= values[i];
}
// Return the result of the calculation
return accumulator;
}
Did you remember to call mLogger = new Logger at some point? Did you accidantally delete mLogger before writing to it?
Try running your program in valgrind to see whether it finds any memory errors.
After your edit, the solution seem clear:
Your first line in main() is :
mLogger->WriteEntry("Registering commands...\0");
Here mLogger is a pointer that has never been initialized. This is "undefined behaviour", meaning anything can appen, often bad things.
To fix this you can either make it a "normal" variable, not a pointer or create a Logger instance using new (either at the declaration or as the first line in main).
I suggest you to not use a pointer to be sure the logger is always there and is automatically destroyed.
By the way, it seems like you want to create every instance of objects on the heap using pointers. It's not recommanded if it's not necessary. You should use pointers ONLY if you want to explicitely state the creation (using new) and destruction (using delete) of the instance object. If you just need it in a specific scope, don't use a pointer. You might come from another language like Java or C# where all objects are referenced. If so, you should start learning C++ like a different language to avoid such kind of problem. You should learn about RAII and other C++ scpecific paradigm that you cannot learn in those languages. If you come from C you should too take it as a different language. That might help you avoid complex problems like the one you showed here. May I suggest you read some C++ pointer, references and RAII related questions on stackoverflow.
First, you don't need to create the std::list on the heap. You should just use it as a normal member of the class.
class Logger
{
private:
list<string> mEntries; // no need to use a pointer
public:
Logger() // initialization is automatic, no need to do anything
{
}
~Logger() // clearing and destruction is automatic too, no need to do anything
{
}
//...
};
Next, entryData don't exist in this code so I guess you wanted to use entry. If it's not a typo then you're not providing the definition of entryData that is certainly the source of your problem.
In fact I would have written your class that way instead:
class Logger
{
private:
list<string> mEntries;
public:
// no need for constructor and destructor, use the default ones
// Public Methods
void WriteEntry(const string& entry) // use a const reference to avoid unnecessary copy (even with optimization like NRVO)
{
mEntries.push_back( entry ); // here the list will create a node with a string inside, so this is exactly like calling the copy constructor
}
void DisplayEntries()
{
cout << endl << "**********************" << endl
<< "* Logger Entries *" << endl
<< "**********************" << endl
<< endl;
for(list<string>::iterator it = mEntries.begin();
it != mEntries.end(); ++it) // if you want to avoid unnecessary copies, use ++it instead of it++
{
cout << *it << endl;
}
}
};
What's certain is that your segfault is from usage outside of this class.
Is an instance of Logger being copied anywhere (either through a copy constructor or operator=)? Since you have mEntries as a pointer to a list, if you copy an instance of Logger, they will share the value of the pointer, and when one is destructed, it deletes the list. The original then has a dangling pointer. A quick check is to make the copy constructor and operator= private and not implemented:
private:
void operator=(const Logger &); // not implemented
Logger(const Logger &); // not implemented
When you recompile, the compiler will flag any copies of any Logger instances.
If you need to copy instances of Logger, the fix is to follow the Rule of 3:
http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29
You can do this by eliminating the need for the destructor (by not using a pointer: list<string> mEntries), or by adding the needed code to the copy constructor and operator= to make a deep copy of the list.
You only need to do
list<string> entries;
entries.push_back();
You do not need to create a pointer to entries.
Nothing too obvious, though you typed
mEntries->push_back(string(entryData));
and I htink you meant entry instead of entryData. You also don't need the string conversion on that line, and your function should take entry by const reference.
However, none of these things would cause your program to segfault. What compiler are you using?
You're missing the copy constructor. If the Logger object is copied and the original deleted, you'll be dereferencing memory that was previously deleted.
A simplified example of the problem
Logger a;
{
Logger b;
a=b;
}
a.WriteEntry("Testing");
Add a copy constructor.
Logger(const Logger& item)
{
mEntries = new list<string>();
std::copy(item.mEntries->begin(), item.mEntries->end(), std::back_inserter(*mEntries));
}