I'm implementing the abstract data type Map for my algorithms class, and we're asked to implement it using 2 arrays.
I have done the implementation last night and everything worked fine(when we send the assignment online it is tested using autotests), and it passed every test.
Then I needed to implement it using binary search, and I did that.
This morning when I ran the code it gave me a runtime error and I think my copy constructor is the cause, because when I commented it out, the code worked fine, yet my copy constructor is exactly as my tutor implemented it.
Here is the code it crashes on:
ArrayMap<string,string> m;
string s1("Sarajevo"), s2("Zagreb");
m[s1] = "BiH";
m[s2] = "Hrvatska";
{
ArrayMap<string,string> m2(m);
ArrayMap<string,string> m3;
m3=m;
m.empty(s1);
cout << m2.numOfElements() << " ";
cout << m2[s1] << " ";
cout << m3.numOfElements() << " ";
cout << m3[s1] << " ";
}
cout << m.numOfElements();
cout << " '" << m[s1] << "' ";
m.obrisi();
cout << m.numOfElements();
cout << " '" << m[s2] << "'";
And here is the constructor:
ArrayMap(const NizMapa<TypeOfKey, TypeOfValue>& rhs){
_capacity = rhs._capacity;
_numOfElements = rhs._numOfElements;
_arrayK = new TypeOfKey[_capacity];
_arrayV = new TypeOfValue[_capacity];
for(int i = 0; i<_numOfElements; i++){
_arrayK[i] = rhs._arrayK[i];
_arrayV[i] = rhs._arrayV[i];
}
}
EDIT:
SSCE
ArrayMap<string,string> m;
string s1("Sarajevo"), s2("Zagreb");
m[s1] = "BiH";
m[s2] = "Hrvatska";
ArrayMap<string,string> m2(m);
cout << m2[s1] << " " << m2.numOfElements();
/* When I do just this it works fine, but when combined with others it crashes the program*/
EDIT: http://pastebin.com/vXdBTs4n
This is the class implementation:
NizMapa is ArrayMap
_kapacitet is _capacity
_brojE is _numOfElements
TipKljuca is TypeOfKey
TipVrijednosti is TypeOfValue
methods:
obrisi is empty/delete(2 versions)
Note:this is NOT the binary search version, it's the sequential search one. If any additional info is needed please ask, this is important.
Thanks in advance!
Here you allocate using _capacity
_capacity = rhs._capacity;
_numOfElements = rhs._numOfElements;
_arrayK = new TypeOfKey[_capacity];
_arrayV = new TypeOfValue[_capacity];
burt here you use _numOfElements
for(int i = 0; i<_numOfElements; i++){
as the loop boundary. So these two probably don't match and your are writing beyond the array boundary if _numOfElements > _capacity.
Related
I have just done a module on pointers and dynamic memory in C++ and am attempting to complete a personal assignment so that I can practice the concepts. The program manages an array of strings that are names. The goal that I set for myself is that the list is stored in the heap (to practice using "new"), and the list is dynamically sized as new names are entered.
Disclaimer: I realize that this is easily accomplished using vectors, and after struggling with this for hours I re-wrote my original code to use a vector for the list with no problems. However I want to learn where my understanding of how pointers work is broken.
The problem that I have with the program is this: I initialize the name array to have zero elements and have a function to add names that handles the dynamic sizing. When first called it seems to re-size the array correctly and add a new name to the the new array. Within the function to add a name, I can print the contents of the new array. I can also re-assign the old array pointer to the address of the new array on the heap. However when I call the print function from main after adding a name to the list, the list does not contain a name. By my understanding, since I'm using pointers I should be updating values directly, so after the add name function terminates, the values should persist. Also, if I attempt to add a second name the program crashes. What am I doing wrong with the handling of memory?
I've searched quite a bit and the closest that I can find for a resolution was this post:
How to make an array with a dynamic size? General usage of dynamic arrays (maybe pointers too)?
I modified my code based on what I understand from that but it still doesn't work properly.
#include <stdio.h>
#include <vector>
#include <iostream>
using namespace std;
void add_name_to_list(string * my_list, size_t * list_size);
string get_name();
void print_names(const string *const my_list, const size_t *const list_size);
int main()
{
string *name_list_ptr {nullptr};
name_list_ptr = new string [0];
size_t name_list_size{0};
size_t *name_list_size_ptr {&name_list_size};
print_names(name_list_ptr, name_list_size_ptr);
add_name_to_list(name_list_ptr, name_list_size_ptr);
print_names(name_list_ptr, name_list_size_ptr);
return 0;
}
void add_name_to_list (string * my_list, size_t *list_size)
{
string new_name{get_name()};
string *new_string_ptr{nullptr};
new_string_ptr = new string [*list_size+1];
// copy existing list into new list
cout << "List size is " << *list_size << " so *list size == 0 is " << (*list_size == 0) << endl;
if(*list_size == 0)
{
new_string_ptr[0] = new_name;
*list_size = *list_size +1;
cout << new_string_ptr[0] << " has been added to position " << *list_size << endl;
}
else
{
print_names(my_list, list_size);
for(size_t i{0}; i < *list_size; i++)
{
cout << "At position " << i << " original list is " << my_list[i] << endl;
new_string_ptr[i] = my_list[i];
cout << "The name " << new_string_ptr[i] << " has been added to position " << i << " of the new list" << endl;
}
new_string_ptr[*list_size - 1] = new_name;
*list_size = *list_size + 1;
}
print_names(new_string_ptr, list_size);
string *temp_ptr{nullptr};
temp_ptr = new string [*list_size-1];
cout << "temp ptr is " << temp_ptr << " and my list is " << my_list << endl;
temp_ptr = my_list;
cout << "temp ptr is " << temp_ptr << " and my list is " << my_list << endl;
my_list = new_string_ptr;
delete [] temp_ptr;
new_string_ptr = nullptr;
print_names(my_list, list_size);
}
string get_name()
{
cin.sync();
cin.clear();
string new_name{};
cout << "\nEnter the full name: ";
getline(cin, new_name);
cin.sync();
cin.clear();
if(new_name.size() <= 1)
return "0";
else
return new_name;
}
void print_names(const string *const my_list, const size_t *const list_size)
{
if(*list_size == 0)
cout << "The list is empty" << endl;
else
for(size_t j{0}; j < *list_size; j++)
cout << j << ". " << my_list[j] << endl;
}
One variation that I've tried based on what I learned from searching is:
cout << "temp ptr is " << temp_ptr << " and my list is " << my_list << endl;
//my_list = new_string_ptr;
//delete [] temp_ptr;
//new_string_ptr = nullptr;
delete [] my_list;
my_list = new string[*list_size];
my_list = new_string_ptr;
print_names(my_list, list_size);
Unfortunately the results are the same.
Without checking the logic of the implementation, your list doesn't update because you are assigning my_list = new_string_ptr; but your function received void add_name_to_list (string * my_list, size_t *list_size).
As you are newcomer to C++ world, let me explain clearly:
list_size is a pointer to a size_t, so if you modify the pointed memory, the change will persist, but if you modify the pointer itself, it will not.
list_size = new size_t; // This change doesn't persist
list_size++; // This change doesn't persist
*list_size++; // This change persists and the value of pointed memory was increased.
With my_list is happening exactly the same, you are trying to modify the pointer itself, not the pointed memory.
So, you should use:
void add_name_to_list (string * &my_list, size_t *list_size)
Or maybe you are more confortable with
void add_name_to_list (string ** my_list, size_t *list_size)
[...]
*my_list = new_string_ptr;
Hope this helps
Can someone help me with this error. I've been up all night trying to get this code to work, can't seem to figure it out.
I am trying to COUT a single value from my vector strut but I keep getting this EXC_BAD_ACCESS error. I used breaks to see if variables are as they should be and they are but the error continues. Can you help me?
vector<department> depart;
depart.push_back(department());
while(lineNum <= lineCount)
{
lineNum = lineNum;
depart[lineNum].dept = sTemp1;
depart[lineNum].building = sTemp2;
depart[lineNum].budget = sTempToInt;
depart.push_back(department());
lineNum++;
}
vector<department> deptVec;
cout << "Vector output: " << deptVec[0].budget;
}
else
{
sTemp1.erase (remove (sTemp1.begin(), sTemp1.end(), ' '), sTemp1.end()); //clean up
//sTemp2.erase (remove (sTemp2.begin(), sTemp2.end(), ' '), sTemp2.end()); //clean up
sTemp2.erase(find_if(sTemp2.rbegin(), sTemp2.rend(), bind1st(std::not_equal_to<char>(), ' ')).base(), sTemp2.end());
sTemp3.erase (remove (sTemp3.begin(), sTemp3.end(), ' '), sTemp3.end()); //clean up
sTempToInt = stoi(sTemp1);//file is instructor
vector<intructor> intruct;
intruct.push_back(intructor()); //Push back new subject created with default constructor.
for(int lineNum = 0; lineNum>= lineCount; lineNum++) //Will create a set of vectors for each line.
{
intruct[lineNum].id = sTempToInt;
intruct[lineNum].name = sTemp2;
intruct[lineNum].dept = sTemp3;
intruct.push_back(intructor()); //Adds a new element:
}
cout << sTempToInt << "\n" << sTemp2 << "\n" << sTemp3 << "\n";
}
The part that is giving me the error is
vector<department> deptVec;
cout << "Vector output: " << deptVec[0].budget;
It is suppose to output a int but throughs an error.
I checked that department.budget is holding the int. Not sure what to try.
The deptVec vector is empty and you are trying to access an element from it. Try pushing values to deptVec before cout.
I fixed the problem. For whatever reason, I was trying to create a new vector after the while loop instead of using the one being used in the while loop.
This is before:
vector<department> deptVec;
cout << "Vector output: " << deptVec[0].budget;
After(what it should be): cout << "Vector output: " << depart[0].dept;
I have such piece of code:
typedef struct reader
{
char name[50];
char card_num[50];
char title[100];
}reader_t;
int main()
{
vector<reader> vec;
ifstream input_file("D:\\lab.txt", ios::binary);
reader_t master[1];
input_file.read((char*)&master, sizeof(master));
for (size_t idx = 0; idx < 1; idx++)
{
reader temp;
strcpy(temp.name, master[idx].name);
strcpy(temp.card_num, master[idx].card_num);
strcpy(temp.title, master[idx].title);
vec.push_back(temp);
cout << "Name: " << master[idx].name << endl;
cout << "Card num: " << master[idx].card_num << endl;
cout << "Title: " << master[idx].title<<endl;
}
cout << vec.size();
getchar();
}
What is does: it reads structures from binary file into an array of structures,copies them into vector and displays structure.And yes, I do need to do like this - I need to store structures from file in vector and this is the only working way to do it I could find(if you can tell, how to read structures to vector directly from file - you are welcome).
So,everything works fine, but the problem is that I need to create a function which would be able to do the same, but with dynamic array.I wrote something like this:
void read_structs(int vec_size)
{
ifstream input_file("D:\\lab.txt", ios::binary);
//Here I commented 2 ways how I tried to create a dynamic array of structs
//reader* master = new reader[vec_size];
//reader* master = (reader*)malloc(sizeof(reader) * vec_size);
input_file.read((char*)&master, sizeof(master));
for (size_t idx = 0; idx < vec_size; idx++)
{
reader temp;
strcpy(temp.name, master[idx].name);
strcpy(temp.card_num, master[idx].card_num);
strcpy(temp.title, master[idx].title);
vec.push_back(temp);
cout << "Name: " << master[idx].name << endl;
cout << "Card num: " << master[idx].card_num << endl;
cout << "Title: " << master[idx].title<<endl;
}
}
And that worked fine too unless I tried to run it.VS wasn't higlighting error in my code, it just was throwing an exception right as the moment when the program tried to access master[0].name.
There is absolutely no point in the temp struct. See, the
vec.push_back(temp);
is already using copy constructor, so copy constructor must work and then the set of strcpy is not doing anything different from that, so just go with
vec.push_back(master[0]).
You can't read into vector directly. You do need to read into temporary. So that is correct. Except I suppose you want to read all entries from the file no matter how many of them there are, so you need to put the read itself also into the loop.
There is not much point in creating an array of one element.
reader_t master[1];
input_file.read((char*)master, sizeof(master));
// ^ you *don't* need & here, arrays degrade to pointers automatically
and
reader_t master;
input_file.read((char *)&master, sizeof(master));
// ^ but you do need & here.
are equivalent. I would go with the later.
So we are basically down to:
reader temp; // calling it temp; the master name makes no sense.
while (input_file.read((char*)&temp, sizeof(temp)))
// read returns input_file and input_file is false if last operation failed
{
vec.push_back(temp);
// verify the stored values by reading back vfrom vec.back().
cout << "Name: " << vec.back().name << endl;
cout << "Card num: " << vec.back().card_num << endl;
cout << "Title: " << vec.back().title<<endl;
}
In the second example, you didn't initialize master, so it obviously crashed.
There is a more C++ approach though. First, you define a read operator for the structure:
std::istream &operator>>(std::istream &in, reader &r) {
return in.read((char *)&r, sizeof(r));
}
and then you simply read the vector using the istream_iterator:
vec.assign(std::istream_iterator<reader>(input_file),
std::istream_iterator<reader>());
and the standard library will generate the above loop for you.
I am a classical music fan. My music collection (mp3) has been carefully classified using "composer" (ex. "Surname name (D.O.B-D.O.D"). I frequently get the "artist" from importing the music, ripping or some online data base. Because my mobile music player (Xbox) orders only by "artist", I would like to "swap":
album_artist = artist
artist = composer
and composer would simply remain the same (and same as artist). (Visual Studio 2013, W7, taglib1.9.1):
TagLib::PropertyMap tags = f.file()->properties();
unsigned int longest = 0;
for (TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
if (i->first.size() > longest) {
longest = i->first.size();
}
}
cout << "-- TAG (properties) --" << endl;
for (TagLib::PropertyMap::Iterator i = tags.begin(); i != tags.end(); ++i) {
if (i->first == "COMPOSER") {
composer = i->second;
composer_key = i->first;
}
if (i->first == "ARTIST") {
artist.append(i->second);
artist_key = i->first;
}
if (i->first == "ALBUMARTIST") {
album_artist.append(i->second);
album_artist_key = i->first;
}
cout << left << std::setw(longest) << i->first << " - " << '"' << i->second << '"' << endl;
}
if (!tags.replace(album_artist_key, artist))
cout << "album_artist_key is wrong";
else
cout << "replacing " << album_artist_key << " with " << artist << endl;
if (!tags.replace(artist_key, composer))
cout << "artist is wrong";
else
cout << "replacing " << artist_key << " with " << composer << endl;
tag->setArtist(composer.toString());
f.save();
NOTE: this code was modified starting from the tagreader.cpp code found in examples of the library.
This compiles, but after execution, all ID3 tags info disappear (corruption?), as seen by windows explorer. So, I did an experiment and commented out everything that makes any change to the tag. Basically, just open the file (FileRef) and do f.save(). This alone causes the tags to disappear.
Two questions (I think I got this completely wrong ...)
Any reason why f.save would cause a metadata corruption ?
Is the idea I am following (tags.replace and the f.save) correct?
That's not corruption; Windows Explorer just still can't read ID3v2.4 tags, a standard which came out 15 years ago, and is the default in TagLib. TagLib can, however, also write ID3v2.3 tags.
I'm getting a segmentation fault while trying to parse a big text file. The file contains 91 529 mRNA transcripts and details about these transcripts. I've created a RefSeqTranscript object that will take these details. When I parse the file, I create a list of these objects and start putting the details into these lists. It works fine for the first 1829 transcripts and then crashes with a segmentation fault. The method I'm running is:
void TranscriptGBFFParser::ParseFile(list<RefSeqTranscript> &transcripts, const char* filepath)
{
cout << "Parsing " << filepath << "..." << endl;
ifstream infile;
infile.open(filepath);
int num = 0;
RefSeqTranscript *transcript = new RefSeqTranscript();
for(string line; getline(infile, line); )
{
in.clear();
in.str(line);
if (boost::starts_with(line, "LOCUS"))
{
if((*transcript).transcriptRefSeqAcc.size() > 0)
{
cout << (*transcript).transcriptRefSeqAcc << ":" << (*transcript).gi << ":" << (*transcript).gene.geneName << ":" << ++num << endl;
transcripts.push_back(*transcript);
delete transcript;
RefSeqTranscript *transcript = new RefSeqTranscript();
}
}
else if (boost::starts_with(line, " var"))
{
TranscriptVariation variant;
(*transcript).variations.push_back(variant);
}
//Store the definition of the transcript in the description attribute
else if (boost::starts_with(line, "DEFINITION"))
{
(*transcript).description = line.substr(12);
for(line; getline(infile, line); )
{
if(boost::starts_with(line, "ACCESSION "))
break;
(*transcript).description += line.substr(12);
}
}
//The accession number and GI number are obtained from the VERSION line
else if (boost::starts_with(line, "VERSION"))
{
string versions = line.substr(12);
vector<string> strs;
boost::split(strs, versions, boost::is_any_of( " GI:" ), boost::token_compress_on);
boost::trim_left(strs[0]);
(*transcript).transcriptRefSeqAcc = strs[0];
(*transcript).gi = atoi(strs[1].c_str());
}
//Gene information is obtained from the "gene" sections of each transcript
else if (boost::starts_with(line, " gene"))
{
for(line; getline(infile, line); )
{
if(boost::starts_with(line.substr(21), "/gene="))
{
Gene *gene = new Gene();
string name = line.substr(27);
Utilities::trim(name, '\"');
(*gene).geneName = name;
(*transcript).gene = *gene;
delete gene;
break;
}
}
(*transcript).gene.geneID = 0;
}
else if (boost::starts_with(line, " CDS"))
{
(*transcript).proteinRefSeqAcc = "";
}
else if (boost::starts_with(line, "ORIGIN"))
{
(*transcript).sequence = "";
}
}
cout << (*transcript).transcriptRefSeqAcc << ":" << (*transcript).gi << ":" << (*transcript).gene.geneName << endl;
transcripts.push_back(*transcript);
delete transcript;
cout << "No. transcripts: " << transcripts.size() << endl;
cout << flush;
infile.close();
cout << "Finished parsing " << filepath << "." << endl;
}
I'm new to C++ and don't have a great understanding of how to work with pointers etc so I'm guessing I might have done something wrong there. I don't understand why it would work for almost 2000 objects before cutting out though.
The file I'm parsing is 2.1 GB and consists of about 44 000 000 lines so any tips on how to improve the efficiency would also be much appreciated.
This is probably not the only answer, but you have a leak...
if (boost::starts_with(line, "LOCUS"))
{
if((*transcript).transcriptRefSeqAcc.size() > 0)
{
cout << (*transcript).transcriptRefSeqAcc << ":" << (*transcript).gi << ":" << (*transcript).gene.geneName << ":" << ++num << endl;
transcripts.push_back(*transcript);
delete transcript;
// LEAK!
RefSeqTranscript *transcript = new RefSeqTranscript();
}
}
You probably mean:
transcript = new RefSeqTranscript();
It's hard to say anything specific unless you provide some more details:
What line does it crashed in?
Do you really need all of those transcripts at the same time?
But I would suggest you a couple improvements:
Don't use pointer (or at least use smart pointer) for the RefSeqTranscript *transcript;
Don't use pointer for the Gene *gene;
Generally, don't use pointers unless you realy need them;
And you have a bug here:
delete transcript;
RefSeqTranscript *transcript = new RefSeqTranscript();
Since you've laready declared transcript outside the loop's body, here you hide it with new variable with the same name. This causes memory leak, and moreover, you delete an outer transcript and do not replace it with anything. So, you probably get a crash on the next iteration.