I am trying to make a class with a 2D array, the length of which is given in parameters of the constructor, so basically when I create an object called Screen, of the class CharMap, I want it to look like this: new CharMap Screen (/*any number*/, /*any number*/) and the two numbers are the length and width of the array. This is the code I wrote, but the damn compiler won't compile it because error: invalid use of non-static data member 'CharMap::yres'
In C++ an array is not able to have a dynamic size using stack memory. If you want to create an array from a constructor you have to allocate it with dynamic/heap memory.
The official "C++ Language Tutorial" doesn't cover vectors, and I don't have a programming teacher; Do you know where I can find a good tutorial for vectors?
I can't seem to find anything that explains how to create a two dimensional vector.
Also, I'm not sure a vector is the right type for this situation. My understanding is that the whole point of vectors is to be able to shrink or grow dynamically, but the grid I am trying to create will never need to change size for any reason.
You could dynamically allocate using the new operator like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// first number/second number are sizes
// like you would declare a normal array
// i.e. [1][3]
int **matrix;
matrix = newint*[ firstnumber ]; // array of pointers to int objects
for (int i = 0; i<firstnumber; ++i)
matrix[i] = newint[ secondnumber ];
// now you can just access it like any other array
// such as matrix[1][3]
So in your class private area declare int **matrix; and allocate the memory as required in your constructor
#include <iostream>
#include <stdlib.h>
#include <ctime>
usingnamespace std;
class CharMap
{
int xres;
int yres;
char **CharGrid;
public:
CharMap(int, int);
~CharMap()
{
FreeMapMemory();//Calls the function to release memory
}
void FreeMapMemory()//Release memory whenever you're planning to reassign CharGrid to a new CharGrid or simply destruction
{
for(int i=0;i<yres;i++)
{
delete []CharGrid[i];//Release the array of columns @ array[i][ALL];
}
delete []CharGrid;//Release the array of rows @ array[ALL];//Will be exclusive of sub new objects, thus we've deleted prior to this.
cout<<"New memory was *ideally* freed, unless reassigned before this process"<<endl;
}
char GetChar (int getx, int gety)
{
return CharGrid[gety-1][getx-1];
};
void WriteChar(int writex, int writey, char writechar)
{
CharGrid[writey-1][writex-1] = writechar;
}
void ClearMap()
{
for (int clry=0; clry<=(yres-1); clry++)
{
for (int clrx=0; clrx<=(xres-1); clrx++)
{
CharGrid[clry-1][clrx-1] = 'X';
};
};
};
void PrintMap()
{
system("CLS");
for (int printy=0; printy <= (yres-1); printy++)
{
for (int printx=0; printx <= (xres-1); printx++)
{
cout << CharGrid[printy][printx];
};
cout << "\n";
};
};
};
CharMap::CharMap (int x, int y)
{
xres = x;
yres = y;
CharGrid = newchar*[y];
for (int i=0; i<y; i++)
{
CharGrid[i] = newchar[x];
for(int j=0;j<x;j++)//Filling the array [i][j];j++
{
CharGrid[i][j] = '.';//defaulting a character; else garbage on read.
}
}
};
int main ()
{
CharMap Screen(20, 10);
Screen.PrintMap();
cout << 1;
cin.get();
Screen.WriteChar(3, 2, 'X');
Screen.PrintMap();
cout << 2;
cin.get();
return 0;
};
It takes time to understand, work it out and ask questions.
You should not declare a new variable array unless you know its definite sizes. In the function declaration, xres and yres has no definite value. So leave it as a blank as illustrated. Assign it a new data after you've determine xres and yres.
Must you declare and delete it this way?
Yes, we're literally declaring a pointer of pointers*; where the pointers* are pointing to array of characters.
When you destroy the object, you must release each new array within your array; the pointer is pointing to pointers - release the embedded first.
Edit: Comment on why I included FreeMemory method.
FWIW, I really dislike the "nested new" approach to this problem. It's clunky to initialize/teardown uses more memory, and is typically [ever so slightly] slower.
Manual memory management is also something you should avoid. My general rule is... if you are manually deleting, you're doing it wrong.
You can use a 1D vector to hold a 2D array. All you have to do is simulate the 2nd dimension with some basic math.
Here's a very simplistic class that should suffice. It's const correct, templated, and copyable, and properly encapsulated:
// make an array that is 16x7 (that is, width of 16, height of 7)
array2D<char> foo(16,7);
// resize the array to something else.... let's say 10x4
foo.resize(10,4);
// assign the element at x=4, y=2 to '@'
foo(4,2) = '@';
// print out that element:
std::cout << foo(4,2);
But xres is assigned a value immediately before feeding it to new, how is that indeterminate?
It is not assigned a value immediately before feeding it to new. It is assigned a value before feeding it to new a number of other times, but not prior to the first, which occurs before the body of the constructor is entered.
It is not assigned a value immediately before feeding it to new. It is assigned a value before feeding it to new a number of other times, but not prior to the first, which occurs before the body of the constructor is entered.
But when I create a CharMap object the first thing that happens is the constructor is called. The very first thing that the constructor does is assign xres and yres, so I thought that would make their values known before everything else in the object is created? I thought that was what constructors where for?
Disch, I would need to make that array2D class in addition to my CharMap class so I could then use it in CharMap?
> The very first thing that the constructor does is assign xres and yres
The very first thing that happens in your constructor does is field initialisation. char **CharGrid = newchar*[xres];
At that point, xres is uninitialized.
#include <iostream>
int init( constchar* var, int value )
{ std::cout << "initialise " << var << " with " << value << '\n' ; return value ; }
int assign( constchar* var, int value )
{ std::cout << "assign " << value << " to " << var << '\n' ; return value ; }
struct A
{
int a = init( "A::a", b + 66 ) ; // *** bad *** b is used before it is initialised
int b = init( "A::b", 88 ) ;
int c = init( "A::c", 10 ) ;
A() // member initialisation in order of declaration ie. first a, then b, then c
{
std::cout << "in A::constructor body\n" ;
a = assign( "A::a", b + 66 ) ; // fine
b = assign( "A::b", 123 ) ;
c = assign( "A::c", 0 ) ;
}
};
int main ()
{
A a ;
}
clang++ -std=c++11 -stdlib=libc++ -O2 -Wall -Wextra -pedantic-errors main.cpp -lsupc++ && ./a.out
main.cpp:11:27: warning: field 'b' is uninitialized when used here [-Wuninitialized]
int a = init( "A::a", b + 66 ) ; // *** bad *** b is used before it is initialised
^
main.cpp:15:5: note: during field initialization in this constructor
A() // member initialisation in order of declaration ie. first a, then b, then c
^
1 warning generated.
initialise A::a with 66
initialise A::b with 88
initialise A::c with 10
in A::constructor body
assign 154 to A::a
assign 123 to A::b
assign 0 to A::c