Creating a personal string vector class - c++

I am not allowed to make use of the vector class so I need to make my own. I made a int vector class and it works fine, but when trying to make it for strings it compiles but gives me an error because of the pointers. Any hint where I am making the mistake? All I did was change every int element for string, but aparently that does not work. Please help I am very confused.
public:
StringRow(){
elements = new string;
size = 0;
}
~StringRow(){...}
void push_back(string value){...}
};

You defined pointer to variable, not array of variables.
elements = new string;
Replace it with
elements = new string[size];
You can optimize algorithm with defining initial size. Create bigger array only if it's necessary.

There are several problems:
in the constructor you don't need to allocate anything. You don't even need a constructor here, you can initialize the members directly as you declare them.
if you allocate with string* tmpElementsArray = new string[size + 1]; you need to deallocate with delete [] tmpElementsArray;
Corrected working version:
#include <string>
#include <iostream>
using namespace std;
class StringRow {
private:
string* elements = nullptr;
int size = 0;
public:
// constructor not needed
// StringRow() {
// elements = nullptr;
// size = 0;
// }
~StringRow() {
delete []elements;
}
void push_back(string value) {
string* tmpElementsArray = new string[size + 1];
for (int i = 0; i<size; i++) {
tmpElementsArray[i] = elements[i];
}
delete [] elements;
elements = tmpElementsArray;
elements[size] = value;
size++;
}
int length() {
return size;
}
string at(int index) {
if (index<size) {
return elements[index];
}
}
};
int main()
{
StringRow s;
string str1 = "hello";
string str2 = "hello2";
s.push_back(str1);
s.push_back(str2);
cout << s.at(0) << endl ;
cout << s.at(1) << endl;
}
Doing a delete []elements if elements is nullptr is OK.
NB: This is not the most efficient way.

Related

Can't modify a string in C++ array

Trying to learn datastructures, I made this class for a stack. It works just fine with integers but it throws a mysterious error with strings.
The class List is the API for my stack. Its meant to resize automatically when it reaches the limit. The whole code is just for the sake of learning but the error I get doesn't make any sense and it happens somewhere in some assembly code.
#include <iostream>
#include<string>
using namespace std;
class List {
private:
int N = 0;
string* list = new string[1];
void resize(int sz) {
max = sz;
string* oldlist = list;
string* list = new string[max];
for (int i = 0; i < N; i++) {
list[i] = oldlist[i];
}
}
int max = 1;
public:
void push(string str) {
if (N == max) {
resize(2 * N);
}
cout << max << endl;
list[N] = str;
N++;
}
void pop() {
cout << list[--N] << endl;
}
};
int main()
{
string in;
List list;
while (true) {
cin >> in;
if (in == "-") {
list.pop();
}
else {
list.push(in);
}
}
}
string* list = new string[max]; in the resize method defines a new variable named list that "shadows", replaces, the member variable list. The member list goes unchanged and the local variable list goes out of scope at the end of the function, losing all of the work.
To fix: Change
string* list = new string[max];
to
list = new string[max];
so that the function will use the member variable.
Don't forget to delete[] oldlist; when you're done with it to free up the storage it points at.

C++, Weird behavior of cout when trying to print integers

Im trying to write a class that stores an id and a value in an container class.
Im using an nested class as my data structure.
When im compiling the code sometimes it prints perfectly, sometimes it prints nothing and sometimes it prints half of the data then stops.
When i debug the code the same weird behavior occours, when it fails during debug it throws an error "Map.exe has triggered a breakpoint.", the Error occours in the print method when im using cout.
cmap.h
#pragma once
class CMap
{
public:
CMap();
~CMap();
CMap& Add(int id, int value);
void print() const;
private:
class container
{
public:
~container();
int container_id = 0;
int container_value = 0;
};
container* p_komp_;
int dim_ = -1;
void resize();
};
cmap.cpp
#include "cmap.h"
#include <iostream>
using namespace std;
CMap::CMap()
{
p_komp_ = new container[0];
}
CMap::~CMap()
{
p_komp_ = nullptr;
cout << "destroy cmap";
}
CMap& CMap::Add(int id, int value)
{
resize();
p_komp_[dim_].container_id = id;
p_komp_[dim_].container_value = value;
return *this;
}
void CMap::resize()
{
container* temp_array = new container[++dim_];
if (dim_ == 0)
{
temp_array[0].container_id = p_komp_[0].container_id;
temp_array[0].container_value = p_komp_[0].container_value;
}
for (unsigned i = 0; i < dim_; i++)
{
temp_array[i].container_id = p_komp_[i].container_id;
temp_array[i].container_value = p_komp_[i].container_value;
}
p_komp_ = temp_array;
}
void CMap::print() const
{
for (unsigned i = 0; i <= dim_; i++)
{
cout << p_komp_[i].container_id;
cout << p_komp_[i].container_value;
}
}
CMap::container::~container()
{
cout << "destruct container";
}
Map.cpp
#include "cmap.h"
#include <iostream>
using namespace std;
void main(void)
{
CMap m2;
m2.Add(1, 7);
m2.Add(3, 5);
m2.print();
}
These two things are a possible reason for your problem:
int dim_ = -1;
and
container* temp_array = new container[++dim_];
When you allocate, you increase dim_ from -1 to 0. That is you create a zero-sized "array", where every indexing into it will be out of bounds and lead to undefined behavior.
You also have memory leaks since you never delete[] what you new[]. I didn't look for more problems, but there probably a more.
And an "array" (created at compile-time or through new[]) will have indexes from 0 to size - 1 (inclusive). You seem to think that the "size" you provide is the top index. It's not, it's the number of elements.
It seems to me that you might need to take a few steps back, get a couple of good books to read, and almost start over.

C++ Dynamic Array Template issue

I am a beginner programmer in school still, and I was assigned this problem:
"Make your own dynamic array template. It should allow creating contiguous arrays (filled with things of the same type) which you can extend without worrying about running out of space.
Do one version using malloc and free.
Do one version using new and delete."
So far this is what I have:
#include <iostream>
#include <sstream>
#include "Array.h"
using namespace std;
int main(){
Array<int> *testArray = new Array<int>(5);
testArray->initArray();
testArray->printArray();
testArray->addData(7);
testArray->printArray();
return 0;
}
And here is the "Array.h" file:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
template<typename T>
class Array{
public:
Array(int size){
size = size;
data = new T[size];
};
Array<T> *addData(T dataToAdd){
Array <T> *tmp = new Array <T> (this->size);
tmp->data = this->data;
Array <T> *newData = new Array<T> (this->size + 1);
for (int i = 0; i < this->size + 1; ++i){
if (i < this->size){
//newData->data[i] = tmp->data[i];
newData->setData(tmp->getData()[i], i);
}
else{
//newData->data[i] = dataToAdd;
newData->setData(dataToAdd, i);
}
}
return newData;
};
void initArray(){
for (int i = 0; i < this->size; ++i){
//this->data[i] = i;
this->setData(i, i);
}
};
void printArray(){
ostringstream oss;
string answer = "";
for (int i = 0; i < this->size; ++i){
oss << this->data[i] + " ";
//cout << this->data[i] << " ";
}
answer = oss.str();
cout << answer << "asdf" << endl;
};
T* getData(){
return this->data;
}
int getSize(){
return this->size;
}
void setData(T data, int index){
this->getData()[index] = data;
}
private:
int size;
T* data;
};
So far what SHOULD happen in my main file is there should be an array of 5 ints, that are initialized to 0,1,2,3,4 from the initArray function.
Then it should print out the array, showing "0 1 2 3 4," add another "7" to it, then print the new array out showing "0 1 2 3 4 7."
For some reason, and I think it has something to do with losing data somehow when going between the two files, the field "data" of my Array class is not being properly changed.
I even hardcoded a test for this in main where I wrote a for loop using the setData function that initializes the Array to "0 1 2 3 4," and then manually printed out these values with another for loop, but the output was only "0 0 0 0 0."
Right now, as the code is, the output is:
asdf
asdf
As it was outputting whitespace before so I added the "asdf"'s to see if my printArray worked at all.
To sum up, why is the data in my private field "data" not being properly stored? I am very new to programming in c++ and any advice would be greatly appreciated. Thank you for your time, and if there is anything you do not understand please ask for clarification and I will do my best.
EDIT: problem solved! Thank you everyone who helped, the issue was with my constructor and how I was calling my functions in main.
One issue is your constructor:
Array(int size){
size = size;
data = new T[size];
};
The way you have it, you're just assigning your size argument to itself, which has no effect. One way to fix it would be to use a different name for the argument:
Array(int size_arg){
size = size_arg;
data = new T[size_arg];
};
However, the preferred way is to use the constructor initializer syntax:
Array(int size) : size(size), data(new T[size]) {};
With the constructor initializer syntax, the compiler knows that you are trying to initialize specific members and doesn't get confused between the argument name and the member name.

Expand and add a new object to a array inside a function

I'm trying to expand and add a new object to a array inside a function and have that array be effected outside the function as well (the arrays pointer is sent as a parameter).
void addMedia(Media* medias[], int &nrOfMedias, string title, int publYear, string author, int nrOfPages)
{
Media** tempArray = new Media*[nrOfMedias +1];
for(int i = 0; i < nrOfMedias; i++)
{
tempArray[i] = medias[i];
}
delete [] medias;
medias = tempArray;
delete [] tempArray;
medias[nrOfMedias] = new Book(title, publYear, author, nrOfPages);
nrOfMedias++;
}
This code works great inside the function but when I get outside it the array is still empty. As i understand this it's because the pointer is changed inside the function but how can i expand the array without having it change the pointer?
(I can not change the return data type or the parameters, assignment requirements.)
Do change medias = tempArray; to *medias = tempArray;, make it compile, polish your memory management (consider, what really should be freed, what not).
Don't view medias as an array of pointers, view it as a pointer to an array. Working example (slightly simplified):
class Media
{
public:
Media () { m_strTitle = "unknown";}
string m_strTitle;
};
class Book : public Media
{
public:
Book(string strTitle) { m_strTitle = strTitle; }
};
void addMedia(Media* medias[], int &nrOfMedias)
{
Media * tempArray = new Media[nrOfMedias +1];
for(int i = 0; i < nrOfMedias; i++)
{
tempArray[i] = (*medias)[i];
}
delete [] *medias;
(*medias) = tempArray;
(*medias)[nrOfMedias] = Book("newTitle");
nrOfMedias++;
}
int main()
{
int numMedia = 10;
Media * myArray = new Media[numMedia];
addMedia(&myArray, numMedia);
for (int i = 0; i < numMedia; i++)
{
cout << i << ") " << myArray[i].m_strTitle << endl;
}
return 0;
}
You don't need delete [] tempArray; because tempArray actually points to the same memory block as medias does after medias = tempArray;
Your function will work well whithout that line but I assume that you know what you pass with Media* medias[]

Dynamic Memory Allocation for Dictionary

Hi there I need to Build something like a dictionary and each word according to my code can have 100 meanings, but maybe it has only 5 meanings then I will be allocating 95 extra space for nothing or maybe it has more than 100 meanings then the program will crash, I know the vector class is very easy and could be good use of, but the task is almost building my own vector class, to learn how it works. Thus **meanings and some other stuff remain the same and here is my code, Also I know I am causing memory leakage, how can I delete properly? :
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class Expression {
char *word_with_several_meanings; // like "bank", "class"
char **meanings; // a pointer to a pointer stores all meanings
int meanings_ctr; // meanings counter
//-----------FUNCTIONS------------------------------------------------
public:
void word( char* = NULL );
void add_meaning(char* = NULL);
char* get_word();
int get_total_number_of_meanings();
char* get_meaning(int meanx = 0);
Expression(int mctr = 0); // CTOR
~Expression(); // DTOR
};
Expression::Expression(int mctr ) {
meanings_ctr = mctr; // Setting the counter to 0
meanings = new char * [100]; // Allocate Space for 100 meanings
}
Expression::~Expression() {
delete [] meanings; // Deleting the memory we allocated
delete [] word_with_several_meanings; // Deleting the memory we allocated
}
void Expression::word( char *p2c )
{
word_with_several_meanings = new char[strlen(p2c)+1];
// copy the string, DEEP copy
strcpy(word_with_several_meanings, p2c);
}
void Expression::add_meaning(char *p2c)
{
//meanings = new char * [meanings_ctr+1];
meanings[meanings_ctr] = new char[strlen(p2c)+1];
strcpy(meanings[meanings_ctr++],p2c);
}
char * Expression::get_meaning( int meanx )
{
return *(meanings+meanx);
}
char * Expression::get_word()
{
return word_with_several_meanings;
}
int Expression::get_total_number_of_meanings()
{
return meanings_ctr;
}
int main(void) {
int i;
Expression expr;
expr.word("bank ");
expr.add_meaning("a place to get money from");
expr.add_meaning("b place to sit");
expr.add_meaning("4 letter word");
expr.add_meaning("Test meaning");
cout << expr.get_word() << endl;
for(int i = 0; i<expr.get_total_number_of_meanings(); i++)
cout << " " << expr.get_meaning(i) << endl;
Expression expr2;
expr2.word("class");
expr2.add_meaning("a school class");
expr2.add_meaning("a classification for a hotel");
expr2.add_meaning("Starts with C");
cout << expr2.get_word() << endl;
for( i = 0; i<expr2.get_total_number_of_meanings(); i++)
cout << " " << expr2.get_meaning(i) << endl;
Expression expr3;
expr3.word("A long test ... ");
char str[] = "Meaning_ ";
for (int kx=0;kx<26;kx++)
{
str[8] = (char) ('A'+kx);
expr3.add_meaning(str);
}
cout << expr3.get_word() << endl;
for(i = 0; i < expr3.get_total_number_of_meanings(); i++)
cout << " " << expr3.get_meaning(i) << endl;
return 0;
}
When you are allocating a multi dimensional array with new then you are allocating it with a loop, e.g.
char **x = new char*[size]
for (int i = 0; i < N; i++) {
x[i] = new int[size];
}
So you also have to delete it in this fashion:
for (int i = 0; i < N; i++) {
delete[] x[i];
}
delete[] x;
Thus when you're having arbitrary sizes of your array you'll have to store them somewhere for using them within the destructor.
delete [] meanings; // Deleting the memory we allocated
won't get rid of your memory allocated, only the pointers themselves.
To free up the actual memory, you will need to iterate through your meanings array, and delete [] each element in it.
Something like:
for (int i = 0; i < meanings_ctr; ++i)
{
delete [] meanings[meanings_ctr];
meanings[meanings_ctr] = NULL;
}
delete [] meanings;
--
For the problem of what to do if you get more than 100 meanings (or in general when your collection is full), the standard technique is to allocate a new array that is double the size (which you can do since it is dynamic), copy your existing collection into that one, and then dispose of your existing one.
I'd use a simple linked list (this is simplified, not complete and untested; also there should be proper getters/setters and stuff):
class Meaning {
char text[20];
Meaning *next;
Meaning(const char *text) : next(0) {
strcpy(this->text, text);
}
}
class Word {
char text[20];
Meaning *first;
Meaning *last;
Word(const char *text) : first(0), last(0) {
strcpy(this->text, text);
}
~Word() {
Meaning *m = first, *n;
while(m) {
n = m->next;
delete m;
m = n;
}
}
void AddMeaning(const char *text) {
if (last) {
last = last->next = new Meaning(text);
}
else {
first = last = new Meaning(text);
}
}
void print() {
printf("%s:\n\t", text);
Meaning *m = first;
while (m) {
printf("%s, ", m->text);
m = m->next;
}
}
}