Pointer to base class lost in while loop, causing segfault. C++ - c++

This code uses a while loop to get user input and execute the appropriate command - I've reduced it to 2 commands for sake of brevity.
The Oblock object is created correctly (command "O"), as is the pointer to the base class. It appears that the calls to both objects work correctly as well.
However, after returning to the while loop, the pointer to the object appears to be lost, and attempting to access its members (command "t") causes a segfault.
I've included the example code below - my questions are afterwards.
#include<vector>
#include<iostream>
#include<string.h>
using namespace std;
class Tetramino {
private:
int squareSize;
vector<string> myShape;
public:
void setValues(int size) {
squareSize = size;
myShape = vector<string> ((size*size), ".");
}
string getValues(int i) {
return myShape[i];
}
int getSize() {
return squareSize;
}
};
class Oblock : public Tetramino {
public:
Oblock() {
setValues(2);
}
};
main () {
string input;
bool runProgram = true;
Tetramino *pBlock;
while (runProgram) {
cin >> input;
if (input == "O")
{
Oblock myBlock;
cerr << "0thi: " << myBlock.getValues(0) << endl;
Tetramino *pBlock = &myBlock;
cerr << "0thi: " << pBlock->getValues(0) << endl;
}
if (input == "t")
{
cerr << "0thi: " << pBlock->getValues(0) << endl;
}
}
return 0;
}
Are objects deconstructed upon exiting the if statements?
Is there perhaps a better way to repeatedly get user input?
Thanks in advance for any advice! I searched for questions similar to this one and couldn't find one appropriate for my needs.

Tetramino *pBlock is local within its scope. You're shadowing over the one in main with the one within the if.
Also, myBlock is local and will be destructed - you'll have a dangling pointer. You should allocate with new (and delete...)
Instead of Tetramino *pBlock = &myBlock; do pBlock = new Oblock; when you handle the "O" input (and handle delete pBlock of the previous one).

if (input == "O")
{
Oblock myBlock;
cerr << "0thi: " << myBlock.getValues(0) << endl;
Tetramino *pBlock = &myBlock;
cerr << "0thi: " << pBlock->getValues(0) << endl;
}
An object with automatic storage duration (commonly called a function-local variable) has a lifetime beginning after its declaration and ending at the end of the nearest enclosing block } token. So myBlock is destroyed at the end of this if statement, and can't be used again.
Also note that you have declared two different pointers named pBlock. Assigning the inner one does nothing to the earlier one, which is still uninitialized.

Answering your first question: Yes, the statement
Oblock yblock;
Creates an instance of Oblock on the stack. It is destroyed when the code leaves the respective block, so any pointers to it become invalid after that. To create an object that lives as long as you wish, use new to create the object on the heap.

Your pointer becomes garbage after myBlock goes out of scope (where the if statement closes) which will be causing the segmentation fault when you try to access it later
if(input == "O"){
Oblock myBlock;
}
^^^^ right here myBlock becomes garbage
Also, if a user inputs "t" before "O" it will cause a segmentation fault because they will be trying to access an uninitialized pointer. You should probably get that looked at.

Related

How can I read .txt files, to inherited objects in c++?

I'm doing an assignment, where I have to create a database in c++ for a sport organization, without using STL (I created my own list and String). The database is keeping the data in a doubly linked list, where besides the nodes, there is the data, which are the teams. Of course there is not just one type of teams, there is currently three. These objects, inherited the teams objects. I have made everything work, except reading the text file, and thus creating objects.
I have already tried, to create a marker, which is the first piece of information the program will read, which will decide which of the three classes needs to be created, then reads the other data, to create the new object, then put it in the end of the doubly linked list. Unfortunately it does'nt work, instead it does nothing, and continue the whole programme, like nothing happened.
void hozzaad(ListaElem *s, team *data) { ///adding to the end
ListaElem *iter = s;
while (iter->kov=NULL)
{
iter = iter->kov;
}
ListaElem *uj = new ListaElem(data);
uj->elozo = iter;
iter->kov = uj;
}
void listaz(ListaElem *s) { //print out all that is in the list
if (s == NULL) {
std::cout << "Ures lista" << std::endl;
return;
}
ListaElem *iter = s;
while (iter!=NULL)
{
iter->adat->kiirt(std::cout);
iter = iter->kov;
}
}
void listament(ListaElem *s, const char *a) { //this one creates the file
std::ofstream file;
file.open(a);
ListaElem *iter = s;
if (file.is_open()) {
while (iter != NULL) {
file << iter->adat->Getclub()<< "\n";
file << iter->adat->Getname() << "\n" << iter->adat->Getmember()<< "\n";
if (iter->adat->Getclub() == 1) {
file << iter->adat->Getsupport() << "\n";
}
if (iter->adat->Getclub() == 2) {
file << iter->adat->Getpompom() << "\n";
}
if (iter->adat->Getclub() == 3) {
file << iter->adat->Getname1() << "\n" << iter->adat->Getname2() << "\n";
}
iter = iter->kov;
}
}
else
{
std::cout << "Nem tudom kinyitni a file-t";
}
file.close();
return;
};
void test4() { // the basic test
Handball c("Kezes HC", 21, 50000);
team adat("", 0);
ListaElem *egyik = new ListaElem(&adat);
hozzaad(egyik,&c);
ListaElem *uj = new ListaElem(&adat);
listament(egyik, "test.txt");
std::ifstream file;
file.open("test.txt");
if (!(file.is_open())) {
std::cout << "hiba\n";
return;
}
int micsoda;
while (file >> micsoda);
{
if (micsoda == 1) {
String beolvas("");
int m;
int d;
getline(file, beolvas);
file >> m;
file >> d;
Handball ujh(beolvas, m, d);
hozzaad(uj, &ujh);
beolvas = "";
}
if (micsoda == 2) {
String fbeolvas("");
int fm;
String e1("");
String e2("");
getline(file, fbeolvas);
file >> fm;
getline(file, e1);
getline(file, e2);
football ujh(fbeolvas, fm, e1, e2);
hozzaad(uj, &ujh);
}
if (micsoda == 3) {
String bbeolvas("");
int bm;
int bd;
getline(file, bbeolvas);
file >> bm;
file >> bd;
Basketball ujh(bbeolvas, bm, bd);
hozzaad(uj, &ujh);
}
}
std::cout << "OK" << std::endl;
listaz(uj);
file.close();
std::cout << "OK" << std::endl;
}
test4() expects to go out like:
OK
Kezes HC 21 50000
OK
Shortening the code to the absolutely necessary:
if (micsoda == 1)
{
Handball ujh;
hozzaad(uj, &ujh);
} // at this point in code, your object ujh runs out of scope!!!
At the time the object runs out of scope, it is destroyed (you will notice if you add some output statement in the class' destructor...) and any pointers to it get invalid; especially, those in the list get invalid (one speaks of dangling pointers – or references, for which the same can happen). Now using them results in undefined behaviour – which means anything could happen. If you're unlucky (or lucky, depending on point of view), your program even crashes.
Be aware that, due to lacking important parts of your code, I am assuming that you only store pointers to the objects in your ListaElem class, which is pretty likely, though, as the classes to be stored are polymorphic...
What you need, though, are objects living longer than just while the programme is in the if block. Assuming we target some real-life scenario, just moving the objects out of the if clauses is no option as we might need more than one single object of the same type. So you will create the objects dynamically. However, then you'll have to deal with memory management and the ownership question as well. Easiest to handle the memory management part is using smart pointers. Ownership? Well, it appears reasonable to me to assume the list being the sole owner of the objects, so you could have:
class ListElem
{
std::unique_ptr<Team> m_data;
public:
ListElem(std::unique_ptr<Team> data) // accepting a unique_ptr already here indicates
// clearly that the item will grab ownership
: m_data(std::move(data)) // unique_ptr is only movable, not copiable!
{ }
}
Then you can change your code above to:
if (micsoda == 1)
{
int n, m; // sample data
hozzaad(uj, std::make_unique<Team>(n, m); // arguments are passed directly to
// the constructor of class Team
}
OK, you are not allowed to use STL; then you'd write your smart pointers on your own, as well as make_unique and move template functions (just as you did for list and string already). On cppreference, you can find a sample implementation, e. g. for std::make_unique. Don't just copy/paste the code, but understand it first, and best re-write it from scratch on your own, otherwise you won't be learning anything (same applies for my code above). Or maybe you ask your teacher if she/he makes an exception on STL for smart pointers.
Final advice: Operating on contiguous memory in general is much faster than operating on memory potentially distributed all over your system RAM. So you might consider rather re-implementing std::vector instead of std::list.

C++ A constructor triggers a destructor

I'm trying to make a Monster generator using vectors and classes to allow the user to create and delete Monster objects as they wish. However, when I call the constructor of Monster in my code it also immediately triggers a destructor, thereby removing Monster from the the vector container.
This is the fragment of my code, which creates and destroys the Monster object:
while (p == 't')
{
cout << "Generate (g) or remove (u) a monster?" << endl;
cin >> p;
while (p == 'u' && Monster::n == 0)
{
cout << "No monsters to remove." << endl;
cout << "Generate (g) or remove (u) a monster?" << endl;
cin >> p;
}
while (p != 'g' && p != 'u')
{
cout << "Error. Follow the instructions." << endl;
cout << "Generate (g) or remove (u) a monster?" << endl;
cin >> p;
}
if (p == 'g')
{
{
vec.push_back(Monster());
vec[Monster::n].print();
}
}
if (p == 'u')
{
vec.~Monster();
}
cout << "Continue (t) or finish (n)?" << endl;
cin >> p;
while (p != 't' && p != 'n')
{
cout << "Error. Follow the instructions?" << endl;
cout << "Continue (t) or finish (n)?" << endl;
cin >> p;
}
This is the constructor for Monster:
Monster::Monster()
{
ostringstream buffer;
buffer << Monster::n;
string name = buffer.str();
Monster::setname(name);
Monster::settype(MonsterType(rand() % 7 + 1));
Monster::setattack(rand() % 25);
Monster::sethealth(rand() % 15);
cout << endl << "Monster generated." << endl << endl;
Monster::n++;
}
This is the destructor:
Monster::~Monster()
{
cout << "Monster removed." << endl << endl;
Monster::n--;
}
When I input the character 'g', it creates a Monster object as expected but then immediately destroys it, even before its stats are printed with the print() method. This means my code is not acting as expected, as it will never allow the proper creation or destruction of Monster objects.
Can someone please help me understand what's going on here?
EDIT:
Alright, I was able to somewhat solve the issue by making another constructor.
This is it:
Monster::Monster(const Monster & mon)
{
Monster::name = mon.name;
Monster::type = mon.type;
Monster::attack = mon.attack;
Monster::health = mon.health;
cout << endl << "Monster created (cpy)." << endl << endl;
Monster::n++;
}
I edited the point where the user decides to generate a Monster to the following:
{
vec.push_back(Monster());
vector<Monster> veccpy = {};
veccpy.push_back(Monster(vec[Monster::n-1]));
vec.back().print();
}
When I lauch the program, it spits out something like this:
Monster created
Monster created (cpy)
Monster removed
Monster created (cpy)
Monster created (cpy)
Monster removed
Monster's stats
Now, the odd thing is even though it uses those constructors so oddly, the program works as I intended it to. n iterates as it should, I can remove or add Monster as I intended and the Monster stats are displayed as they should.
Could someone point to what I'm doing wrong here?
In this line:
vec.push_back(Monster());
You are creating a temporary object of type Monster, which is being copied to the vec. Because it is a temporary object it will be destroyed right after the statement ends at the semicolon. It will be destroyed after it was copied to vec.
vec.~Monster();
This surely does not compile, if you wanted to clear your vector then use vec.clear()
In your code when you write Monster::setname(name);
most programmers will think you are setting a static variable. I hope you don't keep name of your monster as a static variable.
Another thing you need to fix in your code is to make sure it adheres to the rule of three (as it was in comments). Your Monster::n variable is a kind of resource which is being managed by Monster class, and to correctly manage it you need to appropriately modify it in all the constructors and operators which might create/destroy Monster class instance. Without describing it with long text below is a sample code which presents which constructors/operators are bing called in your code:
http://coliru.stacked-crooked.com/a/9184f137b7a90995
struct Monster {
Monster() { std::cout << n << ":" << __PRETTY_FUNCTION__ << "\n"; n++; }
~Monster() { std::cout << n << ":" << __PRETTY_FUNCTION__ << "\n"; n--; }
Monster(const Monster&) { std::cout << n << ":" << __PRETTY_FUNCTION__ << "\n"; n++; }
Monster& operator=(const Monster&) { std::cout << n << ":" << __PRETTY_FUNCTION__ << "\n"; n++; return *this; }
static int n;
};
int Monster::n;
int main()
{
{
std::vector<Monster> vec;
std::cout << "-------------- START\n";
vec.push_back(Monster());
std::cout << "-------------- END\n";
}
std::cout << Monster::n; // should output 0
}
-------------- START
0:Monster::Monster() // temporary created
1:Monster::Monster(const Monster&) // vector element is copy constructed
// from temporary here you have missed Monster::n++
2:Monster::~Monster() // temporary monster destroyed
-------------- END
1:Monster::~Monster() // this is a vector being destroyed
0 // this should always be 0, otherwise
// still something is missing.
vec.push_back(Monster());
This is going to create a temporary object, an instance of the Monster class, and pass it to the vector's push_back() method.
push_back() will take its parameter and copy it into the vector. Since the vector owns its object, this will involve the construction of another instance of the Monster class.
After push_back() returns the temporary object that it passed as the parameter is going to get destroyed, thus invoking your object's destructor. And that's why you see a destructor getting invoked here.
Note that when adding objects to a vector, the vector may choose to reallocate its contents, at any time. This will involve, essentially, copy-constructing new instances of the class in the vector, then destroying the old ones, as well.
Lessons learned
Spend some time reading your C++ book and learn how smart pointers work, and put smart pointers in your vector, rather than object instances, in order to avoid unwanted side effects due to temporaries.
You could also implement move constructors as an alternative solution, but using smart pointers will probably be easier.
A second alternative is to emplace_back(), rather than push_back() new instances. However that won't solve your problem completely. See "reallocation", above.
Additionally, it wouldn't hurt to make your class Rule of 3 compliant either. The real underlying reason you asked this question is likely because it's currently not.
What you are seeing is expected and it's happening because you have defied the vec as std::vector<Monster>. What does it means that you are declaring the vector that would contains the objects of Monster class.
Now the way vector works is that they need to keep all those Monster class objects alive unless you remove them from the vector. Which means, the scope of those Monstors object should be equivalent till the Object remains the vector.
Having said that, now if you look at this line of code vec.push_back(Monster());, the new Monster object hat you are declaring will have the scope till the scope of braces with in the if, and as soon it it will be out of scope, it will not be accessible. Which is why, when the push_back gets executed, it copies the given Monster object (by executing its default copy constructor, or custom one if any), and create a new object for the vector.
If you don't want to the object to be copied, what you do is as follow:
Declare vector as std::vector<std::unique_ptr<Monster>> and
call push_back as .push_back(make_unique<Monster>()).
Here instead of declaring the Monster on the local stack, you are declaring it on the Heap and using the smart pointer to manage the life of the object on the heap.

Using a map that has a abstract base class pointer and calling a derived class function

I've searched on the web and can't find any solutions to my problem I hope you can help.
So I have constructed an abstract base class and have two derived classes that represents different experiments. (one is actually a derived derived class of my base class) And I made a map as such in a separate header file to store different types of experiments.
//Map class template to store name of experiment and the experiment within a project
typedef map <string, baseData <double>*> ExpContainer;
void search(string searchName, ExpContainer exps) {
ExpContainer::iterator Iter;
Iter = exps.find(searchName); //finds the entry corresponding to searchName and returns the iterator
if (Iter != exps.end()) { //need this as if entry is not found, the return will be end iter.
cout << "Found the experiment " << Iter->first << "." << endl;
Iter->second->print();
}
else {
cout << "Sorry, experiment " << searchName << " is not found" << endl;
}
}
The print() function is different for each experiment type and I know there's a problem called slicing so I've made sure that print() is virtual in the base class. Here's my base class:
namespace mynmsp {
//base class of data can be measurements or errors
template < class T> class baseData {
public:
virtual void print() =0;
virtual ~baseData() {
cout << "Destructor called for the base class." << endl;
}
};
}
Then in my main.cpp I've constructed different types of experiment and I want to print them. Each experiment class has different implementation of the print function that overrides the print function from the base class, like:
void print(){ //do something };
And in my main.cpp I have the map defined as:
ExpContainer project;
And after I have constructed each experiment, I've asked the user for the name of the experiment (expName) and inserted into project as such:
project[expName] = &expC;
I think the insertion is fine as I tested the size of project and it was correct.
However, a runtime error occured when my search function was called like this:
search(name, project);
I don't know if there's a problem with slicing or with my pointers?
I tried to make print() a virtual function in each derived class but that doesn't seem to work either.
Apologies for the long question, please help!
Edit: I've constructed my experiments inside a do while loop while project is declared outside. The whole code is very long but its basics is something like this:
string repeatstr; //user input whether to repeat do while loop or not
bool repeat = true; //condition for staying inside do while loop
ExpContainer project; //declared project to store experiments
do {
string expName;
string ans1; //character to store user input
cout << "What is the name of your experiment? " << endl;
cin >> expName;
cout << "Is this a experiment C ? (y/n)" << endl;
cin >> ans1;
if(ans1 =="y"){
//call the constructor for expC
project[expName] = &expC;
}else {
//call the constructor for expB
project[expName] = &expB;
}
cout << "Do you want to enter another experiment? (y/n)" << endl;
cin >> repeatstr;
if (repeatstr == "n") { repeat = false; }
}while (repeat); //loop over this do-while loop while repeat is true
cout << "There are " << project.size() << " in this database." << endl;
//to retrieve info from a certain experiment
string input, name;
cout << "Would you like to retrieve any experiments (y/n)? " << endl;
input = EitherInput("y", "n");
if (input == "y") {
cout << "Please enter the name of the experiment you want to retrieve: " << endl;
cin >> name;
search(name, project); //code breaks down here!
}
You are saving a pointer to the object that was already destroyed. You can check the addresses that you have in the map, most probably they are the same. You should store your experiment object in dynamic memory
if(ans1 =="y")
{
project[expName] = new expC();
} // Scope ends here and all variable created in it will be destroyed.
else
{
project[expName] = new expB();
} // Scope ends here and all variable created in it will be destroyed.
And after you are done with them you need to call delete on each pointer to avoid memory leak. Also you need to check if the items in the map are already existing, otherwise you will loose pointers to allocated memory which is automatically a memory leak.
I would recommend you to use std::share_ptr< baseData<double> > instead of bare baseData<double>*. Here you can read more about it. And also consider using typedef in order to have more clear syntax.
P.S.
The function
void search(string searchName, ExpContainer exps)
will copy whole map to its body. Use constant reference instead
void search(string searchName, const ExpContainer& exps)
But then you'll also need to declare function print as const:
virtual void print() const = 0;
and override it with const modifier:
virtual void print() const override;
And use constant iterator ExpContainer::const_iterator Iter

How do I access objects from a vector of "pointer-to-objects" in c++

I'm writing a program that creates a vector of pointer-to-objects.
How do I access the individual objects that the pointers reference from the pointer-to-objects vector?
I'm trying to call the speak() function in the class Object for each of the objects that the pointers inside the vector reference.
Thank you for your time
class Object
{
public:
void speak()
{
cout<<"Hello!"<<endl;
}
};
int main()
{
int choice;
vector<Obj*> objVector; //create empty vector of "pointer-to-object"
Object* ptrObj; //point to object
while (choice!=5)
{
cout <<"1.Create Object\n";
cout <<"2.Destroy Object\n";
cout <<"3.Print number of existing Objects\n";
cout <<"4.Tell existing Objects to say Hello\n";
cout <<"5.Quit Program"<<endl;
cout <<"Please enter your choice: ";
cin >> choice;
if (choice==5)
cout <<"\nProgram is quitting\n"<<endl;
else if (choice==1)
{
ptrObj= new Object;
ObjVector.push_back(ptrObj); //adding an Object object
}
else if (choice==2) //remove object
{
objVector.pop_back();
}
else if (choice==3)
{
cout <<"\nThere are " << objVector.size() <<" objects total.\n" << endl;
}
else if (choice==4)
{
for (int i=0; i<objVector.size(); i++)
{
????????????
}
}
}
return 0;
}
In your existing code, you can access the pointer exactly the way you use it elsewhere in code:
Object* obj = objVector[i];
obj->speak();
// or, simply:
objVector[i]->speak();
Using the operator -> is simply another way to say (*objVector[i]).speak().
Alternatively, the idiomatic approach to writing the loop would look like this:
for(vector<Object*>::iterator it = objVector.begin(); it != objVector.end(); ++it) {
// iterators work like another level of pointers, and need to be dereferenced:
(*it)->speak();
}
If your compiler supports C++11, you can rewrite the loop like this:
for(auto it = std::begin(objVector); it != std::end(objVector); ++it) {
(*it)->speak();
}
Or like this, using range-based for, which dereferences the iterator for you:
for(auto& obj : objVector) {
obj->speak();
}
As an aside, there are cases where you will not be sure whether objVector[i] is even in the vector at all, and accessing it may crash your program or even cause demons to fly forth from your nasal cavity.
For added safety, you can reference positions in your vector with the at function, like so:
try {
for (int i=0; i<objVector.size(); i++)
{
Object* obj = objVector.at(i);
obj->speak();
}
} catch (const std::out_of_range& ex) {
cerr << "no object at position " << i << " in objVector" << endl;
cerr << "objVector says " << ex.what() << endl;
}
Notice, though, that this is a lot slower, although it gives you a chance to handle the problem in the catch block. The try block will run the loop and stop and run the catch block if the at function throws an exception - which will be an exception of type out_of_range. Note also that using [i] will not do the same thing, because it does not throw an exception - it doesn't even bother to check if i is within the length of the vector. This happens to also be why [i] is faster than .at(i).
Finally, also notice that the loops using iterators cannot encounter this particular problem, so long as you don't try to use the iterators after adding or removing something from the vector.
The easiest way is to use *(objVector[i])
To access speak, objVector[i]->speak is just shorter.
You can dereference them with *. Like *(ObjVector[i])
But if you just need to call a method of object you can do it with ->
ObjVector[i]->speak()
Unrelated to the question, but I drop some comment to revise the program.
As others pointed out, you can call object function from pointer contained on vector with doing objVector[i]->speak().
However, as #greyfade pointed out, there is leaking memory issue. You have to delete object when the object is created by new. You can delete object by delete like this,
Object* ptr = objVector.back();
objVector.pop_back();
delete ptr;
To erase memory leaking issue, you can store Object object directly in objVector instead of Object*. In this way, you don't have to worry about deleting objects. You can do like this,
int main()
{
int choice;
vector<Object> objVector; //create empty vector of "pointer-to-object"
while (choice!=5)
{
cout <<"1.Create Object\n";
cout <<"2.Destroy Object\n";
cout <<"3.Print number of existing Objects\n";
cout <<"4.Tell existing Objects to say Hello\n";
cout <<"5.Quit Program"<<endl;
cout <<"Please enter your choice: ";
cin >> choice;
if (choice==5)
cout <<"\nProgram is quitting\n"<<endl;
else if (choice==1)
{
objVector.emplace_back(); //adding an Object object
}
else if (choice==2) //remove object
{
objVector.pop_back();
}
else if (choice==3)
{
cout <<"\nThere are " << objVector.size() <<" objects total.\n" << endl;
}
else if (choice==4)
{
for (auto& obj : objVector)
{
obj.speak();
}
}
}
return 0;
}
This code is using c++11 feature. You can add object by calling emplace_back and delete object by just calling pop_back(). Isn't this sweet?
And one more thing. You forgot some code on header. This code cannot be compiled without these headers,
#include <iostream>
#include <vector>
using namespace std;
I'd happy if this code helps you.

STL string comparison functor

I have the following functor:
class ComparatorClass {
public:
bool operator () (SimulatedDiskFile * file_1, SimulatedDiskFile * file_2) {
string file_1_name = file_1->getFileName();
string file_2_name = file_2->getFileName();
cout << file_1_name << " and " << file_2_name << ": ";
if (file_1_name < file_2_name) {
cout << "true" << endl;
return true;
}
else {
cout << "false" << endl;
return false;
}
}
};
It is supposed to be a strict weak ordering, and it's this long (could be one line only) for debug purposes.
I'm using this functor as a comparator functor for a stl::set. Problem being, it only inserts the first element. By adding console output to the comparator function, I learned that it's actually comparing the file name to itself every time.
Other relevant lines are:
typedef set<SimulatedDiskFile *, ComparatorClass> FileSet;
and
// (FileSet files_;) <- SimulatedDisk private class member
void SimulatedDisk::addFile(SimulatedDiskFile * file) {
files_.insert(file);
positions_calculated_ = false;
}
EDIT: the code that calls .addFile() is:
current_request = all_requests.begin();
while (current_request != all_requests.end()) {
SimulatedDiskFile temp_file(current_request->getFileName(), current_request->getResponseSize());
disk.addFile(&temp_file);
current_request++;
}
Where all_requests is a list, and class Request is such that:
class Request {
private:
string file_name_;
int response_code_;
int response_size_;
public:
void setFileName(string file_name);
string getFileName();
void setResponseCode(int response_code);
int getResponseCode();
void setResponseSize(int response_size);
int getResponseSize();
};
I wish I could offer my hypotesis as to what's going on, but I actually have no idea. Thanks in advance for any pointers.
There's nothing wrong with the code you've posted, functionally speaking. Here's a complete test program - I've only filled in the blanks, not changing your code at all.
#include <iostream>
#include <string>
#include <set>
using namespace std;
class SimulatedDiskFile
{
public:
string getFileName() { return name; }
SimulatedDiskFile(const string &n)
: name(n) { }
string name;
};
class ComparatorClass {
public:
bool operator () (SimulatedDiskFile * file_1, SimulatedDiskFile * file_2) {
string file_1_name = file_1->getFileName();
string file_2_name = file_2->getFileName();
cout << file_1_name << " and " << file_2_name << ": ";
if (file_1_name < file_2_name) {
cout << "true" << endl;
return true;
}
else {
cout << "false" << endl;
return false;
}
}
};
typedef set<SimulatedDiskFile *, ComparatorClass> FileSet;
int main()
{
FileSet files;
files.insert(new SimulatedDiskFile("a"));
files.insert(new SimulatedDiskFile("z"));
files.insert(new SimulatedDiskFile("m"));
FileSet::iterator f;
for (f = files.begin(); f != files.end(); f++)
cout << (*f)->name << std::endl;
return 0;
}
I get this output:
z and a: false
a and z: true
z and a: false
m and a: false
m and z: true
z and m: false
a and m: true
m and a: false
a
m
z
Note that the set ends up with all three things stored in it, and your comparison logging shows sensible behaviour.
Edit:
Your bug is in these line:
SimulatedDiskFile temp_file(current_request->getFileName(), current_request->getResponseSize());
disk.addFile(&temp_file);
You're taking the address of a local object. Each time around the loop that object is destroyed and the next object is allocated into exactly the same space. So only the final object still exists at the end of the loop and you've added multiple pointers to that same object. Outside the loop, all bets are off because now none of the objects exist.
Either allocate each SimulatedDiskFile with new (like in my test, but then you'll have to figure out when to delete them), or else don't use pointers at all (far easier if it fits the constraints of your problem).
And here is the problem:
SimulatedDiskFile temp_file(current_request->getFileName(),
current_request->getResponseSize());
disk.addFile(&temp_file);
You are adding a pointer to a variable which is immediately destroyed. You need to dynamically create your SDF objects.
urrent_request = all_requests.begin();
while (current_request != all_requests.end()) {
SimulatedDiskFile temp_file(...blah..blah..); ====> pointer to local variable is inserted
disk.addFile(&temp_file);
current_request++;
}
temp_file would go out of scope the moment next iteration in while loop. You need to change the insert code. Create SimulatedDiskFile objects on heap and push otherwise if the objects are smaller then store by value in set.
Agree with #Earwicker. All looks good. Have you had a look inside all_requests? Maybe all the filenames are the same in there and everything else is working fine? (just thinking out loud here)