Best practice concerning data creating classes - c++

i am new to c++ and often have the problem to create list of objects from file data. Since the amount of data may be huge i want to avoid unnecessary copying.
class Tool
{
Tool(string fileName);
vector<MyClass> read(); // 1)
vector<MyClass*> read(); // 2)
read(vector<MyClass*>& vec); // 3)
}
vector<MyClass> Tool::read()
{
MyClass c;
vector<MyClass> vec;
c.a = 1;
c.b = 2;
vec.push_back(c);
c.a = 3;
c.b = 4;
vec.push_back(c);
...
return vec;
}
vector<MyClass*> Tool::read()
{
MyClass* c;
vector<MyClass> vec;
c = new MyClass();
c->a = 1;
c->b = 2;
vec.push_back(c);
c = new MyClass();
c->a = 3;
c->b = 4;
vec.push_back(c);
...
return vec;
}
Tool::read(vector<MyClass*>& vec)
{
MyClass* c;
c = new MyClass();
c->a = 1;
c->b = 2;
vec.push_back(c);
c = new MyClass();
c->a = 3;
c->b = 4;
vec.push_back(c);
...
return vec;
}
In 1) the read will be quite expensive because there is much to copy. I have read somewhere that recent c++ versions could make those copies quite efficient but i am not sure
In 2) the copy operations should use significant less data. But I have to delete somewhere and at some time the objects where the pointers are pointing to. I guess i need to do that somewhere outside of Tool, since i don't need Tool anymore after read the file.
In 3) I can give the pointer to the read method and be able to push the pointers directly into the destination vector. Seem to be the best way out of the ways getting my vector of MyClasses. And since i give the vector to the read method I am more aware of deleting its elements later.
Maybe someone knows better ways to deal with such scenarios?
Thanks much for comments and help.
Edit1: I added implementations as UnholySheep suggested.

I did something interesting:
#include <iostream>
#include <vector>
// Don't do this in real code.
using namespace std;
std::vector<string> makeVec() {
vector<string> vec;
vec.push_back("first");
vec.push_back("second");
cout << "Address: " << &vec << endl;
return vec;
}
int main(int, char **) {
vector<string> vec = makeVec();
cout << "Address: " << &vec << endl;
}
^ Foo
Address: 0x7ffdaeead8a0
Address: 0x7ffdaeead8a0
Notice that the address of the vector is the same both inside makeVec and then inside main(). This is because the compiler optimizes what is going on.
But then I changed main:
int main(int, char **) {
vector<string> vec = makeVec();
cout << "Address: " << &vec << endl;
cout << endl;
vector<string> vec2;
vec2 = makeVec();
cout << "Address2: " << &vec << endl;
}
Running this version:
Address: 0x7fff5b4febf0
Address: 0x7fff5b4febf0
Address: 0x7fff5b4fec30
Address2: 0x7fff5b4fec10
So... from this, if you're returning an object of any type, then if you can do both declaration and instantiation together, it's more efficient. Do this:
vector<string> vec = makeVec();
Do NOT do this:
vector<string> vec2;
vec2 = makeVec();

Related

Passing an Array of Objects into a Function

I have a class that defines object.name as "Ben" and object.favNum as 25.
I also have an array (names) to store 5 objects of this class.
Before I output the attributes of the objects in the array, I change the value of names[0].name to "Jim" and names[0].favNum to 40, using pointer notation.
Here is my code:
#include<iostream>
class Person {
public:
std::string name = "Ben";
int favNum = 25;
};
int main()
{
Person names[5];
Person *ptr = &names[0];
// Changing Values
(ptr + 0)->name = "Jim";
(ptr + 0)->favNum = 40;
for (int i = 0; i < 5; i++)
{
std::cout << (std::string) (ptr + i)->name << std::endl;
std::cout << (int) (ptr + i)->favNum << std::endl << std::endl;
}
system("pause>0");
}
Output:
Jim
40
Ben
25
Ben
25
Ben
25
Ben
25
Where I have the "Changing Values" comment, I want to replace those 2 lines with a function.
The function will be able to change the values as I am doing with the 2 lines.
This is where I am having a problem.
I'm struggling with figuring out what type to declare the function as, how to pass the pointer in, how to receive the pointer, and how to change the values of the objects in the array in the function.
I would recommend against the use of pointers, unless you really need pointers.
// use references instead
void change(Person &person) {
person.name = "Jim";
person.favNum = 40;
};
int main()
{
Person names[5];
change(names[0]);
// ...
}
The appropriate way to write such a function would be using references. A reference is similar to a pointer, but can never be nullptr. In your case you don't need nullability, you just need to edit a Person without copying it.
You probably want change() to be parametric though:
// use references instead
void change(Person &person, std::string name, int favNum) {
person.name = std::move(name);
person.favNum = favNum;
}
But now you are assigning all members of Person using this function, which makes it pointless. It would be simpler if you assigned Person. Why not use a struct and aggregate initialization:
struct Person {
std::string name = "Ben";
int favNum = 25;
};
int main()
{
Person names[5];
names[0] = {"Jim", 40};
// ...
}
On a side note, what you are doing here is unnecessary complicated:
(ptr + 0)->name = "Jim";
// this is equivalent to
ptr[0].name = "Jim";
// this is equivalent to
ptr->name = "Jim";
This is how your code should look in "real" c++ (the setting of the values will be done in the constructor function or using the setNameAndFavNum() function):
#include <array>
#include <iostream>
struct Person {
public:
Person(const std::string& name_ = "Ben", int favNum_ = 25) : name(name_), favNum(favNum_) {};
void setNameAndFavNum(const std::string& name_, int favNum_) {
name = name_;
favNum = favNum_;
};
std::string name;
int favNum;
};
int main() {
std::array<Person, 5> names;
// Changing Values
names[0].setNameAndFavNum("Jim", 40);
// Alternatively you can use the constructor and implicit copy constructor
// names[0] = {"Jim", 40};
for (int i = 0; i < names.size(); i++) {
std::cout << names[i].name << std::endl;
std::cout << names[i].favNum << std::endl << std::endl;
}
}
You shouldn't mess around with pointers and raw values when writing c++ code.

C++ Segmentation fault while dereferencing a void pointer to a vector

#include <iostream>
#include <vector>
#include <mutex>
struct STRU_Msg
{
std::string name;
void *vpData;
};
class CMSG
{
public:
template <typename T>
int miRegister(std::string name)
{
STRU_Msg msg;
msg.name = name;
msg.vpData = malloc(sizeof(T));
msgtable.push_back(msg);
std::cout << "registeratio ok\n";
return 0;
}
template <typename T>
int miPublish(std::string name, T tData)
{
for (int i = 0; i < msgtable.size(); i++)
{
if (!name.compare(msgtable[i].name))
{
(*(T *)msgtable[i].vpData) = tData;
std::cout << "SUccess!\n";
return 0;
}
else
{
std::cout << "cannot find\n";
return 0;
}
}
}
private:
std::vector<STRU_Msg> msgtable;
};
int main()
{
CMSG message;
std::string fancyname = "xxx";
std::vector<float> v;
// message.miRegister< std::vector<float> >(fancyname);
// for (int i = 0; i < 1000; i++)
// {
// v.push_back(i);
// }
// std::cout << "v[0]: " << v[0] << ", v[-1]: " << v[v.size()-1] << '\n';
// message.miPublish< std::vector<float> >(fancyname, v);
for (int i = 0; i < 1000; i++)
{
v.push_back(i);
}
std::cout << "v[0]: " << v[0] << ", v[-1]: " << v[v.size()-1] << '\n';
message.miRegister< std::vector<float> >(fancyname);
message.miPublish< std::vector<float> >(fancyname, v);
return 0;
}
What I want to achieve is to write a simple publish/subscribe (like ROS) system, I use void pointer so that it works for all data type. This is the simplified code.
If I publish an int, it works fine, but what really confuse me are:
If I pass a long vector (like this code), it gave me the
"segmentation fault (core dump)" error.
If I define the vector between "register" and "publish" (i.e. like
the commented part), this error goes away.
If I use a shorter vector, like size of 10, no matter where I define
it, my code run smoothly.
I use g++ in Linux.
Please help me fix my code and explain why above behaviors will happen, thanks in ahead!
You cannot copy std::vector or any other non-trivial type like that. Before you do anything (even assignment-to) with such an object, you need to construct it using a constructor and placement new.
A way to do this would be
new(msgtable[i].vpData) T;
Do this in the register function.
Then you can assign a value as you do.
Still better, do not use malloc at all, allocate your object with (normal, non-placement) new.
I however strongly suggest ditching void* and moving to a template based implementation of STRU_Msg. If you don't feel like reinventing the wheel, just use std::any.

After passing pointer to the main function, cannot print the content properly

I am practicing using pointers to create objects and access data. I created a stuct called BigNum to represent a number with multiple digits. When I try to print the content of the struct inside the readDigits function, it can be printed pretty well. However, after passing the pointer to the main function, the content of the stuct is printed out to be random numbers. Why? How to fix it?
struct BigNum{
int numDigits; //the number of digits
int *digits; //the content of the big num
};
int main(){
BigNum *numPtr = readDigits();
for (int i=0; i<(numPtr->numDigits);i++ ){
std::cout << (numPtr->digits)[i] << std::endl;
}
return 0;
}
BigNum* readDigits(){
std::string digits;
std::cout << "Input a big number:" << std::endl;
std::cin >> digits;
int result[digits.length()];
toInt(digits,result);
BigNum *numPtr = new BigNum();
numPtr->numDigits = digits.length();
numPtr->digits = result;
/* When I try to print in here, it's totally okay!
std::cout << "Here is the content:" << std::endl;
for (int i=0; i<numPtr->numDigits;i++ ){
std::cout << (numPtr->digits)[i] << std::endl;
}
*/
return numPtr;
}
void toInt(std::string& str, int result[]){
for (int i=0;i<str.length() ;i++ ){
result[str.length()-i-1] = (int)(str[i]-'0');
}
}
BigNum* readDigits(){
//....
int result[digits.length()];
//....
numPtr->digits = result;
return numPtr;
}
result is stored on the stack. So if you return it as part of numPtr, it will be invalid as soon as you exit the function. Instead of storing it on the stack you have to allocate it with new.
You have undefined behavior because you assign address of automatic object to digits pointer. When readDigits() returns this memory is not valid anymore. You should assign to this pointer address of heap-based object (or some equivalent, e.g. use vector or smart pointer):
#include <vector>
struct BigNum{
int numDigits; //the number of digits
std::vector<int> digits; //the content of the big num
};
Then you can insert numbers into vector this way:
int input;
while ( std::cin >> input) //enter any non-integer to end the loop
{
digits.push_back(input);
}
The problem is that within the function BigNum* readDigits() you assign apointer to stack memory to the pointer of your newly allocated BigNum:
int result[digits.length()]; // <--- variable is on the stack!!!
toInt(digits,result);
BigNum *numPtr = new BigNum();
numPtr->numDigits = digits.length();
numPtr->digits = result; // <--- make pointer to stack memory available to caller of readDigits
Now if you proceed the access to numPtr->digits is ok since the memory of result is still valid on the stack (as long as you are within readDigits). Once you've left ´readDigits()´ the memory of result is overwritten depending on what you do (calling other functions, ...).
Right now I'm even wondering why you don't get a compiler error with ´int result[digits.length()];´ since ´digits.length()´ is not constant and the size of required stack memory has to be defined at compile time... so I'm thinking that the size of result is actually 0...?? Would be a nice thing to test!
My recommendation is to modify the code of readDigits as follows:
BigNum* readDigits()
{
std::string digits;
int i;
std::cout << "Input a big number:" << std::endl;
std::cin >> digits;
//int result[digits.length()];
//toInt(digits,result);
BigNum *numPtr = new BigNum();
numPtr->numDigits = digits.length();
numPtr->digits = (int *)malloc(sizeof(int) * numPtr->numDigits); // allocate heap memory for digits
toInt(digits, numPtr->digits);
/* When I try to print in here, it's totally okay!
std::cout << "Here is the content:" << std::endl;
for (i = 0; i <numPtr->numDigits; i++)
{
std::cout << (numPtr->digits)[i] << std::endl;
}
*/
return numPtr;
}
Remember to free your memory if ´BigNum *numPtr´ is no longer used (´free(numPtr->digits);´) otherwise you'll get a memory leak (sooner or later):
int main()
{
BigNum *numPtr = readDigits();
int i;
for (i = 0; i < (numPtr->numDigits); i++)
{
std::cout << (numPtr->digits)[i] << std::endl;
}
free(numPtr->digits); // free memory allocated by readDigits(..)
return 0;
}

Dynamically Allocated Array of Struct Pointers in C++

Ok i'm pretty new to c++ (I think what we are learning is somehow an hybrid of c and c++).
I've found alot of anwsers to my question, sadly all of them where in C using malloc.
struct A {
int randomStuff = 0;
};
struct B {
int numOfA= 5; // In reality this number is variable.
A** arrayOfA;
};
The struct are given to us. Now I need to allocate and fill this array with pointers to, I guess, A pointers. <- Correct me here if I'm wrong pointers are still quite complex for me.
A * a1 = new A;
A * a2 = new A;
B * b = new B;
// Allocate space for the array...
b->arrayOfA = new A*[numOfA];
// Next I want to initialize the pointers to NULL
for(int i; i < b->numOfA; i++){
b->arrayOfA[i] = NULL;
}
// In another function I would the assign a value to it
b->arrayOfA[0] = a1;
b->arrayOfA[1] = a2;
The way I see it is that b->arrayOfA needs to point to an array of A struct...somehow like this
b->arrayOfA = new A*;
A * arr[numOfA];
b->arrayOfA = arr;
My brain is bleeding.
How do I correctly allocate it and assign existing values(A structs) to it?
*edit
It would appear that the code was working as intended and that my display was causing me issues. Basically, I needed an array "arrayOfA[]" in which I would put the pointers to an A struct. Effectively making the result of this:
cout << arrayOfA[0]->randomStuff // 0 would be displayed
To be 0.
You could allocate an array of pointers and for each of them allocate an array of your objects
int x = 5, y = 6;
b->arrayOfA = new A*[x]; //array of pointers
for(int i=0;i<x;i++){
b->arrayOfA[i] = new A[y]; //matrix (array of arrays)
}
for(int i=0;i<x;i++){
delete[] b->arrayOfA[i]; //don't forget to free memory
}
delete[] b->arrayOfA;
You should be able to just use a vector:
#include <vector>
int main()
{
vector<A> vector_of_a;
vector_of_a.push_back(a1); //store a1 in the vector
vector_of_a.push_back(a2); //store a2 in the vector
//...
std::cout << "Number of A's: " << vector_of_a.size() << std::endl;
std::cout << vector_of_a[0].randomStuff << std::endl; //prints 0 as specified but with '.' not '->' Objects are still on the heap, and not the stack.
}
The A's in the vector are stored on the heap, but you don't need to manage the memory yourself (no need for malloc/free or new/delete.
The A objects will be disposed of correctly when the vector goes out of scope.
You also get
You can push in pointers to objects too, but this reduces the usefulness of the vector as you then have to do your own memory management for the objects:
#include <vector>
int main()
{
A* a1 = new A();
A* a2 = new A();
vector<A> vector_of_a;
vector_of_a.push_back(a1); //store pointer to a1 in the vector
vector_of_a.push_back(a2); //store pointer to a2 in the vector
//...
std::cout << "Number of A's: " << vector_of_a.size() << std::endl;
std::cout << vector_of_a[0]->randomStuff << std::endl; //prints 0 as specified
//...
for (auto i : vector_of_a)
{
delete (i);
}
vector_of_a.clear();
}
If you really don't want to use a vector, then I reccommend turning your struct B into a fully fledged class.
It gives you the benefit of encapsulation, functions to manage the data are stored within the class, and the class does the memory management and doesn't leave it to the
user's code to manage and clear up behind it:
class B
{
public:
array_of_A(unsigned int size);
~array_of_A();
bool init_array();
unsigned int get_size();
A** get_array();
private:
unsigned int num_of_A;
A** array_of_A;
}
B::array_of_A(unsigned int size)
{
num_of_a = size;
array_of_A = new A*[size]; //create array
memset (array_of_A, nullptr, size); //initialise contents to nullptr
}
B::~B()
{
for(unsigned int i = 0; i < num_of_a; i++)
{
delete array_of_a[i]; //delete each A
}
delete[](array_of_a); //delete the array of pointers.
}
unsigned int B::get_size()
{ return num_of_A; }
A** B::get_array()
{ return array_of_A; }
int main()
{
B b((5)); //most vexing parse...
b.get_array()[0] = new A();
b.get_array()[1] = new A();
b.get_array()[2] = new A();
std::cout << b.get_array()[0]->randomStuff << std::endl //prints 0 as requested
} //b goes out of scope, destructor called, all memory cleaned up
aaaand by internalising the memory management and supporting arbitarilly long arrays, we've just started implementing a (much) simpler version of vector. But good for practice/learning.

Segmentation Fault when trying to push a string to the back of a list

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));
}