initialization with '{...}' expected for aggregate object - c++

I am trying to do string parsing (and it is proving to be a huge pain). I'm getting this error: "initialization with '{...}' expected for aggregate object "
I have an element defined like this:
Element * currentBar = new Element("Bar");
I want to make an array or something to store multiple bars, so I am trying to do something like this:
Element allBars [] = new Element("Bars");
I am pretty sure this is not what I want to do, especially since I am getting this error "initialization with '{...}' expected for aggregate object "
This is a segment of my code:
if(!chartDataString.empty()){
chartData.clear();
int index = 0;
char c, c1, c2;
inputMode currentInputMode = inputMode::UNKNOWN;
Element * cur = NULL;
while(index<chartDataString.size()){
c = chartDataString[index];
c1 = chartDataString[index+1];
c2 = chartDataString[index+2];
string s;
s = c1;
Element * currentBar = new Element("Bar");
Element allBars [] = new Element("Bars");
if(c == '*'){
if(c1 == 'i'){
currentBar->addChild(Element("rehearsalLetter", "info"));
}
else{
currentBar->addChild(Element("leftDoubleBarLine", s));
index++;
}
else if(c == '['){
currentBar->addChild(Element("leftDoubleBarLine"));
}
else if(c == 'T'){
string signature = string() + c1 + '/' + c2;
currentBar->addChild(Element("timeSignature", signature));
index += 2;
}
//start a new bar
else if(c == '|'){
allBars->addChild(currentBar);
currentBar = new Element("bar");
}
And my element class just in case it's helpful:
#include <string>
#include <ostream>
#include "ArrayList.h"
class Element{
public:
Element(){}
Element( const string& _title ){
title = _title;
}
Element( const string& _title, const string& _value){
title = _title;
value = _value;
};
~Element(){};
void addChild(Element & child){
children.add(child);
}
void serialize(ostream & o ){
if( children.size() == 0 ){
o << "<" << title << ">";
o << " " << value << " ";
o << "</" << title << ">";
}else{
o << "<" << title << ">" << endl;
for( int i = 0; i < children.size(); ++i ){
children.elementAt(i).serialize(o);
}
o << "</" << title << ">" << endl;
}
}
private:
string title;
string value;
ArrayList<Element> children;
};

When you declare a variable Element allBars [] the compiler does expect a list of values like { Element("Bar"), Element("Foo") }. This is a static array whose size is known at compile time. So for example:
Element allBars [] = { Element("Bar"), Element("Foo") };
(Beware some compiler do require the number of element to be specified in the []).
If you want to declare a dynamic array, either you use a std::vector<Element> allBars;, which I would strongly recommend, because it cause grow and shrink without you worrying for memory allocation and deallocation. Your class should have a default constructor, a copy constructor and an assignment operator (even if generated by the compiler).
Or you use a pointer that you fill with new Element[xxx] like Element* allBars = new Element[size_of_array];, that cannot grow nor shrink and that you will have to explicitly delete using delete[] in order to call the Element destructor for every element of your array.
In both case, each Elementof the array will be initialized using the default constructor.

Are you trying to initialise the array with a single Element, but want it to vary in size? If so, use a vector:
vector<Element*> allBars;
vector.push_back(new Element());
If you are able to use C++11, consider using unique_ptr to ensure no memory leaks, and use emplace_back to avoid a copy when adding to the vector:
vector<unique_ptr<Element>> allBars;
vector.emplace_back(unique_ptr<Element>(new Element()));

Related

Changing a private value of a class in method not returning the changes to main()

I'm having a problem that I haven't found an answer for in a week now. I have a dynamic array class and it has a method to add string values to it. It's supposed to represent an inventory you can add items to. However, I find that the changes made in the method to the private values of the class element aren't "updated" when I try to call for a print-method for the class element "backpack" later in the main(). I think this might be a problem due to referencing issues, but I've seen this work when a class hasn't been in a different module.
My "backpack" module print and add methods:
const int INITIAL_SIZE = 5;
Inventory::Inventory():
array_(new string[INITIAL_SIZE]),
max_space_(INITIAL_SIZE),
used_space_(0) {}
void Inventory::add(string item){
if ( size() == max_space_ ) {
string* new_array = new string[2 * max_space_];
for ( int i = 0; i < size(); ++i ) {
new_array[i] = array_[i];
}
delete [] array_;
array_ = new_array;
max_space_ = 2 * max_space_;
}
array_[used_space_] = item;
++used_space_;
}
void Inventory::print() {
for ( int i = 0; i < size(); ++i ) {
cout << array_[i] << endl;
}
}
The main():
Inventory inv;
string input;
while (cout << "input> "
and getline(cin,input)){
add_to_bag(input,inv);
So the point is you reset the inventory when you give it new contents. The function add_to_bag(); is:
void add_to_bag(string input, Inventory inv){
const string WHITESPACE1_REGEX = "[[:space:]]*";
const string WHITESPACE2_REGEX = "[[:space:]]+";
const string WORD_REGEX = "[[:alpha:]_]+";
const string LINE_REGEX =
WHITESPACE1_REGEX +
WORD_REGEX +
"(" +
WHITESPACE2_REGEX +
WORD_REGEX +
")*" +
WHITESPACE1_REGEX;
regex line_reg(LINE_REGEX);
regex word_regex(WORD_REGEX);
string line = input;
if ( regex_match(line, line_reg) ) {
sregex_iterator iter(line.begin(), line.end(), word_regex);
sregex_iterator end;
while ( iter != end ) {
inv.add(iter->str());
++iter;
}
} else {
cout << "Error: unknown inventory contents." << endl;
}
}
Your problem is:
void add_to_bag(string input, Inventory inv);
You pass a copy of the Inventory object to add_to_bag. You modify that copy ... and then it gets thrown away. The fix is to pass by reference:
void add_to_bag(string input, Inventory &inv);
Incidentally, in real-life code, I would strongly advise the use of std::vector<std::string> rather than "roll your own". There are a number of tricky exception handling issues you have got wrong here - and unless Inventory doesn't have a destructor (implying a memory leak), or does have a correct copy constructor I would have expected you to run into "double free" issues. (Read about "The Rule of Three".)
A simple way to design your class would be as follows:
class Inventory {
private:
std::vector<std::string> items_;
public:
Inventory(){}
~Inventory(){}
void addItem( const std::string& item ) {
items_.push_back( item );
}
void printInventory() const {
int idx = 0;
for (; idx < items_.size(); ++idx ) {
std::cout << items_[idx] << std::endl;
}
}
void clearInventory() {
items_.clear();
}
};
And as for your problem Martin Bonner had already answered it with the modifying of the copy and the removal of it afterwards and the other issues with the memory management.

Why does returning a data structure rather than a pointer mess with the integrity of my data?

I'm building a sparse matrix class that holds two arrays (row and column) of pointers to doubly linked lists (down and right). Sort of like this:
rows
c0123456789
o1
l2
u3
m4 A-->B-->
n5 | |
s6 | V
7 V D-->
8 C-->
9
Both arrays are initialized to have nullptr in every space until something is inserted in that place.
I have a function "readFile" that reads in objects from a text file and inserts them into this sparse matrix. For some reason, before this function returns, all of the data in it is fine, but after I return, I get random memory locations in my arrays. Here is main.cpp
#include <iostream>
#include <string>
#include <fstream>
#include "sparseMatrix.h"
using namespace std;
class basic
{
private:
int x, y;
string word;
basic *down;
basic *right;
public:
basic(int x, int y, string word)
{
this->x = x;
this->y = y;
this->word = word;
down = nullptr;
right = nullptr;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
basic *getRight()
{
return right;
}
void setRight(basic *newRight)
{
right = newRight;
}
basic *getDown()
{
return down;
}
void setDown(basic *newDown)
{
down = newDown;
}
void print()
{
cout << "X: " << x << ", Y: " << y << ", word: " << word << ".\n";
}
};
sparseMatrix<basic> readFileBROKEN(string pathToFile);
sparseMatrix<basic> *readFile(string pathToFile);
int main()
{
cout << "Working:\n\n";
sparseMatrix<basic> *workingMatrix = readFile("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
workingMatrix->printyArray();
cin.get();
cout << "Not working:\n\n";
sparseMatrix<basic> brokenMatrix = readFileBROKEN("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
brokenMatrix.printyArray();
cin.get();
delete workingMatrix;
}
sparseMatrix<basic> readFileBROKEN(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> matrix(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix.insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix.printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
sparseMatrix<basic> *readFile(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> *matrix = new sparseMatrix<basic>(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix->insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix->printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
and here is sparseMatrix.h:
template <class dataType>
class sparseMatrix
{
private:
//The dimensions of the sparse matrix.
int width;
int height;
//Dynamic array of pointers to heads of linked lists.
dataType** xArray;
dataType** yArray;
public:
//Constructor. Sets everything in the two arrays to nullptr.
sparseMatrix(int height, int width)
{
this->width = width;
this->height = height;
xArray = new dataType*[width];
yArray = new dataType*[height];
for (int row = 0; row < height; row++)
{
this->yArray[row] = nullptr;
}
for (int col = 0; col < width; col++)
{
this->xArray[col] = nullptr;
}
}
//Deconstructor. First goes through the matrix and looks for every city it can find, and deletes
//all of those. Then when it's done, it deletes the two dynamic arrays.
~sparseMatrix()
{
dataType *currentdataType;
dataType *next;
for (int row = 0; row < height; row++)
{
currentdataType = yArray[row];
while (currentdataType != nullptr)
{
next = currentdataType->getRight();
delete currentdataType;
currentdataType = next;
}
}
delete [] yArray;
delete [] xArray;
}
//Creates a copy of the data we are passed, then creates links to this copy.
void insert(dataType data)
{
//Make sure the data is valid.
if (data.getX() < 0 || data.getX() >= width || data.getY() < 0 || data.getY() >= height)
{
std::cout << "That dataType doesn't fit into the sparse matrix!\n";
data.print();
std::cin.get();
}
else
{
//Copy the data we were passed.
dataType *newData = new dataType(data);
//Easy case. If nothing is in this row, set yArray[row] to the address of this data.
if (yArray[data.getY()] == nullptr)
{
yArray[data.getY()] = newData;
}
//Not so easy case. Move forward (right) until we find the right location, then set links.
else
{
dataType *current = yArray[data.getY()];
while (current->getRight() != nullptr)
{
current = current->getRight();
}
current->setRight(newData);
}
//Easy case. If nothing is in this col, set xArray[col] to the address of this data.
if (xArray[data.getX()] == nullptr)
{
xArray[data.getX()] = newData;
}
//Not so easy case. Move forward (down) until we find the right location, then set links.
else
{
dataType *current = xArray[data.getX()];
while (current->getDown() != nullptr)
{
current = current->getDown();
}
current->setDown(newData);
}
}
}
void printyArray()
{
for (int r = 0; r < height; r++)
{
if (yArray[r] != nullptr)
{
std::cout << r << ' ';
//yArray[r]->print();
}
}
}
};
readFile reads everything in from a file that looks like this:
0 0 hello
5 2 world
6 8 foo
9 5 bar
...
As expected, before returning, the only locations that are NOT nullptr are the ones that I have inserted into. (0, 2, 8 and 5). However when the function returns, EVERY SINGLE location in the array is not nullptr. I added a second function which returns a pointer to dynamically allocated sparseMatrix object, rather then returning the object itself, and this fixed it. However, I don't understand why. It seems like these two functions should behave identically the same way.
Also, the part that is most confusing to me, why does this run perfectly fine in Xcode, but not in Visual Studio?
tomse's answer is correct and gives the why and a fix, but it's an unnecessarily expensive fix for this problem. His suggestion of the copy constructor also solves numerous future problems such as the classics Why did my vector eat my data? and Dude, where's my segfault? Make the copy constructor. Don't use it unless you have to.
I think Andras Fekete got the problem right, but his post is kind of garbled. His solution is bang on, though.
Define your function like this:
bool readFile(string pathToFile, sparseMatrix<basic> & matrix)
Remove the definition of matrix inside the function in favour of the one passed in.
Return false on error so you know the matrix is bad (or use exceptions).
Create the matrix in the calling function and pass it into the revised reader function.
sparseMatrix<basic> matrix(100, 100);
if readFile("C:/users/jmhjr/desktop/testdata.txt", matrix);
That puts you right back where you were with the pointer version, but without the pointer and without having to do the extra work of copying data you didn't need to copy.
Your function:
sparseMatrix<basic> readFileBROKEN(string pathToFile)
returns a copy of the object (which is OK), but sparseMatrix does not define a copy constructor, so the default generated will be used which creates a shallow copy by just copying the adresses inside the returned object.
But the memory where the address points to is deleted when you leave your function (because the destructor of the locally created object is called).
To solve this you have to define your own copy contructor in sparseMatrix which copies all the content of the object.
sparseMatrix(const sparseMatrix& rhs) :
width(rhs.width),
height(rhs.height),
xArray(nullptr),
yArray(nullptr)
{
... and now copy all the content from rhs.xArray to this->xArray,
(same for yArray)
}
The problem is that you're allocating 'matrix' inside both of the readFile functions. Upon returning from the function, both variables are deallocated. However, returning the value (eradFile) the matrix is copied into your variable of the calling function, whereas returning the pointer (readFileBROKEN) is just returning the address where the matrix used to be stored.
To fix this, you should allocate the 'matrix' variable, and pass in a reference to the function. Then the function can return a void while stuffing the matrix properly.

Seg fault from constructor

I call this code in main()
for (COwnerList l=b1.ListOwners(10); !l.AtEnd(); l.Next())
cout << l.Surname() << ", " << l.Name () << endl;
for (CCarList l=b1.ListCars("Peter","Smith"); !l.AtEnd(); l.Next ())
cout << l.RZ () << ", " << l.VIN() << endl;
for (COwnerList l=b1.ListOwners(10); !l.AtEnd(); l.Next())
cout << l.Surname() << ", " << l.Name() << endl;
I tried to debug and found out seg fault comes from Constructor of COwnerList
COwnerList CRegister::ListOwners (unsigned int vin) const
{
vector<Car>::const_iterator it;
COwnerList tmp;
it = lower_bound(byVINList.begin(), byVINList.end(), Car("",vin), cmpVIN);
if(it != byVINList.end())
tmp.car = &(*it);
tmp.in = it->owners.end() - it->owners.begin();
return tmp;
}
constructor im calling looks like this:
COwnerList::COwnerList(void)
{
here = car->owners.begin();
i = 0;
in = car->owners.end() - car->owners.begin();
}
interesting is it doesnt crash after 1st for in main(), so there must be something wrong in the code between those 2 for cycles in main(), but i have no idea what it could be i am not modifying anything related to ListOwners() there
EDIT1
Car constructor:
Car::Car( const string & pid,
const unsigned int & pvin = 0,
const string & cname = "",
const string & csurname = "")
{
rz = pid;
VIN = pvin;
name = cname;
surname = csurname;
}
EDIT2
class COwnerList
{
public:
COwnerList(void);
string Name ( void ) const;
string Surname ( void ) const;
bool AtEnd ( void ) const;
void Next ( void );
//vector<pair<string, string> > powners;
const Car *car;
int in;
private:
vector<pair<string, string> >::const_iterator here;
int i;
};
The problem is that the car pointer is not initialized during the call in the COwnerList constructor. In the first loop, you might have got lucky. Things like this happens all the time. Sometimes the OS won't throw a seg fault everytime when you are calling a code which is not allocated yet.
Just put a condition guard in your code like this:
if (car != NULL) {
here = car->owners.begin();
i = 0;
in = car->owners.end() - car->owners.begin();
}
The error is more likely you are modifying the vector after saving that tmp.toto pointer to the vector's internal storage.
Note that when you do tmp.car = &(*it) you are making a pointer towards the internal storage of the vector.
If later you call push_back() on the vector, you cannot rely in the pointers that you had before, because the vector can reallocate its contents in other memory addresses after you call the push_* methods.
Also note that a debugger may not tell the exact line that has to be corrected, even if the crash happens there. The debugger may tell you the first line where the problem is evident, but the cause of the problem may have happened several lines before.

C++ String and Pointers Manipulation

Suppose I have this data structure in C++ :
struct Stash {
int size; // Size of each space
int quantity; // Number of storage spaces
int next; // Next empty space
// Dynamically allocated array of bytes:
unsigned char* storage;
// Functions!
void initialize(int size);
void cleanup();
int add(const void* element);
void* fetch(int index);
int count();
void inflate(int increase);
};///:~
void Stash::initialize(int sz) {
size = sz;
quantity = 0;
storage = 0;
next = 0;
}
int Stash::add(const void* element) {
if(next >= quantity) // Enough space left?
inflate(increment);
// Copy element into storage,
// starting at next empty space:
int startBytes = next * size;
unsigned char* e = (unsigned char*)element;
for(int i = 0; i < size; i++){
storage[(startBytes + i)] = e[i];
}
next++;
return(next - 1); // Index number
}
void* Stash::fetch(int index) {
// Check index boundaries:
assert(0 <= index);
if(index >= next)
return 0; // To indicate the end
// Produce pointer to desired element:
int value = (index*size);
return &(storage[value]);
}
int Stash::count() {
return next; // Number of elements in CStash
}
void Stash::inflate(int increase) {
assert(increase > 0);
int newQuantity = quantity + increase;
int newBytes = newQuantity * size;
int oldBytes = quantity * size;
unsigned char* b = new unsigned char[newBytes];
for(int i = 0; i < oldBytes; i++)
b[i] = storage[i]; // Copy old to new
delete []storage; // Old storage
storage = b; // Point to new memory
quantity = newQuantity;
}
void Stash::cleanup() {
if(storage != 0) {
cout << "freeing storage" << endl;
delete []storage;
}
} ///:~
Suppose now I use the data structure to memorize strings in this way :
int main(){
Stash* st1 = new Stash;
st1->initialize(sizeof(string));
string s1 = "This is a GOOD morning";
st1->add(&s1);
string s2 = "This is a BAD morning";
st1->add(&s2);
string* s3;
s3 = static_cast<string*> (st1->fetch(0));
cout << *s3 << endl;
string* s3;
s3 = static_cast<string*> (st1->fetch(1));
cout << *s3 << endl;
st1->cleanup();
delete st1;
return 0;
}
It Works!!! This is the output:
This is a GOOD morning
This is a BAD morning
But in this other way:
int main(){
Stash* st1 = new Stash;
st1->initialize(sizeof(string));
string s1 = "This is a GOOD morning";
st1->add(&s1);
s1 = "This is a BAD morning";
st1->add(&s1);
string* s3;
s3 = static_cast<string*> (st1->fetch(0));
cout << *s3 << endl;
string* s4;
s4 = static_cast<string*> (st1->fetch(1));
cout << *s4 << endl;
st1->cleanup();
delete st1;
return 0;
}
It doesn't work. This is the output:
This is a BAD morning
This is a BAD morning
So, what happened in the machine when I try to use the same reference?
I have tried with other datatypes and it works well.
The first use of s1 invokes the constructor:
string s1 = "This is a GOOD morning";
You then add the address of s1 to the Stash. Next you assign a new value to s1:
s1 = "This is a BAD morning";
This doesn't create a new string -- it invokes the assignment operator which replaces the same string object with a new value. You then save another copy of the address of s1:
st1->add(&s1);
If you look at the data in st1 then you'll see you have two copies of the same pointer, both pointing to s1. This is expected. In the first case you are storing pointers to two different objects which contain different values.
Your code copies the bytes making up a std::string container (NOT the characters that are in the string data). This probably consists of a pointer to the string data, plus a size and capacity.
When you write s1 = "stuff", the std::string internally allocates new memory, so its previous internal pointer is now invalid.
Then you retrieve that previous internal pointer from your data structure and try to use it, causing undefined behaviour.
If your intent is to save the characters in the string then you need to add s1.c_str() instead of &s1.
If your intent is to store a copy of any object then you need to invoke the copy constructor to create a copy; not do a bitwise copy as you are currently doing. You could also invoke a move constructor or move assignment operator if your intent is to store the object and not leave the original object behind.
In your second approach, you have used address of s1 to be stored in stack. And you are not copying the contents within the stack, so when you change s1 to have a different content earlier content also changes, because you essentially pushed the pointer not the copy of the content.
For example, if you do the following (copying the content to a new string to be used for push), this works:
string s1 = "This is a GOOD morning";
st1->add(new string(s1));
s1 = "This is a BAD morning";
st1->add(new string(s1));

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;
}
}
}