How to allocate memory for specific number of strings? - c++

I was given the task to program something like a dictionary, and the way I am allocating memory for the meanings is just to allocate for 100 meanings in the constructor, which works perfectly fine.
However, the professor didn't approve that and he asked me to rewrite the code in a way that I allocate memory for a relevant number of meanings. I basically have no idea how to do that, how the constructor will know in advance how many meanings I will have?
What would you guys suggest? I post just part of the code, which is relevant for the problem.
#include"expression.h"
//---------------------METHODS-------------------------------------------
Expression::Expression(int m_ctr)
{
count_meanings = m_ctr; // Set the counter to 0
meanings = new char * [100]; // Allocate memory for 100 meanings
}
Expression::~Expression()
{
delete [] meanings; // Free the allocated memory
delete [] word_with_several_meanings; // Free the allocated memory
}
void Expression::word(char *p2c)
{
word_with_several_meanings = new char[strlen(p2c)+1];
strcpy(word_with_several_meanings, p2c); // copy the string, method: DEEP copy
}
void Expression::add_meaning(char *p2c)
{
meanings[count_meanings] = new char[strlen(p2c)+1];
strcpy(meanings[count_meanings++], p2c); // copy the string, method: DEEP copy
}
char * Expression::get_word()
{
return word_with_several_meanings;
}
char * Expression::get_meaning(int n_meaning)
{
return * (meanings + n_meaning);
}
int Expression::get_total_number_of_meanings()
{
return count_meanings;
}
int main(void)
{
Expression expr;
expr.word("bank");
expr.add_meaning("a place to get money from");
expr.add_meaning("a place to sit");
cout << expr.get_word() << endl;
for(int i = 0; i<expr.get_total_number_of_meanings(); i++)
cout << " " << expr.get_meaning(i) << endl;

The C++ way of doing that is to use:
std::string to store a single string (instead of raw char* C-like strings)
std::vector to store a sequence of strings (like the "meanings" in your dictionary)
So, you can have a vector<string> data member inside your class, and you can dynamically add meanings (i.e. strings) to it, using vector::push_back().
If you - for some reason - want to stay at the raw C level, you could use a linked list data structure, storing a raw C string pointer inside each node, and when you add a new meaning, you can create a new node pointing to that string, and add that node to the linked list. A singly-linked list having a node definition like this may suffice:
struct MeaningListNode
{
char * Meaning; // Meaning raw C string
struct MeaningListNode* Next; // Pointer to next meaning node, or nullptr for last
};
But, frankly speaking, the vector<string>> approach seems much simpler and better to me.

Related

Appending struct by reference to an array in C++

How can I implement a function in C++ that appends a struct instance to an array by reference? So that after appending a struct stored in a variable to the array, this variable can be used further to change the instance of array.
pseudocode:
struct St{
int x
}
St* arr;
St a = {0};
append a to arr;
a.x = 1;
//expecting arr[0].x = 1
Here is the C++ code with the film example (see comments describing the problem):
struct Film{
int id;
char* name;
};
void add_film(Film *&films, int &size, Film &film){
if (size == 0)
films = new Film[1];
else
{
Film *tmp = new Film[size + 1];
for (int i = 0; i < size; ++i)
{
tmp[i] = films[i];
}
delete[]films;
films = tmp;
}
films[size] = film;
film = films[size]; //how to reassign passed film object to a new object in array?
size++;
}
int main(){
Film *films = nullptr;
int size = 0;
Film film = {1, "Name1"};
add_film(films, size, film);
film.name = "Name2";
std::cout << films[0].name; //output: "Name1", expected: "Name2"
}
Appending struct by reference to an array in C++
There are two problems with this:
There cannot be arrays of references in C++.
There is no way to append to an array. The size of an array is a constant. There is no way to add or remove elements.
An issue with your attempted solution is that you have an array of Films, and not an array of references. This isn't very surprising, as problem 1 described above states there are no such thing as arrays of references. The solution is simple however: Use pointers instead of references. Technically, you could use a reference wrapper instead, but a pointer is often simpler.
You've basically figured out the solution to 2. already. What you're doing is creating a new array, copying the old elements from the old array into the new one, and destroying the old array. That's a good approach in general, but there are a number of problems with this trivial implementation:
Bare owning pointers are unsafe and hard to use.
Reallocating and copying the entire array on every append is very expensive.
Former can be solved by using the RAII idiom, and latter can be solved by separating the storage of the objects from the creation of the objects, and by growing the storage by a constant factor i.e. geometrically. There is no need to implement such RAII container though, since the standard library has you covered. It's called std::vector.
In conclusion: You can use std::vector<Film*>.

Creating a Array of Nodes

When we create a array of integers we do it like this:
int main() {
int x;
cout << "Enter size of array"
cin >> x;
int* myArray;
myArray = new int[x]
}
we assign the asterisks next to array, we are assigning it as a array of pointers right?
If I make a array of nodes where:
struct Node {
string Name;
int Age;
}
I ran some code to experiment with it and understand it more and I tried to do just like the array sample but create a array with nodes.
int main() {
Node* sumShit[5];
Node* America = new Node();
America->age = 16;
America->Name = "America";
sumShit[0] = America;
Node* Japan = new Node();
Japan->age = 15;
Japan->Name = "Japan";
sumShit[1] = Japan;
cout << "[" << sumShit[1]->Name << ", " << sumShit[1]->age << "]";
}
Everything printed out fine with pointers but then I did it also without pointers, where I just stored node properties in the Node:
Node myNodeShit[5];
Node Poop;
Poop.age = 16;
Poop.Name = "Poop";
myNodeShit[0] = Poop;
sortArrayName(myNodeShit, 5);
printArray(myNodeShit, 5);
And this also worked, however whats the advantages to using pointers and just storing it within the node. When it comes to algorithms, sorting and using memory, is there a preferred way. Im trying to figure why it would be better to have it as a array of pointers to nodes vs a array of nodes.
Using pointers allows for greater flexibility in updating / modifying existing data because the data is only declared once in memory. From here, you can use pointers to make changes from anywhere in your code.
Additionally, using pointers conserves memory whereas creating the actual nodes will make copies of said node. This becomes apparent when you're passing the array into a function. In this case the array of nodes (without pointers) becomes a local copy pushed onto the call stack. When the function returns, you'll lose any modification you made to the node. Conversely, using pointers will save the node's state.
When in doubt, try to use pointers where you can.

How to use a dynamically resizing String Array?

I'm trying to use an array in C++ that changes in size. For some reason the size does not change, it only ever holds 1 string. The difficult part is that the user cannot input the number of courses they are going to add, instead the addCourse function is called until the user stops. A vector cannot be used (this is for a school assignment, and a resizing array is required). I'm stuck as to why the array only seems to hold one string, I would think it to hold the equivalent of numCourses strings. How would I go about resizing to hold multiple strings after each call to the function?
void Student::addCourse(string* courseName)
{
int x;
numCourses += 1;//increments number of courses
string newCourse = *courseName;
string* newCourses = new string[numCourses];//temporary array
for(x=0; x<numCourses - 1; x++)//fills temp array with the values of the old
{
newCourses[x] = courses[x];
}
newCourses[numCourses - 1] = newCourse;//adds extra value
delete[] courses;//removes original array
courses = newCourses;//sets the new course list
}
Edit: For those asking why a vector cannot be used because the point of the assignment is to actively avoid memory leak using the heap. Using an array like this forces intentional delete of stored values.
The comment should have answered your question: there is no way for the debugger to know that a pointer to a string is pointed to an array, nor does it know its bounds, because no such information is kept at runtime (a std::vector will show its whole contents in the debugger, in contrast).
Your method prototype should read:
void Student::addCourse(const string& courseName);
If you don't want to have a memory leak, declare a pointer to courses in your class:
private:
string* courses;
Allocate space for an array of strings in your constructor:
Student::Student()
{
courses = new String[5];
}
Then deallocate in the destructor:
Student::~Student()
{
delete[] courses;
}
This gives you room for up to 5 courses. If you need more you need to adjust the size of the array of strings at run time:
void Student::ExtendArray()
{
delete[] courses;
courses = new String[10];
}
Note this code is not exception safe, but will give you the basic idea.

Array of doubles and heap corruption

I have a function like
template <class Type>
myFunc(Type** arrayToBeFilled);
I call it like this:
double* array = NULL;
myFunc(&array);
And inside the function I do some reading and parsing numbers with strtod function:
//Here comes file opening, getting number of lines and number of doubles in every line
...
char *inputString = new char[LONG_STRING_SIZE];
char *pNext = NULL;
(*arrayToBeFilled) = new Type[length*rowSize];
for (int i=0; i<length; i++)
{
source.getline(inputString, LONG_STRING_SIZE);
pNext = NULL;
for (int j=0; j<rowSize; j++)
{
double d = strtod(inputString, &pNext);
(*arrayToBeFilled)[i*rowSize+j] = d;
inputString = pNext;
pNext = NULL;
}
}
Variable d is just for check with debugger - and it's just fine while running.
But after filling the array I try to print it (just for check)
for (int i=0; i<length; i++)
{
for (int j=0; j<rowSize; j++)
{
cout<<(*arrayToBeFilled)[i*rowSize+j]<<" ";
}
cout<<"\n";
}
And here comes bad output - other numbers, sometimes heap corruption and so. I was printing it in and out of the function - the same results. And I can't delete this array no or neither out the function - run time errors follow me!
Why do you use raw C arrays in C++? If you use STL classes like std::vector instead of raw new[], your code will become cleaner, simpler to read and maintain (for example, you don't need explicit delete[] calls: the destructor will cleanup heap memory). In general, in modern C++ the rule is "if you are writing new or delete, you are doing it wrong" (with some exceptions).
Note also that with C++11 move semantics, you can simply return the vector instead of using output reference/pointer arguments:
template <typename Type>
inline std::vector<Type> myFunc()
{
...
}
Inside your function body, instead of your code
(*arrayToBeFilled) = new Type[length*rowSize];
just write:
std::vector<Type> arrayToBeFilled(length*rowSize);
and then simply return arrayToBeFilled; .
(Note also that vector's can be nested together: you may also use vector<vector<Type>> to make a 2D array, but this is less efficient than a single vector<Type>, which more directly maps to your raw new[] call.)
In addition, in the code you posted you create a raw C array on the heap with new char[LONG_STRING_SIZE] and assign the pointer to it to inputString; then you modify inputString with an assignment from pNext: but in doing so, you leak the initial array whose pointer was stored in inputString.
Seems you don't have a return type
template <class Type>
void myFunc(Type** arrayToBeFilled);
and you should initialize your function
double array = NULL;
myFunc<double>(&array);
also when it comes to input, print out the values that you get, more often than not you may get something unexpected which causes the error.

char array corruption in C++

I'm working on a project that makes me store an array of objects whose constructor is
Item(char* item, int itemType){
char temp[200];
for(int i = 0; i < 200; i++){
temp[i] = '\0';
if(item[i] != '\0'){
temp[i] = item[i];
}
}
_item = item;
_itemType = itemType;
_tweetIDs = NULL;
}
Don't worry about _tweetIDs, that's another functional part of my program and isn't related to my problem.
This array is stored within a class:
ItemList()
How this works is that the functional part of my program parses a line of input and puts it into the Item(char*, int) object. This is how it adds the line:
int addItem(char* item, int type){
char temp1[200];
for(int i = 0; i < 200; i++){
temp1[i] = '\0';
}
int j = 0;
while(item[j] != '\0'){
temp1[j] = item[j];
j++;
}
_items[_size] = Item(temp1, type);
_size++;
return _size;
}
Where _items is the Item() array and _size is a field that is incremented every time an Item() is added.
My issue comes when I have to print the contents of the list.
I have a method that does that:
void printList(){
for(int i = 0; i < 500; i++){
if(_items[i] != NULL){
cout << "[" << i << "] ";
_items[i]->printContents();
}
}
}
I tested printContents() in the constructor of Item() and tested printList in the addItem method and they both work when called within the class itself. The issue comes when I have to call the print method outside the class body.
In the main method, I create a List object:
List itemList;
The default constructor sets all members of the Item() array to NULL and initializes _size.
After adding a few Item() objects into the array (Which I confirmed is increasing in size through the debugger), I tried to print it out. When I call:
itemList.printList();
It gives me the right amount of indexes (And lines), but the char array is just a bunch of garbage. I used the debugger to try and find out where it went wrong. In the addItem() method, I called printList to check the array, and the output from that is fine. Then, I called itemList.printList() right after the last addItem() call, and it gave me garbage. In between the addItem() and itemList.printList(), the char array is lost or something along those lines.
Any idea what's going wrong? I'll give you any more code if you need it.
In your Item constructor, you are setting what I presume is a member _item as such:
_item = item;
This just assigns the pointer value of the location pointed to by item into _item. It does not actually copy the string!
The next time you go to read this location, it might be valid - chances are, though, it will be garbage, as you are seeing.
What you are looking for is a function like strcpy (as a side note, there's no need to do quite so much manual copying - just pass that pointer around and copy it once - in the Item constructor).
EDIT, to address your comment:
strcpy made your program crash because you are using it on unallocated memory.
You have to allocate memory for an array using new[] in c++
Take note on the lifetime of a variable.
If you declare temp1 as static array, then it will be destroyed immediately by the end of function addItem.
At the end, all object that refers to this memory location will be invalid.
And ....
If you want to pass a reference to an array do it this way:
Item(char** item, int itemType)
I'm imagining your definition of class Item minimally looks like this:
class Item
{
Item(char* item, int itemType);
private:
char *_item;
};
Your constructor must allocate memory for _item in order to make a copy of what gets passed in via the constructor. Failure to do that will inevitable result in memory problems and exceptions. Alternatively, you can use something like a vector of char.
In Item constructor you create local array char temp[200], you copy there what is pointed by char * item and then you don't use temp[200] any more. What's the point of doing that?
Later you assign passed pointer to _item member. The pointer points to local variable char temp1[200] in addItem(). When addItem() finishes then temp1 is destroyed and so _item in Item class points to garbage.
What you probably need to do is to allocate memory either statically in _item definition or dynamically using new (and then not forget to release it). I think the first solution will be safer for you. In the latter case you would also have to take care of copy constructor and assign operator. So, you need to change _item definition from char * _item to char _item[200], and then you can use strncpy:
Item(char* item, int itemType) {
strncpy(_item, item, 200);
}