I have a little program which stores some objects, and there is a function which should find required object if there are any matching abstraction object, returns the first found or default object. I need to display them, but when I'm using "show" function I'm getting this error:
no matching function for call to 'show'
Here is the code of main source file:
#include <iostream>
#include "object.h"
#include "Inventory.h"
using namespace std;
void fill(Inventory& i)
{
i.add_item("object", 22, 20, "blue");
i.add_item("object2", 25, 20, "red");
i.add_item("object3", 40, 20, "green");
}
void show(object& item)
{
cout << item.get_modelName() << " "
<< item.get_screenSize()<< " "
<< item.get_capacity() << " "
<< item.get_colour() << " "
<< endl;
}
int main()
{
Inventory inventory;
inventory.init();
fill(inventory);
object query1;
query1.init("", 0, 128, "silver");
show(inventory.find_item(query1));
object query2;
query2.init("object", 20, 20, "silver");
show(inventory.find_item(query2));
#ifndef NDEBUG
cin.get();
#endif
return 0;
}
Inventory.h:
#ifndef Inventory_h
#define Inventory_h
#pragma once
#include "object.h"
class Inventory
{
public:
// Initializes inventory for storing abstraction objects
void init() { _count = 0; }
// Returns the number of currently stored abstraction objects
size_t get_count() { return _count; }
// Returns stored abstraction object by its index or default if index is invalid
iPhone get_item(size_t i) { return (i <_count) ? _items[i] : iPhone(); }
// From passed property values, creates and adds new abstraction object in an array _items
void add_item(std::string modelName, float screenSize, int capacity, std::string colour);
// Looks for a matching abstraction object and returns the first found or default object
object find_item(object& query);
private:
// The maximum number of abstraction objects that can be stored
static const size_t MAX_SIZE{ 10 };
// An actual array for storing abstraction objects
object _items[Inventory::MAX_SIZE];
// The number of currently stored abstraction objects in the array _items
size_t _count;
};
#endif /* Inventory_h */
Inventory.cpp:
#include "Inventory.h"
void Inventory::add_item(std::string modelName, float screenSize, int capacity, std::string colour)
{
if (_count < Inventory::MAX_SIZE)
{
object new_item;
new_item.init( modelName, screenSize, capacity, colour);
_items[_count] = new_item;
_count++;
}
}
object Inventory::find_item(object &query)
{
for (size_t i = 0U; i < _count; i++)
{
object& item = _items[i];
//for string type property
if (query.get_modelName()!= ""
&& query.get_modelName() != item.get_modelName())
continue;
// for number type property
if (query.get_screenSize() != 0
&& query.get_screenSize() != item.get_screenSize())
continue;
// for number type property
if (query.get_capacity() != 0
&& query.get_capacity() != item.get_capacity())
continue;
//for string type property
if (query.get_colour()!= ""
&& query.get_colour() != item.get_colour())
continue;
return item;
}
return object{}; // return the default value object (or null object)
}
It is my first experience in C++. That's why I might have some mistakes.
Why I'm getting this error?
The problem is that your find_item returns the object by value, and that the show function takes its argument by reference.
Since the find_item function can return a default-constructed object, you can't change it to return by reference, so you need to update your show function to be able to handle temporary objects (which is what the compiler will create when you do show(inventory.find_item(query2))).
The solution is very simple: While a reference can't bind to a temporary object, constant references can. So simply change the show function to take a reference to a constant object:
void show(iPhone const& item) { ... }
Of course it works for a simple show function like yours, which doesn't modify the argument. If you modify the argument you can't make it constant, but you can still use it with temporary objects by using something that was introduced in the C++11 standard: rvalue references (sometimes mistakenly called universal references):
void show(iPhone&& item) // Note the use of double &&
{
...
}
Inventory::find_item 's return value iPhone is passed by value, for show(inventory.find_item(query2)); it's a temporary object which won't match iPhone& item for show().
Temporary object can't be bound to reference, but is ok for const reference. And show() doesn't change anything of the parameter, you should make the parameter type to const iPhone&.
void show(const iPhone& item)
{
cout << item.get_modelName() << " "
<< item.get_screenSize()<< " "
<< item.get_capacity() << " "
<< item.get_colour() << " "
<< endl;
}
BTW: Make sure that those iPhone::get_xxx() are const member functions.
Related
I have two classes, one for fuel and the other for miles. Every 24 miles you use 1 fuel. For this assignment I am required to have a pointer in my "miles" class to point to a "fuel" class so we can have "mile" class work with the "fuel" class without having to create a whole object "fuel" object inside the "mile" class. It was going well until I tested it and after some debugs it seems that, for some reason, the fuel object has it's values changed whenever I call the get function in my "miles" class. However, all I am doing is returning a member variable. Why would it change my fuel object? I also do not change the pointer's value anywhere else so it should always point to the fuel object.
Class Specification
#ifndef ODO_H
#define ODO_H
#include "FuelGauge.h"
class Odometer
{
private:
int currentMiles = 0;
int maxMilage = 999999;
FuelGauge* fuelObject = nullptr;
int counter = 0;
public:
Odometer(int, FuelGauge);
int getMiles();
void addMiles();
};
#endif ODO_H
Class Implementation
#include "Odometer.h"
Odometer::Odometer(int currMileage, FuelGauge Gauge) {
if (currMileage >= 0 && currMileage <= maxMilage) {
currentMiles = currMileage;
}
else {
cout << "Starting mileage is invalid" << endl;
}
fuelObject = &Gauge;
}
int Odometer::getMiles() {
return currentMiles;
}
void Odometer::addMiles() {
if (currentMiles < maxMilage) {
currentMiles++;
counter++;
}
else if (currentMiles >= maxMilage) {
cout << "You have reached the max mileage ... resetting to zero!" << endl;
currentMiles = 0;
}
if (counter >= 24) {
fuelObject->useFuel();
counter = 0;
}
}
In main, when I call the getMiles() function; The object pointed to by "fuelObject" has it's values changed to some random large number. Why does this happen?
This should be undefined behavior right here.
Odometer::Odometer(int currMileage, FuelGauge Gauge) {
if (currMileage >= 0 && currMileage <= maxMilage) {
currentMiles = currMileage;
}
else {
cout << "Starting mileage is invalid" << endl;
}
fuelObject = &Gauge;
}
Once your constructor finishes execution, your fuelObject is left pointing to garbage, since the parameter will go out of scope and it is by-val.
Apart from that, I have to agree with #scheff that the error you are seeing is likely not caused by the code you have shown.
Currently working on Object Oriented Programming in c++ and having problems with an instance showing nothing changed from a method I've created.
The whole code is based off of this object I've created from a header file.
#ifndef DEQUE_H_
#define DEQUE_H_
#include <iostream>
const int CAPACITY = 5;
const int DEFAULT = -1;
class Deque
{
public:
Deque();
int get_size() const;
bool is_empty() const;
bool is_full() const;
int operator[](int i) const;
static Deque insert_tail(int);
private:
int size_;
static int array_[CAPACITY];
};
std::ostream & operator<<(std::ostream &, const Deque &);
#endif
One of the problems I'm having is the insert_tail method that doesn't show any changes to my static array.
In the cpp file itself.. these are the function declarations.
#
include <iostream>
#include "Deque.h"
Deque::Deque()
:size_(0)
{
}
int Deque::array_[5] = {};
int Deque::get_size() const
{
return size_;
}
bool Deque::is_full() const
{
if (size_ == 5) return 1;
else return 0;
}
bool Deque::is_empty() const
{
if (size_!= 5) return 1;
else return 0;
}
int Deque::operator[](int i) const
{
int something = array_[i];
return something;
}
Deque Deque::insert_tail(int x)
{
Deque d;
d.size_ += 1;
int size = d.size_;
d.array_[size - 1] = x;
return d;
}
std::ostream & operator<<(std::ostream & cout, const Deque & dq)
{
cout << dq.get_size() << " [ ";
for (int i = 0; i < dq.get_size(); ++i)
{
cout << dq[i] << " ";
}
cout << "]";
return cout;
}
The operator works just fine. The bools work just fine and the remove_head and remove_tail thing I'll do once I figure out insert tail. Right now, it's not making any chances to the very object I've created inside the main.
#include <iostream>
#include "Deque.h"
void print(const Deque & deque)
{
static int i = 1;
std::cout << i << ". " << deque << ", empty: " << deque.is_empty()
<< ", full: " << deque.is_full();
i++;
}
void test_insert_tail(Deque & deque, int x)
{
deque.insert_tail(x);
print(deque); std::cout << "\n";
}
int main()
{
Deque deque;
print(deque);
std::cout << "\n";
test_insert_tail(deque, 2);
return 0;
}
The output should look like this,
1. 1 [ 2 ], empty: 0, full: 0
but looks like this
1. 0 [], empty: 1, full: 0
What's going on inside my static method for handling all the private attributes that I'm missing on? What did I do wrong exactly?
The problem with your code is the misuse of the static word. In fact, static means that is not associated with an instance of the object: this means that the content of the static member (the array_ variable in this case) is shared between every instance you will create.
That's the same for the insert_tail method, that can be used even if you don't create an instance. Now, let's try to understand what you've written in this method:
Deque d;
d.size_ += 1;
int size = d.size_;
d.array_[size - 1] = x;
return d;
In the first line, you created a new Deque object. That's the first mistake, cause you're not modifying the actual Deque. Then you add the operations, and in the end, you return the created Deque. However, this object is not saved anywhere, because when you call deque.insert_tail() you aren't assigning the returned value anywhere.
Let's try and get this a little bit more concrete.
Since what you're doing is creating a data structure, you won't need any static member. This because everything needs to be saved inside the data structure.
Then, inside the insert_tail you'll need to remove the object you created inside. It'll look something like this:
size_ += 1;
int size = size_;
array_[size - 1] = x;
With those two modifications the code will probably work as expected, however, I suggest you focus on improving the appearance of your code. Using the underscore character at the end of the variable name is a little bit confusing. In C the only scenario you can use it inside the name int foo_bar for normal variables, and at the beginning int _foo for reserved variables.
I am designing a cheat for the game WordBrain. Basically this is just a small program that takes a number of letters, permutes the sequence, then splits the sequence into 'words' which have length attribute, then search my text file to file meaningful permutation to print out.
while (next_permutation(letters.begin(), letters.end())) //loop through possible permutations of the combination
{
int position_marker = 0; //serve specific purpose
for (auto x : substring_collection) //trouble is in this loop
{
string temp;
int k = x.take_length();
try { temp = letters.substr(position_marker, k); }
catch (out_of_range) { cout << "OUT OF RANGE"; }
x.set(temp); //member content does not register change
position_marker += x.take_length(); //also member word_length is 0 now, despite their are no invocation of methods capable of changing it
}
if (all_of(substring_collection.begin(), substring_collection.end(), [&](substring & s) {return !(library.find(s.take_content()) == library.end()); }))
{
for (auto x : substring_collection)
{
cout << x.take_content() + " ";
}
}
}
This is the location the trouble stems from. Basically, substring_collection is a vector<substring>, which contains objects of class substring
Here is how the class looks like:
class substring
{
private:
std::string content;
int word_length;
public:
substring() : content(""), word_length(0) {};
substring(std::string & s, int c) : content(s), word_length(c) {};
void set(std::string & s)
{
content = s;
}
void clear()
{
content.clear();
}
void set_length(int c)
{
word_length = c;
}
void showinfo() const
{
std::cout << "Content is " + content << " Length is : " << word_length;
}
int take_length() const
{
return word_length;
}
std::string take_content() const
{
return content;
}
};
I suspect that the reason the code goes wrong is position_marker, whose value depends on the member 'word_length' of the object substring is set to 0.
In the code prior to this loop, I only the setting method for this member to take data from users' input (from std::cin).
Can you please tell me that is there any hidden mechanism that reset the property, or create brand new objects that I did not aware of?
Also, teachings on coding styles are very welcomed. I just started learning to code so any tips are much appreciated.
for (auto x : substring_collection)
Here, x is of type substring. This is a copy of the element in the vector, and then when you modify it, you only modify the copy, not the original.
You'll have to use a reference to modify the original element in the vector
for (auto& x : substring_collection)
For why word_length is 0, I don't know, it isn't in the code you posted. My guess would be that you resized the vector, which called the default constructor of substring, which set word_length to 0.
I'm a beginner in C++ and I understand basic concepts of "pass-by-value or reference", object scope and object instantiation with and without use of the keyword "new" in simple examples. The problem is that when the problems that I'm trying to solve become more complicated, I don't know how is this theory I know from simple examples applied in problems that consists of multiple classes.
I have a PaintWidget.cpp which is responsible for painting all the Vehicles.
void PaintWidget::paintEvent(QPaintEvent *) {
if (!Vehicle::GetVehicles()->empty()) {
cout << "not null" << endl;
QPainter painter(this);
QPen pen1(Qt::red);
pen1.setWidth(2);
std::vector<Vehicle>::iterator it;
for (it = Vehicle::GetVehicles()->begin(); it != Vehicle::GetVehicles()->end(); it++) {
cout << "draaaaw" << endl;
QRect rect(it->GetXcord(), it->GetYcord(), it->GetWidth(), it->GetHeight());
cout << std::to_string(it->GetXcord()) + " " + std::to_string(it->GetYcord()) + " " + std::to_string(it->GetWidth()) + " " + std::to_string(it->GetHeight()) + " " << endl;
painter.setPen(pen1);
painter.drawRect(rect);
}
} else {
cout << "is null" << endl;
}
}
And then I have Vehicle.h
#ifndef VEHICLE_H
#define VEHICLE_H
#include <vector>
#include <map>
class Vehicle {
public:
Vehicle();
Vehicle(const Vehicle& orig);
virtual ~Vehicle();
void initVehicles();
Vehicle createVehicle();
static std::map<Road, int> &GetVeh_num() {
return veh_num;
}
static std::vector<Vehicle> *GetVehicles() {
return &vehicles;
}
private:
int xcord;
int ycord;
int height = 20;
int width = 50;
static std::vector<Vehicle> vehicles;
static std::map<Road, int> veh_num;
};
#endif /* VEHICLE_H */
Vehicle.cpp
#include "Vehicle.h"
#include <vector>
std::vector<Vehicle> Vehicle::vehicles;
std::map<Vehicle::Road, int> Vehicle::veh_num = {
{Vehicle::Top, 0},
{Vehicle::Right, 0},
{Vehicle::Bottom, 0},
{Vehicle::Left, 0}
};
Vehicle::Vehicle() {
}
Vehicle::Vehicle(const Vehicle& orig) {
}
Vehicle::~Vehicle() {
}
int Vehicle::GetXcord() const {
return xcord;
}
int Vehicle::GetYcord() const {
return ycord;
}
void Vehicle::SetXcord(int xcord) {
this->xcord = xcord;
}
void Vehicle::SetYcord(int ycord) {
this->ycord = ycord;
}
int Vehicle::GetHeight() const {
return height;
}
int Vehicle::GetWidth() const {
return width;
}
void Vehicle::initVehicles() {
for (int i = 0; i < 5; i++) {
Vehicle::vehicles.push_back(this->createVehicle());
}
}
Vehicle Vehicle::createVehicle() {
std::map<Vehicle::Road, int>::iterator it;
Vehicle v;
for (it = Vehicle::veh_num.begin(); it != Vehicle::veh_num.end(); it++) {
int &vehnum = it->second;
if (it->first == Vehicle::Road::Right) {
int xc = 520 + vehnum * this->GetWidth() + vehnum * 5;
int yc = 220;
v.SetXcord(xc);
v.SetYcord(yc);
v.SetRoad(Vehicle::Right);
}
}
return v;
}
As you can see, createVehicle returns copy of a new Vehicle which is then inserted in the static variable Vehicles. GetVehicles returns pointer to vector of inserted vehicles because I don't want to return a copy. When I run this code, nothing gets painted although there are 5 objects in the static variable (paintEvent gets called and string "draaaaw" is printed 5 times). I suspected that I have a problem with object life span, so I changed
static std::vector<Vehicle> vehicles;
to
static std::vector<Vehicle*> vehicles;
and of course instantiation of Vehicle from
Vehicle v;
to
Vehicle *v = new Vehicle();
which creates an object on the heap if I understand correctly. After all required changes in methods, my code works (all objects are painted). What I don't understand is when these objects get destroyed and why, if I'm returning a copy every single time. How come vector vehicles is not empty (I still have 5 "ghost" objects that do not contain any values I set earlier). As far as I understand creating objects with new is not recommended, so the second option are smart pointers?
Thanks :)
Edit
I purposely left out setters and getters in .h and .cpp file so that the code is as short as possible with only relevant information.
I think your main problem is you have defined an empty copy constructor:
Vehicle::Vehicle(const Vehicle& orig)
{
}
This means that when you add a Vehicle to a container, a copy is made that doesn't actually copy anything. You should delete your own constructor and let the compiler do the work for you.
You will never have a problem with object life span if you don't use pointers or references. It simply isn't possible for that to occur. The whole concept of "RAII" is that if the variable is accessible in code, it is valid.
To test this, also print out the coordinates and size of your vehicle. If that prints a valid result, you know that your issue lies in the paint function itself.
So just don't pass a pointer of the object unless you need to modify it, and even then return a reference instead of a pointer. Just make sure not to return a reference of a temporary, as that CAN lead to life span issues.
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));
}