Vector not clearing correctly - c++

I'm having a problem clearing a vector of vectors.
I use a tuple function to return multiple variables.
I call to the tuple function twice in succession (using a different file each time).
When I check the outputs from the function, I get the equivalent of
vector1.size = vector1.size
and
vector2.size = (vector1.size + vector2.size)
rather than
vector1.size= vector1.size and vector2.size = vector2.size.
This leads me to believe that somewhere a vector is not being correctly cleared between the function calls, and that this is causing the second output to be written on top of the first output.
Below is my code:
main() and declarations
tuple<vector<float>, vector<vector<float>>, int> ReadFile(string fileToBeRead);
vector<float> sadInterpolationStorage;
vector<vector<float>> sadInterpolationVectorStorage;
vector<float> userInterpolationStorage;
vector<vector<float>> userInterpolationVectorStorage;
int sadAnimationLength;
int inputAnimationLength;
int main() {
tie(sadInterpolationStorage, sadInterpolationVectorStorage, sadAnimationLength) = ReadFile(sadWalkCycleMocapFile);
tie(userInterpolationStorage, userInterpolationVectorStorage, inputAnimationLength) = ReadFile(inputMocapFile);
cout << sadInterpolationVectorStorage[1].size() << endl;
cout << userInterpolationVectorStorage[1].size() << endl;
return 0;
}
userInterpolationVectorStorage[1].size() will always be equal to the sum of
(sadInterpolationVectorStorage[1].size()+ sadInterPolationVectorStorage[1].size(). It seems like a vector is not being cleared somewhere, but in ReadFile() I create new variables for everything.
ReadFile()
tuple<vector<float>, vector<vector<float>>, int> ReadFile(string fileToBeRead) {
string firstLineInFile;
string secondLineInFile;
string thirdLineInFile;
vector<float> interpolationStorage;
vector<vector<float>> interpolationVectorStorage;
int animationLength = 0;
vector<vector<float>> vectorStorage;
//Create a vector to hold all the mocap data values in float form.
vector<float> floatTokens;
//Create a vector to hold an entire frame (the data and the words).
vector<string> oneFrame;
//Create an input file stream
ifstream in(fileToBeRead, ios::in);
//cases to handle the first 3 lines of the file (first 3 lines contain no data, but are necessary)
//extracts each line into an object and writes them to the top of the output file
getline(in, firstLineInFile);
getline(in, secondLineInFile);
getline(in, thirdLineInFile);
//loop until the end of the file is reached
while (in.eof() == 0) {
//create a buffer to store each frame
stringstream buffer;
//write the frameID to the file.
extractFrameID(in);
//loop around the 29 lines in a frame, push each line into the vector.
for (int i = 0; i < 29; i++) {
string tempString;
getline(in, tempString);
//if the end of the file is reached (.empty() is used as the last line in the mocap file is always an empty line.
if (tempString.empty()) {
#pragma region Storing all data vectors in a vector
vectorStorage.push_back(rootXVector);
vectorStorage.push_back(rootZVector);
vectorStorage.push_back(lowerBackXVector);
vectorStorage.push_back(lowerBackYVector);
vectorStorage.push_back(lowerBackZVector);
vectorStorage.push_back(upperBackXVector);
vectorStorage.push_back(upperBackYVector);
vectorStorage.push_back(upperBackZVector);
vectorStorage.push_back(thoraxXVector);
vectorStorage.push_back(thoraxYVector);
vectorStorage.push_back(thoraxZVector);
vectorStorage.push_back(lowerNeckXVector);
vectorStorage.push_back(lowerNeckYVector);
vectorStorage.push_back(lowerNeckZVector);
vectorStorage.push_back(upperNeckXVector);
vectorStorage.push_back(upperNeckYVector);
vectorStorage.push_back(upperNeckZVector);
vectorStorage.push_back(headXVector);
vectorStorage.push_back(headYVector);
vectorStorage.push_back(headZVector);
vectorStorage.push_back(rClavicleYVector);
vectorStorage.push_back(rClavicleZVector);
vectorStorage.push_back(rHumerusXVector);
vectorStorage.push_back(rHumerusYVector);
vectorStorage.push_back(rHumerusZVector);
vectorStorage.push_back(rRadiusXVector);
vectorStorage.push_back(rWristYVector);
vectorStorage.push_back(rHandXVector);
vectorStorage.push_back(rHandYVector);
vectorStorage.push_back(rFingersXVector);
vectorStorage.push_back(rThumbXVector);
vectorStorage.push_back(rThumbZVector);
vectorStorage.push_back(lClavicleYVector);
vectorStorage.push_back(lClavicleZVector);
vectorStorage.push_back(lHumerusXVector);
vectorStorage.push_back(lHumerusYVector);
vectorStorage.push_back(lHumerusZVector);
vectorStorage.push_back(lRadiusXVector);
vectorStorage.push_back(lWristYVector);
vectorStorage.push_back(lHandXVector);
vectorStorage.push_back(lHandYVector);
vectorStorage.push_back(lFingersXVector);
vectorStorage.push_back(lThumbXVector);
vectorStorage.push_back(lThumbZVector);
vectorStorage.push_back(rFemurXVector);
vectorStorage.push_back(rFemurYVector);
vectorStorage.push_back(rFemurZVector);
vectorStorage.push_back(rTibiaXVector);
vectorStorage.push_back(rFootXVector);
vectorStorage.push_back(rFootZVector);
vectorStorage.push_back(rToesXVector);
vectorStorage.push_back(lFemurXVector);
vectorStorage.push_back(lFemurYVector);
vectorStorage.push_back(lFemurZVector);
vectorStorage.push_back(lTibiaXVector);
vectorStorage.push_back(lFootXVector);
vectorStorage.push_back(lFootZVector);
vectorStorage.push_back(lToesXVector);
#pragma endregion
//loop for every data type, 58 or so
for (size_t i = 0; i < vectorStorage.size(); i++) {
//interpolate between each data value and store the result in a vector.
//loop for every data value of every data type , 227 or so (1 per frame).
for (size_t j = 1; j < vectorStorage[i].size(); j++)
{
interpolationStorage.push_back(vectorStorage[i][j] - vectorStorage[i][j - 1]);
}
interpolationVectorStorage.push_back(interpolationStorage);
interpolationStorage.clear();
}
vectorStorage.clear();
cout << "Reading of " << fileToBeRead << " completed" << endl;
return make_tuple(interpolationStorage, interpolationVectorStorage, animationLength);
}
oneFrame.push_back(tempString);
}
//populate the buffer with the vector.
copy(oneFrame.begin(), oneFrame.end(), ostream_iterator<string>(buffer, "\n"));
//split the buffer up into tokens(objects) and store them into a vector
vector<string> mainTokenVector = split(buffer.str(), ' ');
//defining vectors.
vector<float> floatTokenVector;
vector<string> stringTokenVector;
//loop to split up the token vector into strings and floats, and store them in vectors
for (size_t i = 0; i < mainTokenVector.size(); i++) {
//if the token is a string, put it in the string vector
if (isFloat(mainTokenVector[i]) == 0) {
stringTokenVector.push_back(mainTokenVector[i]);
}
//if the token is a float, put it in the float vector
else if (isFloat(mainTokenVector[i]) == 1) {
floatTokenVector.push_back(stof(mainTokenVector[i]));
}
}
#pragma region Pushing data values to vectors
//pushing all data values to their responding vectors in order to interpolate between them later.
rootXVector.push_back(floatTokenVector[0]);
rootZVector.push_back(floatTokenVector[2]);
lowerBackXVector.push_back(floatTokenVector[6]);
lowerBackYVector.push_back(floatTokenVector[7]);
lowerBackZVector.push_back(floatTokenVector[8]);
upperBackXVector.push_back(floatTokenVector[9]);
upperBackYVector.push_back(floatTokenVector[10]);
upperBackZVector.push_back(floatTokenVector[11]);
thoraxXVector.push_back(floatTokenVector[12]);
thoraxYVector.push_back(floatTokenVector[13]);
thoraxZVector.push_back(floatTokenVector[14]);
lowerNeckXVector.push_back(floatTokenVector[15]);
lowerNeckYVector.push_back(floatTokenVector[16]);
lowerNeckZVector.push_back(floatTokenVector[17]);
upperNeckXVector.push_back(floatTokenVector[18]);
upperNeckYVector.push_back(floatTokenVector[19]);
upperNeckZVector.push_back(floatTokenVector[20]);
headXVector.push_back(floatTokenVector[21]);
headYVector.push_back(floatTokenVector[22]);
headZVector.push_back(floatTokenVector[23]);
rClavicleYVector.push_back(floatTokenVector[24]);
rClavicleZVector.push_back(floatTokenVector[25]);
rHumerusXVector.push_back(floatTokenVector[26]);
rHumerusYVector.push_back(floatTokenVector[27]);
rHumerusZVector.push_back(floatTokenVector[28]);
rRadiusXVector.push_back(floatTokenVector[29]);
rWristYVector.push_back(floatTokenVector[30]);
rHandXVector.push_back(floatTokenVector[31]);
rHandYVector.push_back(floatTokenVector[32]);
rFingersXVector.push_back(floatTokenVector[33]);
rThumbXVector.push_back(floatTokenVector[34]);
rThumbZVector.push_back(floatTokenVector[35]);
lClavicleYVector.push_back(floatTokenVector[36]);
lClavicleZVector.push_back(floatTokenVector[37]);
lHumerusXVector.push_back(floatTokenVector[38]);
lHumerusYVector.push_back(floatTokenVector[39]);
lHumerusZVector.push_back(floatTokenVector[40]);
lRadiusXVector.push_back(floatTokenVector[41]);
lWristYVector.push_back(floatTokenVector[42]);
lHandXVector.push_back(floatTokenVector[43]);
lHandYVector.push_back(floatTokenVector[44]);
lFingersXVector.push_back(floatTokenVector[45]);
lThumbXVector.push_back(floatTokenVector[46]);
lThumbZVector.push_back(floatTokenVector[47]);
rFemurXVector.push_back(floatTokenVector[48]);
rFemurYVector.push_back(floatTokenVector[49]);
rFemurZVector.push_back(floatTokenVector[50]);
rTibiaXVector.push_back(floatTokenVector[51]);
rFootXVector.push_back(floatTokenVector[52]);
rFootZVector.push_back(floatTokenVector[53]);
rToesXVector.push_back(floatTokenVector[54]);
lFemurXVector.push_back(floatTokenVector[55]);
lFemurYVector.push_back(floatTokenVector[56]);
lFemurZVector.push_back(floatTokenVector[57]);
lTibiaXVector.push_back(floatTokenVector[58]);
lFootXVector.push_back(floatTokenVector[59]);
lFootZVector.push_back(floatTokenVector[60]);
lToesXVector.push_back(floatTokenVector[61]);
#pragma endregion
//clear the vectors to prepare them for the next frame.
oneFrame.clear();
mainTokenVector.clear();
stringTokenVector.clear();
floatTokenVector.clear();
animationLength++;
}
//close the file currently associated with the object, disassociating it from the stream.
in.close();
}
I've spent the past few hours trying to figure out why this is happening, with no success, so any help at all would be greatly appreciated. If you have any queries let me know.
Cheers!

Figured it out.
I had declared all the body vectors( lowerBackXVector
lowerBackYVector
lowerBackZVector) etc outside the function, therefore they were not getting reset. I defined them within the function and now it works perfectly. Thanks guys.

Related

Returning a vector from a function in order to print the contents

My program has a function named 'WordLadder' that uses BFS to make a path from one word in a dictionary to another. I was given a starter code that prints the number of nodes in the path, but I want to print the path itself. Currently, I have appended the words to a vector as they enter a queue but I am not able to return the vector as part of my 'WordLadder' function in order to print it in the main program.
I just want the program to print a path based on the two words I picked, i.e. "TOON - POON - POIN - POIE - PLIE - PLEE - PLEA", if the start word is "TOON" in the dictionary and the target word is "PLEA" in the dictionary.
I tried to declare the vector outside of the function and print it in main with this code, but I was unsuccessful.
void print(std::vector < int >
const & transformation) {
std::cout << "The vector elements are : ";
for (int i = 0; i < transformation.size(); i++)
std::cout << transformation.at(i) << ' ';
}
I have attempted to return the vector inside of the function, but I receive this error
error: no viable conversion
from returned value of type
'vector<std::__cxx11::string>' (aka
'vector<basic_string<char> >') to
function return type 'int'
return transformation;
Here is my code. Any help would be appreciated, as I am new to C++.
// To check if strings differ by exactly one character
bool nextWord(string & a, string & b) {
int count = 0; // counts how many differences there
int n = a.length();
// Iterator that loops through all characters and returns false if there is more than one different letter
for (int i = 0; i < n; i++) {
if (a[i] != b[i]) {
count++;
}
if (count > 1) {
return false;
}
}
return count == 1 ? true : false;
}
// A queue item to store the words
struct QItem {
string word;
};
// Returns length of shortest chain to reach 'target' from 'start' using minimum number of adjacent moves. D is dictionary
int wordLadder(string & start, string & target, set < string > & ew) {
//Create vector to store path in a
vector < string > transformation;
// Create a queue for BFS and insert 'start' as source vertex
queue < QItem > Q;
QItem item = {
start
};
Q.push(item);
// While queue is not empty
while (!Q.empty()) {
// Take the front word
QItem curr = Q.front();
transformation.push_back(Q.front().word);
Q.pop();
// Go through all words of dictionary
for (set < string > ::iterator it = ew.begin(); it != ew.end(); it++) {
// Proccess the next word according to BFS
string temp = * it;
if (nextWord(curr.word, temp)) {
// Add this word to queue from the dictionary
item.word = temp;
Q.push(item);
// Pop from dictionary so that this word is not repeated
ew.erase(temp);
// If we reached target
if (temp == target) {
return trasformation;
}
}
}
}
return 0;
}
// Driver program
int main() {
string start;
string target;
// make dictionary
std::ifstream file("english-words.txt");
set < string > ew;
copy(istream_iterator < string > (file),
istream_iterator < string > (),
inserter(ew, ew.end()));
cout << endl;
cout << "Enter Start Word" << endl;
cin >> start;
cout << "Enter Target Word" << endl;
cin >> target;
cout << wordLadder(start, target, ew);
return 0;
}
There are multiple problems.
You were on the right track when you said "I tried to declare the vector outside of the function and print it in main..."
So, change wordLadder to take the vector by reference.
int wordLadder(vector<string> &transformation, string & start, string & target, set < string > & ew)
Then declare it in main and pass it to wordLadder
vector<string> t;
wordLadder(t, start, target, ew);
print(t);
You will have to also change print to take a vector of the right type, ie. string and not int
void print(std::vector < string > &transformation)

Unable to delete contents of dynamic array in C++

I've been beating my head against this one for awhile. In the deconstructor of my class, I have a for loop that is supposed to iterate through an array of objects and delete them. When I try though, I get a read access violation. The attached code is supposed to read info from two documents and use that to create Country objects.
#include "pch.h"
#include "CountryCatalogue.h"
#include "Country.h"
#include <iterator>
#include <map>
//imports for reading the files
#include <iostream>
#include <fstream>
CountryCatalogue::CountryCatalogue()
{
_maxSize = 10;
_curSize = 0;
_catalogue = new Country*[_maxSize];
}
CountryCatalogue::CountryCatalogue(std::string continentFileName, std::string countryFileName)
{
//block that opens the files and checks to make sure they can be read
//open up the files
std::ifstream inFile1;
std::ifstream inFile2;
//opening both text files and ensuring that the file is readable to the program
inFile1.open(continentFileName);
if (!inFile1) {
std::cout << "Unable to open file";
exit(1); // terminate with error
}
inFile2.open(countryFileName);
if (!inFile2) {
std::cout << "Unable to open file";
exit(1); // terminate with error
}
// read the continet file
// while there is still stuff to read in the file
std::string str;
while (!inFile1.eof())
{
std::string Country, Cont;
//reading lines from file and assigning to variables
std::getline(inFile1, Country);
std::getline(inFile1, Cont);
//mapping to variables read from file
_countryContinent.insert(std::pair<std::string, std::string>(Country, Cont));
_curSize++;
}
//closing file after use
inFile1.close();
//creating array
_catalogue = new Country*[_curSize+2];
//resetting size to zero for later itteration
_curSize = 0;
// read the country file
// while there is still stuff to read in the file
while (!inFile2.eof())
{
std::string name, POP, AREA;
int pop;
double area = 0.0;
std::getline(inFile2, name);
std::getline(inFile2, POP);
std::getline(inFile2, AREA);
if (!POP.empty() && POP[POP.length() - 1] == '\n') {
POP.erase(POP.length() - 1);
}
if (!AREA.empty() && AREA[AREA.length() - 1] == '\n') {
AREA.erase(AREA.length() - 1);
}
pop = std::stoi(POP);
area = std::stod(AREA);
//creating iterator to search through mapped values
std::map<std::string, std::string>::iterator it;
it = _countryContinent.find(name);
//creating empty string variable to store continent
std::string cont;
//using value found by iterator to make continent string
//ensuring value isn't the end valueof the map
if (it != _countryContinent.end()){
cont = it->second;
}
//std::cout << name << pop << area << cont << std::endl;
// add the country to the catalogue
addCountry(name, pop, area, cont);
}
}
CountryCatalogue::~CountryCatalogue() {
/*for (int i = 0; i < _curSize; i++){
delete _catalogue[i];
std::cout << "deleted" << i << std::endl;
}*/
delete[] _catalogue;
}
void CountryCatalogue::addCountry(std::string name, int pop, double area, std::string cont) {
//std::cout << name << pop << area << cont << std::endl;
//std::cout << _curSize << std::endl;
Country* toAdd = new Country(name, pop, area, cont);
if (_curSize == _maxSize) {
expandCapacity();
}
//adding country object to array
_catalogue[_curSize] = toAdd;
//adding to _curSize for next iteration
_curSize++;
}
void CountryCatalogue::printCountryCatalogue() {
std::string s;
/*for (int i = 0; i < _curSize; i++) {
s += _catalogue[i]->to_string() + "\n";
}*/
std::cout << _curSize << std::endl;
}
void CountryCatalogue::expandCapacity() {
//doubling array size
_maxSize = _maxSize * 2;
//creating pointer to new array of new size
Country** newCatalogue = new Country*[_maxSize];
//copying old array into new
for (int i = 0; i < _curSize; i++) {
newCatalogue[i] = _catalogue[i];
}
//deleting old array
delete[] _catalogue;
//making _catalogue point to newCatalogue
_catalogue = newCatalogue;
}
UPDATE:
What my code is supposed to do is get information from text files and create objects using that data. I am required to use an array instead of a vector. The code runs fine and I can create the country object. The issue is that I cannot add the created object to the _catalogue array, as I cannot delete it afterwards. When I attempt to iterate through the array, I receive a message saying Heap Corruption was detected.
Your problem is due to this line
_catalogue = new Country*[_curSize+2];
in the second constructor. You have forgotten to update _maxSize so you have a mismatch between _maxSize and the real allocated amount of memory.
Try:
_maxSize = _curSize+2;
_catalogue = new Country*[_maxSize];
You created _catalogue as a dynamic array.
To release the memory allocated for arrays of elements using new TYPE[SIZE] the syntax is:
delete[] _catalogue;
Loop is Needed for deleting memory allocated for Matrix elements. For example
int matrix = new int[rows][cols];
for (int i = 0; i < rows; ++i)
delete [] matrix[i];
The array is deleted row by row.

How to speed up loading text file to multi vector

I have to load large files (several GB) with data and I want to load them to two dimensional vector. Code below does the job, but it is insanely slow. To be more specific, the goal is to get all lines where values in 2nd column are equal to index(_lh,_sh). And then exclude the lines where 4th column value is same as line+1 and line-1.
Now, I'am new to c++ and I usualy code in Python (have working code for this problem already). But I need it to be as fast as posible so I tried to rewrite my python code to C++. But it rus slower than Python now (and only getting the data to vector is implemented)... so before I proceed, I want to improve that.
From what I have found in similar questions, the problem would be dynamic vectors, .push_back() and getline().
I am rather confused about maping and chunk loading mentioned in similar questions so I am not able to change the code acording to these.
Could you help me to optimize this code?
Thank you.
#include <iostream>
#include <sstream>
#include <fstream>
#include <array>
#include <string>
#include <vector>
using namespace std;
int pixel(int radek, int sloupec, int rozmer = 256) {
int index = (radek - 1) * rozmer + sloupec;
int index_lh = (index - rozmer - 1);
int index_sh = (index - rozmer);
int index_ph = (index - rozmer + 1);
int index_l = (index - 1);
int index_p = (index + 1);
int index_ld = (index + rozmer - 1);
int index_sd = (index + rozmer);
int index_pd = (index + rozmer + 1);
array<int, 9> index_all = { {index, index_lh, index_sh, index_ph, index_l, index_p, index_ld, index_sd, index_pd } };
vector<vector<string>> Data;
vector<string> Line;
string line;
for (int m = 2; m < 3; m++) {
string url = ("e:/TPX3 - kalibrace - 170420/ToT_ToA_calib_Zn_" + to_string(m) + string(".t3pa"));
cout << url << endl;
ifstream infile(url);
if (!infile)
{
cout << "Error opening output file" << endl;
system("pause");
return -1;
}
while (getline(infile, line))
{
Line.push_back(line);
istringstream txtStream(line);
string txtElement;
vector<string> Element;
while (getline(txtStream, txtElement, '\t')){
Element.push_back(txtElement);
}
Data.push_back(Element);
}
}
cout << Data[1][0] << ' ' << Data[1][1] << ' ' << Data[1][2] << endl;
return 0;
}
int main()
{
int x = pixel(120, 120);
cout << x << endl;
system("pause");
return 0;
}
Vectors can get slow if their underlying buffer gets reallocated often. A vector is required to be implemented on a buffer of continuous memory, and every time the buffer limit is exceeded, it will have to allocate a new and larger buffer, and then copy the content from the old buffer to the new buffer. If you have an idea of how big buffers you require (you don't need to be excact), you can help the program to allocate a buffer of appropriate size by using e.g. Data.reserve(n) (where n is approximately the number of elements you think you need). This does note change the "size" of the vector, just the size of the underlying buffer. As a concluding remark, I have to say I haven't really ever benchmarked this, so this may or may not improve the performance of your program.
EDIT: Though, I deem it a bit more likely that the performance is a bit bottled by the line Data.push_back(Element); which makes a copy of the Element-vector. If you're using C++11, I believe it's possible to work around this by doing something like Data.emplace_back(std::move(Element)); in which case you can't alter Element afterwards (it's content is moved). You would also need to include memory for std::move.
In the while loop, you could try changing the lines from
while (getline(infile, line))
{
Line.push_back(line);
istringstream txtStream(line);
string txtElement;
vector<string> Element;
while (getline(txtStream, txtElement, '\t')){
Element.push_back(txtElement);
}
Data.push_back(Element);
}
to:
while (getline(infile, line))
{
Line.push_back(line);
istringstream txtStream(line);
string txtElement;
//vector<string> Element; [-]
Data.emplace_back(); // [+]
while (getline(txtStream, txtElement, '\t')) {
//Element.push_back(txtElement); [-]
Data.back().push_back(txtElement); // [+]
}
//Data.push_back(Element); [-]
}
That way, the vectors in Data don't need to get moved or copied there -- they are already constructed, albeit empty. The vectors in Data are default-constructed with .emplace_back(). We get the last element in Data with the .back() function, and push our values as usual with .push_back(). Hopefully this helps :)
You can try using old C file reading API (FILE*, fopen(), etc.) or setting a bigger buffer for std::istringstream as follows
constexp std::size_t dimBuff { 10240 } // 10K, by example
char myBuff[dimBuff];
// ...
istringstream txtStream(line);
txtStream.rdbuf()->pubsetbuf(myBuff, dimBuff);
Another thing that you can try is using std::deques instead of std::vectors (but I've no idea if this is useful).
As suggested by muos, you can use move semantics; you can use emplace_back() also.
So I suggest to try with
Element.push_back(std::move(txtElement));
Data.push_back(std::move(Element));
or
Element.emplace_back(std::move(txtElement));
Data.emplace_back(std::move(Element));
You can also swith the following lines (there isn't a move constructor from a string for std::istringstream, if I'm not wrong)
Line.push_back(line);
istringstream txtStream(line);
adding move semantics (and emplace_back())
istringstream txtStream(line);
Line.emplace_back(std::move(line));
p.s.: obviously reserve() is usefull
You can also use reserve(int) on the vectors so they are created closer to the target size.
That too can avoid a lot of vector hopping around the heap, as the vector will only be recreated of it passes the target size.
You can call reserve again if vector passes the size you previously reserved:
vector<int> vec;
vec.reserve(10);
for (int i=0;i < 1000; i++)
{
if ( vec.size() == vec.capacity() )
{
vec.reserve(vec.size()+10);
}
vec.push_back(i);
}

How can I store a string(from a file with n number of lines) in a dynamic array initialized as a unique pointer? C++

A student looking for some guidance...
This is a class assignment with the instructions:
Re-write your program, List of Chores, using a unique_ptr object as your data member. You should store a dynamic array in the unique_ptr object. Use this array for storing, retrieving, deleting and updating chores.
I am trying to learn more about using the unique_ptr object so I may create the constructor which is supposed to initialize the list. A text file has the list and the constructor should store that list into an array. I am trying to work around the error "Access violation reading location." In the original program, I created a temporary dynamic array with a larger capacity and copied the list into that array. There's a list of 10 chores in the text file. Here is that code:
In Header:
private:
/* var to keep len of list */
int len = 0;
int max = 9;
/* add appropriate data structure to store list */
string *arr = new string[max];
In .cpp:
/* reads the file line by line and initializes list */
ListOfChores::ListOfChores(string fileName){
ifstream file(fileName, ifstream::in);
string line;
if (file.is_open()) //Checking if the file can be opened
{
while (getline(file, line)) // Gets a single line
{
if (len >= max)
{
string *narr = new string[max + 10]; // New, larger array
for (int i = 0; i < max; i++)
{
narr[i] = arr[i]; // Copies line
}
delete[] arr; // Clears
arr = narr; // Copies
max += 1; // Growth
}
arr[len] = line; // Store a line in the array
len++; // Increases length by 1
}
file.close(); // Closes file
}
else cout << "Unable to open file" << endl;
}
And to show that I have been working on this and am not a lazy student...
New program attempt header:
private:
/* var to keep len of list */
int len = 0;
int max = 9;
/* add appropriate data structure to store list */
string *arr = new string[max]; // Primary array
string *narr = new string[max]; // New array
New program .cpp:
/* reads the file line by line and initializes list */
ListOfChores::ListOfChores(string fileName) {
unique_ptr<string[]> arr(new string[max]); // Unique pointer initialization
ifstream file(fileName, ifstream::in);
string line = " ";
if (file.is_open()) //Checking if the file can be opened
{
while (getline(file, line)) // Gets lines from file
{
if (len >= max)
{
max++; // Growth
unique_ptr<string[]> narr(new string[max]); // New unique pointer
narr = move(arr);// narr owns the object
narr[max] = line; // Store a line in the array
len++; // Increases length by 1
arr = move(narr); // arr owns the object
}
else
{
arr[len] = line; // Store a line in the array
len++; // Increases length by 1
}
}
file.close(); // Closes file
}
else cout << "Unable to open file" << endl;
}
The entire point of a unique_ptr is to destroy the managed pointer when the unique_ptr goes out of scope. This means you do not want it declared inside any confined scope. Any work you do will be destroyed as soon as the current block of code is exited. A unique_ptr created inside a function and not returned to the caller is gone and it's contents are gone with it as soon as the function returns. If you have a class intended to store a dynamic amount of data, then the data should be managed at the object's scope.
So
private:
/* var to keep len of list */
int len = 0;
int max = 20; // made bigger so first 50% increase is 10 elements
/* add appropriate data structure to store list */
std::unique_ptr<std::string[]> arr; // Primary array
// no need for New array here
Discussion of whether a 50% or 100% array size increase is better can be found here: What is the ideal growth rate for a dynamically allocated array?. As always, your mileage may vary, but a one-at-a-time increase is generally agreed upon to be a bad idea.
Now onto ListOfChores where we want to use, but not declare, the unique_ptr.
ListOfChores::ListOfChores(string fileName) {
//no unique_ptr here. Scope is too narrow to be useful
ifstream file(fileName, ifstream::in);
string line = " ";
if (file.is_open()) //Checking if the file can be opened
{
while (getline(file, line)) // Gets lines from file
{
if (len >= max)// the current size is too small Let's make it bigger!
// if we grow before adding the line, we don't need any
// special code to add the new line.
{
max *= 1.5; // Grow by 50%. Numerous studies have shown that 50% is a
// good balance of RAM vs copy overhead in the general case
std::string * narr = new string[max]; // no unique_ptr here either
// old school copy for simplicity and obviousness
for (int index = 0; index < len; index++)
{
narr[index] = arr[index]
}
arr.reset(narr); // frees and replaces the old array
// arr now manages narr
}
// done growing, add normally to array
arr[len] = line; // Store a line in the array
len++; // Increases length by 1
}
file.close(); // Closes file
}
else cout << "Unable to open file" << endl;
}
Other ListOfChores member functions will use arr, reading, adding and subtracting as needed. In order to add efficiently, the array growth code should be removed from the constructor and placed in a private method to be called by the constructor and other methods that need to enlarge the array.

C++ creating an array in a temporary class

Newbie question! I've started a small project that loads integer values from a file into an array. (The array needs to be accessed at random which is why i've chosen an array and not a vector.)
To load the data values from the file I've created a Load/Save class. The load function reads the first line of the file which gives us the total number of entries the array needs to have, then it will fill the array with the rest of the values in that file.
This load object is only created temporarily, I want to give the data to the program and then delete the object.
What is the best way to achieve this? should I create the array in main() and pass the load object a reference, in which case how can I create the array so it can be re-sized for the amount of data that needs loading..?
here is the load/save class:
class FileIOClass {
public:
FileIOClass();
int ReadFile(char*);
private:
};
this is the cpp code for the class:
FileIOClass::FileIOClass() {
}
int FileIOClass::ReadFile(char* file_name) {
string line;
ifstream file;
file.open(file_name, ios::in);
cout << "Loading data...\n";
int num_x, num_y, array_size;
bool machine_header = false;
if (file.is_open()) {
while(getline(file, line)) {
if (line.size() && machine_header == false) {
// Load machine header information
file >> num_x;
file >> num_y;
file >> array_size;
machine_header = true; // machine header has now been read, set this to true.
}
else {
// this is where i want to load the data from the file into an array.
// the size of the array should be equal to the value in array_size.
}
}
cout << "Loading complete!\n";
}
else {cout<<"File did not open!\n";}
file.close();
return 0;
}
and here is the main.cpp so far:
int main(int argc, char** argv)
{
FileIOClass data_in;
data_in.ReadFile(argv[1]);
return 0;
}
there will be several other classes that will process the data thats contained in the array.
I bet there are a load of odd newbie mistakes in this code - please feel free to point them out, better to learn these things now.
Thanks all!
Ian.
Somthing like this might be good:
vector<int> myVector(array_size);
for(int i=0; file && i<array_size; i++) {
file >> myVector[i];
}
As long as you've already decided on using a class to read the file, storing the data within the class seems reasonable. add a member to this class to store the data:
class FileIOClass {
public:
FileIOClass();
int ReadFile(char*);
unsigned int operator [](int i) const {return m_data[i];}
int size(void) { return m_data.size(); }
private:
std::vector<int> m_data;
};
and insert data into this member in your ReadFile method:
while(getline(file, line)) {
int pos = 0;
if (line.size() && machine_header == false) {
// Load machine header information
file >> num_x;
file >> num_y;
file >> array_size;
m_data.resize(array_size);
machine_header = true; // machine header has now been read, set this to true.
}
else {
file >> m_data[pos++];
// this is where i want to load the data from the file into an array.
// the size of the array should be equal to the value in array_size.
}
}
notice that I overloaded the [] operator, so you can use your class like this:
int main(int argc, char** argv)
{
FileIOClass data_in;
data_in.ReadFile(argv[1]);
if (data_in.size() >= 1)
cout << data_in[0];
return 0;
}
The array needs to be accessed at random which is why i've chosen an array and not a vector.
C++ vectors allow efficient random access (they are arrays under the hood). Use std::vector unless you have profiled your code and found them inefficient for what you are doing.