C++ map::erase() does not erase data - c++

I'm trying to test C++ map::erase() with the following code:
//file user.h
#include <string>
#include <fstream>
#include <cstring>
using namespace std;
class User {
string name;
int id;
public:
User(const string& name, int id) : name(name), id(id) {}
int getID() const {return id;}
~User(){}
};
//file main.cpp
#include "user.h"
using namespace std;
typedef map<string, User*> Dict;
int main()
{
Dict dict;
dict["Smith"] = new User("Smith", 666); //Id = 666
dict["Adams"] = new User("Adams", 314); //Id = 314
auto it = dict.find("Adams"); //look for user 'Adams'
if (it == dict.end())
//show 'not Found' if didn't find 'Adams'
cout << "not Found" << endl;
else
//else, show the Id = 314
cout << "id1: " << it->second->getID() << endl;
//Here I think there is a problem
//I ask to delete Adams from the list
dict.erase(it);
//So in this print the ID shouldn't be found
cout << "id2: " << it->second->getID() << endl;
return 0;
}
After I try to delete the item from the list it seems like it is not deleted as the program shows the following:
pc#pc:~/Test$ ./main
id1: 314
id2: 314
As I understand id2 shouldn't show any value. Is this good or did I misunderstood the use of erase. If yes, how can I delete the item after it is shown?

you are in undefined behavior land. You are using an iterator (it) after you have modified the map. Anything can happen - including apparently working (a bit). You shoud redo
auto it = dict.find("Adams"); //look for user 'Adams'
this will not find anything

Basically you have undefined behavior calling
dict.erase(it);
//So in this print the ID shouldn't be found
cout << "id2: " << it->second->getID() << endl;
The iterator variable isn't somehow reset when it was used with dict.erase(it);.
Also you should take care to call delete before using erase(). Otherwise you would leak memory.

You're erasing a pointer from the map, but the object being pointed to by the map isn't being erased. You need to spend some time learning about memory management in c++.

Related

Calling a string getter function from a header file

I'm learning C++, and I'm just messing around with putting classes in separate files for practice. I have a getter function, which returns a string (because the variable is saved as a string). However, from my main() function, I am not sure how to call it. I know the problem is probably that I need to include string somewhere when I call the object, but I have no idea how to format it.
I know this is a pretty newbie questions, but I couldn't find the answer anywhere. Could someone help me out?
(p.s. I'm not trying to get this specific code to work, since it's useless. I'm just trying to learn how to apply it for future reference).
I've tried throwing in string in a couple of places when calling or creating the object, but I always get an error. I know I could get around it by not encapsulating the variable or not having a separate class file, but that's not what I want.
main.cpp
#include <iostream>
#include "usernameclass.h"
#include <string>
using namespace std;
int main()
{
usernameclass usernameobject;
usernameobject.getUsername();
return 0;
}
usernameclass.h
#ifndef USERNAMECLASS_H
#define USERNAMECLASS_H
#include <string>
class usernameclass
{
public:
usernameclass();
std::string getUsername();
void setUsername(std::string name);
askUsername();
private:
std::string usernameVar = "test";
};
#endif
usernameclass.cpp
#include "usernameclass.h"
#include <iostream>
#include "username.h"
#include <string>
using namespace std;
string usernameclass::getUsername(){
return usernameVar;
cout << "test cout" << endl;
}
usernameclass::askUsername(){
string name;
cout << "What is your name?" << endl;
cin >> name;
setUsername(name);
cout << "Ah, so your name is "+usernameVar+", great name I guess!" << endl;
cin.get();
cin.get();
cout << "You're about to do some stuff, so get ready!" << endl;
}
usernameclass::usernameclass(){}
void usernameclass::setUsername(string name){
string* nameptr = &usernameVar;
*nameptr = name;
}
Expected result: runs getUsername() function and returns usernameVar
Actual result: doesn't run the getUsername() function
The current code would not compile, because you have not specified return type of 'askUsername()' routine, which is 'void', I believe.
Other things are good, apart from an output in 'getUsername()', which happens after returning from the function and about which you should have received a warning, I guess.
To the question: you can call that 'get' method in 'main()' as:
cout << usernameobject.getUsername();
Your code should be structured more like this instead:
main.cpp
#include <iostream>
#include "usernameclass.h"
int main()
{
usernameclass usernameobject;
// optional:
// usernameobject.askUsername();
// do something with usernameobject.getUsername() as needed...
return 0;
}
usernameclass.h
#ifndef USERNAMECLASS_H
#define USERNAMECLASS_H
#include <string>
class usernameclass
{
public:
std::string getUsername() const;
void setUsername(std::string name);
void askUsername();
private:
std::string usernameVar = "test";
};
#endif
usernameclass.cpp
#include <iostream>
#include "usernameclass.h"
std::string usernameclass::getUsername() const {
return usernameVar;
}
void usernameclass::setUsername(std::string name) {
usernameVar = name;
}
void usernameclass::askUsername() {
std::string name;
std::cout << "What is your name?" << std::endl;
std::getline(std::cin, std::name);
setUsername(name);
std::cout << "Ah, so your name is " << getUsername() << ", great name I guess!" << std::endl;
std::cout << "You're about to do some stuff, so get ready!" << std::endl;
}

STL vector containing vector causing segfault

The following code causes a segfault when I try to issue my push_back call. What am I doing wrong?
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
std::string * foo = new std::string("hello world");
cout << *foo << endl;
std::vector<std::vector<std::string *> > my_vecs;
my_vecs[0].push_back(foo); // segfaults
cout << "trying to print my_vecs size of " << my_vecs.size() << " but we never reach that point due to segfault " << endl;
return 0;
}
I'm pretty sure I'm violating one of the contracts for using vector, as the problem is surely not with the STL implementation.
When you create my_vecs it has 0 elements, hence my_vecs[0] does not exists and gives segfault. You have to first reserve at least one element of my_vecs and then you can insert in the vector my_vecs[0] your pointer:
std::vector<std::vector<std::string *> > my_vecs(1);
my_vecs[0].push_back(&foo);
The outer vector must first be explicitly grown, before one can push to its elements.
This may be a little surprising since STL map's automatically insert their keys. But, it's certainly the way it is.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
const int DESIRED_VECTOR_SIZE = 1;
std::string * foo = new std::string("hello world");
cout << *foo << endl;
std::vector<std::vector<std::string *> > my_vecs;
for (int i = 0; i < DESIRED_VECTOR_SIZE; ++i) {
std::vector<std::string *> tmp;
my_vecs.push_back(tmp); // will invoke copy constructor, which seems unfortunate but meh
}
my_vecs[0].push_back(foo); // segfaults
cout << "now able to print my_vecs size of " << my_vecs.size() << endl;
return 0;
}

Search for key by vector in map

So, we have a school-project in creating a phonebook where you should be able to look up phone numbers by searching for the name. I decided to use a map with a string for the phone number and and a vector of strings for the name, due associated number should be able to have multiple names in it.
However, due to us jumping straight from Python to C++ without any explanation of the syntax or the language, I am having a hard time coming up with a way to look for the number by searching for names.
The class I am using looks like this
class Telefonbok
{
public:
void add(string namn, string nummer)
{
map<string, vector<string>>::iterator it = boken.find(nummer);
if (it != boken.end())
{
cout << "This number already exists, please choose another";
}
else
{
namn_alias.push_back(namn);
boken[nummer] = namn_alias;
}
}
void lookup(string name)
{
for (map<string, vector<string>>::iterator sokning = boken.begin(); sokning != boken.end(); sokning++)
cout << "Hello!";
}
private:
vector<string> namn_alias;
string nummer;
map<string, vector<string>> boken;
};
What I am trying to do in lookup function is to search for a phone number by the names in the vector, but I am stumped on how to proceed with looking through the vector inside the for-loop.
The plan was to go through the Map keys one by one to find the vector that contains the searched-for name. Any tips on how to proceed or some functions I have missed that can be used for this?
Algirdas is correct, you should read up on C++.
Assuming you are mapping name to 1-or-more numbers, but only 1 number per name...
#include <cstddef>
#include <iostream>
#include <map>
#include <string>
#include <vector>
using std::cout;
using std::endl;
using std::map;
using std::string;
using std::vector;
class Telefonbok
{
public:
void add(string namn, string nummer) {
auto it = nummer_namn.find(nummer);
if (it != nummer_namn.end()) {
cout << "This number already exists, please choose another" << endl;
}
else {
nummer_namn[nummer] = namn;
namn_nummer[namn].push_back(nummer);
}
}
void lookup(string name) {
auto it = namn_nummer.find(name);
if (it == namn_nummer.end()) {
cout << "Unable to find any numbers for " << name << ", sorry." << endl;
return;
}
for (auto const& sokning : it->second)
cout << name << " : " << sokning << endl;
}
private:
map<string, vector<string>> namn_nummer;
map<string, string> nummer_namn;
};
int main() {
Telefonbok bok;
bok.add("Eljay", "789");
bok.add("Eljay", "456");
bok.add("Beaker", "123");
bok.lookup("Eljay");
bok.lookup("Beaker");
bok.lookup("Bunsen Honeydew");
return EXIT_SUCCESS;
}

Getting backwards output when iterating through a map and printing content

When I read through the code posted below it appears that the output printed would be:
Syndy
James
Phuong
Germaine
Agatha
Anges
Jack
However, when I run it I get Germaine and his vector of friends printed first, then Syndy and her vector of friends second:
Germaine
Agatha
Anges
Jack
Syndy
James
Phuong
Can anyone help me understand why Germaine, the second key, is accessed and printed first in the printFacebook function instead of Syndy?
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;
void printFacebook(map<string, vector<string>>& m) {
for (pair<string, vector<string>> p : m) {
cout << p.first << endl;
for (string f : p.second) {
cout << "\t" << f << endl;
}
}
}
int main() {
map<string, vector<string>> facebook;
facebook["Syndy"].push_back("James");
facebook.find("Syndy")->second.push_back("Phuong");
//map<string, vector<string>>::iterator f = facebook.find("Syndy");
//auto f = facebook.find("Syndy");
facebook["Germaine"];
facebook.find("Germaine")->second.push_back("Agatha");
facebook.find("Germaine")->second.push_back("Anges");
facebook.find("Germaine")->second.push_back("Jack");
printFacebook(facebook);
}
A std::map stores its items ordered by the value of the keys. In your case, the keys are "Syndy" and "Germaine". The default ordering of those is such that "Germaine" is first and "Syndy" is second.
Hence, when you iterate over the items of the map, the item corresponding to the key "Germaine" is seen before the item corresponding to the key "Syndy".
That explains the output.

Strange behavior for int / counter

I'm trying to add some songs to a vector inside a class. One of the values I'm storing is an int representing the song. It's essentially a counter. The first song I add should have the value 1, the second value two and so forth. But It's getting other strange values like big random numbers (positives and negatives). I can't wrap my head around what I'm doing wrong. This is the code:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Jukebox{
public:
void addSong(string artist, string title, string filename) {
song s {++songCounter, artist, title, filename};
Songs.push_back(s);
}
void printSong (int song) {
cout << Songs[song].no << ". ";
cout << Songs[song].artist << " - ";
cout << Songs[song].title << " : ";
cout << Songs[song].filename << endl;
}
private:
struct song {
int no;
string artist;
string title;
string filename;
};
vector<song> Songs;
int songCounter;
};
int main() {
Jukebox jbox;
jbox.addSong("U2", "Magnificent", "U2-Magnificent.mp3");
jbox.addSong("Sting", "Englishman in New York", "Sting-Englishman_in_New_York.mp3");
jbox.addSong("U2", "One", "U2-One.mp3");
jbox.printSong(0);
jbox.printSong(1);
jbox.printSong(2);
return 0;
}
Update
Ok, I'm probably stupid and should read more about classes before trying to implement this. But I think I did read and I still don't get it. This is what my class looks like now (which won't work):
class Jukebox(): songCounter(0)
{
public:
void addSong(string artist, string title, string filename) {
songCounter++;
song s {songCounter, artist, title, filename};
Songs.push_back(s);
}
void printSong (int song) {
cout << Songs[song].no << ". ";
cout << Songs[song].artist << " - ";
cout << Songs[song].title << " : ";
cout << Songs[song].filename << endl;
}
private:
int songCounter;
struct song {
int no;
string artist;
string title;
string filename;
};
vector<song> Songs;
};
Final word
Ok. From the example I've seen of c++ contructor classes I had some kind of wrong impression of how they worked. Now I think I'm getting it a little bit more. But the syntax still seems strange to me. But I try to read more so I really understand it. Here is what I did and to seems to work:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Jukebox {
public:
void addSong(string artist, string title, string filename) {
songCounter++;
song s {songCounter, artist, title, filename};
Songs.push_back(s);
}
void printSong (int song) {
cout << Songs[song].no << ". ";
cout << Songs[song].artist << " - ";
cout << Songs[song].title << " : ";
cout << Songs[song].filename << endl;
}
Jukebox(): songCounter(0) {} // Constructor
private:
int songCounter;
struct song {
int no;
string artist;
string title;
string filename;
};
vector<song> Songs;
};
int main() {
Jukebox jbox;
jbox.addSong("U2", "Magnificent", "U2-Magnificent.mp3");
jbox.addSong("Sting", "Englishman in New York", "Sting-Englishman_in_New_York.mp3");
jbox.addSong("U2", "One", "U2-One.mp3");
jbox.printSong(0);
jbox.printSong(1);
jbox.printSong(2);
return 0;
}
You did not initialize songCounter in your constructor.
Jukebox(): songCounter(0),//....other members
If you do not initialize it, then it may have any random value and that leaves your program in an Undefined State.
Always be careful while using unitialized variables, it often leads to Undefined Behavior and your program is a good example of it.
Also, I am not sure of your design but probably it should be a static member if you want to use it as a counter, which maintains state for all objects of your Song class.
Or
You will have to explicitly set it to a proper value at time of creating a Song object.
Okay its a counter for JukeBox and not Song class so its still okay to be a member.
You didn't initialize the variable songCounter.
Add the following to the class definition of Jukebox:
Jukebox(): songCounter(0) {}
you need a constructor for Jukebox and in that you need to initialise the counter to 0.
I think you should initialize songCounter to be 0. In the public part of the class:
public Jukebox() : songCounter(0) {}
Where do you initialise songCounter? In C++, primitives aren't zero initialised by default. You need to add
: songCounter(0)
to your constructor.