0

I've been programming in managed code most of the time and I was interested in going back to C++. Been knocking my head all over the place(on Google) to find an answer. So I started doing the exercises here : http://www.cplusplus.com/forum/articles/12974/ and stumbled lots of errors. I tried the cola machine(second one) and it gave me the idea of making a cola machine and just tried to initialize a list in a Machine with pointers of Drink.

I didn't want to use the Boost library as I wanted to understand how containers worked(especially list).

I will post my code after the questions :

1) I am getting the following error in the line (*it)->getDrinkName() EXC_BAD_ACCESS on the method : getDrinkName() in Drink.cpp why is that? Am i not initializing correctly the list with the Drink?

When I try this:

Drink* test = new Drink("Coke");
cout << test->getDrinkName();

it works. Is it my constructor in Drink that is making it crash?

2) Am I suppose to initialize the list in the constructor of Machine? like :

_list = new list<Drink *>();

3) Here is the code :

Drink.h

#include <iostream>
#include <string>
using namespace std;

class Drink
{
public:
    Drink(string name);
    string getDrinkName();
private:
    string _name;
};

Drink.cpp :

#include "Drink.h"

Drink::Drink(string name)
{
    _name = name;
}

string Drink::getDrinkName()
{
    return _name;
}

Machine.h

#include <iostream>
#include <list>
#include "Drink.h"
using namespace std;

class Machine
{
public:
    Machine();
    list<Drink*> getList() const;
private:
    list<Drink*> _list;
};

Machine.cpp :

#include "Machine.h"

Machine::Machine()
{

}
list<Drink*> Machine::getList() const
{
    return _list;
}

main.cpp

#include <iostream>
#include <string>
#include "Machine.h"
using namespace std;

int main () {
    Machine* machine = new Machine();

    Drink* testCoke = new Drink("Coke");
    machine->getList().push_back(testCoke);
    std::list<Drink*>::const_iterator it ;
    for(it = machine->getList().begin();it!=machine->getList().end();it++)
    {
        cout << (*it)->getDrinkName();
        delete *it;
    }


    return 0;
}

Thanx in advance!

bachibusuc
  • 31
  • 1
  • 3
  • oops i forgot the delete, here i added it! thanx. I know i can take out all dynamic allocation like all tutorials they use like list... but i want to put pointers in the list and then eventually use boost, but for now i want to know why it – bachibusuc Feb 10 '14 at 21:37
  • @juanchopanza while I agree that he should avoid the dynamic allocation, this is NOT the source of the problem, this is an issue of iterator scope. – IdeaHat Feb 10 '14 at 21:42
  • @bachibusuc Adding `delete` just made the code a lot worse. Really take a step back and start with a good C++ book. – pmr Feb 10 '14 at 21:47
  • `` Look guys, regardless of whether or not pointers are a good idea or a bad idea, beginners in C++ MUST know how they work, since there is 40+ years of code developed without smart pointers. Simply writing off a problem as the pointers whenever you see pointers is not only unhelpful, it also doesn't teach people how to use pointers correctly, and is generally mean. `` – IdeaHat Feb 10 '14 at 21:56
  • @pmr I thought I could have used this solution : http://stackoverflow.com/a/307121/2081433 – bachibusuc Feb 11 '14 at 03:38

3 Answers3

2

First of all, std::list should be just about the last priority among containers to learn about. Second, using dynamic allocation explicitly should be an even lower priority than that.

If I were going to simulate a soft-drink machine, it's a fair guess that you wouldn't find a single explicit pointer, new, delete, or iterator in anything I wrote. My first (admittedly simplified) version would probably look something like this:

#include <map>
#include <iostream>
#include <string>

int main() {
    // Stores a Drink and a quantity of that drink.
    // Establish initial stock according to drink quality.
    std::map<std::string, int> machine{ 
        { "Coke", 2 }, 
        { "Mt Dew", 97 }, 
        { "Diet Coke", 1 }
    };

    std::cout << "Please insert money and select from the following list:\n";
    for (auto const &s : machine)
        if (s.second > 0)
            std::cout << s.first << "\n";

    std::string temp;
    std::getline(std::cin, temp);
    while (machine.find(temp) == machine.end()) {
        std::cout << "\rBad name. Please a name from the list.";
        std::getline(std::cin, temp);
    }   
    --machine[temp];
    std::cout << "\nEnjoy your " << temp << "\n";   
}

At the risk of sounding condescending, your current code shows your background all too well. You start with the worst of ancient C++ practices, mix in the worst of "managed" code, and end up with an unreadable mess that barely runs limps along at all.

My advice would be that if you're going to try to write C++, rather than starting with a mindset like: "I"m going to use list and pointers", you instead start with: "what's the simplest and most effective way to solve this problem?" and act accordingly.

If your impression of that effective solution includes any raw pointers or uses of list, new or delete, you should probably stop right then and there, and do some more reading and/or thinking, because anything of the sort is a pretty solid indication that you probably don't know enough to solve the current problem well at all. If some more reading doesn't get you out of that hole, then chances are you're reading a lousy book--unfortunately, good books about C++ are nearly a rarity (you might want to check the C++ Book List for recommendations).

Community
  • 1
  • 1
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • No worries, thank you for your comment and your solution. As I said, I am digging inside. I could have done it in a main function like you did(especially the exercise is simple) but I was just looking to see how the headers,classes or even pointers actually worked. Just this example helped me look and read into value/reference returns mentionned by MadScienceDreams. – bachibusuc Feb 11 '14 at 03:23
1

The problem is you are returning the container by value, not reference. machine->getList() makes a copy on every call, which goes out of scope in the for loop. Change it to:

const list<Drink*>& Machine::getList() const;

Edit: more explicit:

Lets look at this:

std::list<Drink*>::const_iterator it ;
for(it = machine->getList().begin();it!=machine->getList().end();it++)
{
    cout << (*it)->getDrinkName();
    delete *it;
}

The first call to machine->getList() creates an additional list. We call .begin() to get a pointer to the first element of that list. That list then goes out of scope, so it is destroyed: out pointer now points to deallocated memory. Our copy of the iterator (it) is now pointing to an invalid location. When we deference it (using *(it)), we get the error your seeing.

IdeaHat
  • 7,641
  • 1
  • 22
  • 53
  • That shouldn't actually alter Drink* in the list, though? So, its a problem that he should address but I don't think is the problem causing the crash? – TJ Bandrowsky Feb 10 '14 at 21:42
  • @TJBandrowsky The problem is int `cout << (*it)->getDrinkName();` The iterator pointed to it goes out of scope. I'll make this more explicite in my answer – IdeaHat Feb 10 '14 at 21:43
  • That made it! i will read more on reference/value returns. Thanx again!! – bachibusuc Feb 10 '14 at 21:51
0

I would say, to find out, do this in your loop:

auto x = *it;
cout << x->getDrinkName();
delete x;

If you set a breakpoint on the auto x line, you should be able to see if x is equal to your previously defined testCoke. They should be the same.

TJ Bandrowsky
  • 842
  • 8
  • 12