I want to work with Images, and I want to create my structure Image with first 2 values to be it's size (grayscale) and the third - data (2D array of size m by n).
How can I implement it? If I don't know the image's size in advance. Something like this:
struct Image{
int n;
int m;
data = 2D array of size mxn
}
Instead I would use
struct Image{
int n;
int m;
vector<vector<int>> data;
}
Of you could use a 1D vector that has size
vector<int> data(m * n);
Ignoring encapsulation, something like this could work:
#include <vector>
struct Image {
int n;
int m;
std::vector<std::vector<int>> data; // May want to change int type ?
Image(int n, int m) : n(n), m(m), data(n) {
for (int row = 0; row < n; row++) {
data[row].resize(m);
}
}
};
// Example Usage
Image img(10, 10);
for (int row = 0; row < img.n; row++) {
for (int col = 0; col < img.m; col++) {
img.data[row][col] = valueFromImageFile;
}
}
If you're not just looking for something quick and dirty, and this is going to be an ongoing project, I would recommend that you learn more about object oriented concepts :)
There are two common patterns for that (even if inherited from C). Both share a common principle : a logical separation between a fixed size header and a variable part.
First method : the struct contains the fixed size part and a pointer (or pointers in a generic case) to memory allocated with new (in constructor) and deallocated in destructor. It is a bit hand-driven, but easy to use and to understand.
Image could look like (I assume Pixel is a class representing a single pixel):
class Image {
private:
int _x, _y;
Pixel *data;
public:
Image(int x, int y);
~Image();
Pixel& getPixel(int i, int j) {
return data[i * _y + j];
}
};
Image::Image(int x, int y) {
data = new Pixel[x * y];
_x = x;
_y = y;
}
Image::~Image() {
delete[] data;
data = NULL;
}
Second method : the pseudo-struct is composed of a fixed size header, and of a dynamic part. The dynamic part is declared in the struct to be of size 1. It is obviously false, and you cannot use sizeof on such a struct, but as C does not inforce comparison of array to the declared size it works :
class Image {
private:
int _x, _y;
Pixel data[1];
Image(int x, int y); // private to force usage of createImage
public:
static Image* createImage(int x, int y);
Pixel& getPixel(int i, int j) {
return data[i * _y + j];
}
};
Image* Image::createImage(int x, int y) {
Image* image = (Image *) malloc(sizeof(Image) + (x * y - 1) * sizeof(Pixel));
image->_x = x;
image->_y = y;
return image;
}
I must admit it is really C code embedded in C++, but I cited it because it is heavily used in Microsoft Win32 API.
Related
I'm designing a dynamic hurtbox for characters in a text-based game, which catches the locations of hits (or misses) of a weapon swung at them. The location (indices) and damage (magnitude) of hits are then translated into decreases in corresponding limb health variables for a character. My thoughts are that this hurtbox would best be implemented using a class with some 3D vector/array member.
Naturally, I might want varying dimensions of the 3D container for different sizes of enemy, but I'm aware that size is usually determined upon initialization. So here's my question:
Would it be more efficient to use a C-style dynamic array, the size of which I can decide and allocate inside a parameterized constructor, like so?
class hurtBox {
private:
int ***hurtBoxMatrix;
public:
hurtBox(int l, int w, int h) {
hurtBoxMatrix = new int**[l];
for (int i = 0; i < l; i++) {
hurtBoxMatrix[i] = new int*[w];
for (int j = 0; j < w; j++) {
hurtBoxMatrix[i][j] = new int[h] ();
}
}
}
};
Or, would a vector that I push elements into, up to my desired dimensions, suffice?
class hurtBox {
private:
vector<vector<vector<int>>> hurtBoxMatrix;
public:
hurtBox(int l, int w, int h) {
for (int i = 0; i < l; i++) {
hurtBoxMatrix.push_back(vector<vector<int>>);
for (int j = 0; j < w; j++) {
hurtBoxMatrix[i].push_back(vector<int>);
for (int k = 0; k < h; k++) {
hurtBoxMatrix[i][j].push_back(0);
}
}
}
}
};
I imagine the former, since that first allocation is constant time, right? Is there a way to do this that's better than either of these?
Thanks in advance.
You'd be better off simply allocating the 3D array in a single allocation, and use indexing to access the elements. Allocation for the std::vector storage can then be handled in the constructor for std::vector.
In general it's best to avoid:
multiple allocations
repeatedly calling push_back
class hurtBox {
private:
vector<int> hurtBoxMatrix;
int m_l;
int m_w;
int m_h;
public:
hurtBox(int l, int w, int h)
: hurtBoxMatrix(l * w * h), m_l(l), m_w(w), m_h(h) {}
int& operator (int i, int j, int k) {
return hurtBoxMatrix[ I*m_w*m_h + j*m_w + k ];
}
const int operator (int i, int j, int k) const {
return hurtBoxMatrix[ i*m_w*m_h + j*m_w + k ];
}
};
I have a 2D grid class which is made up of cells, I must store the cells in a 1D std::vector (due to specification) that models the 2D grid.
I have written a Grid::resize function that resizes the grid to a new width and height but preserves the content of the grid within the kept region (all new cells are set to
Cell::DEAD).
I have written a resize function that when run causes a segmentation fault: 11, which I assume means the function is trying to access data outside the bounds of the vector but I am not sure where the error is.
Class
class Grid {
private:
std::vector<Cell> grid_cells;
unsigned int width;
unsigned int height;
public:
Grid(const unsigned int width, const unsigned int height);
}
Constructor code
Grid::Grid(const unsigned int width, const unsigned int height) {
std::vector<Cell> cells(width*height);
int i;
for(i=0;i<(width*height);i++){
cells[i] = Cell::DEAD;
}
this->width = width;
this->height = height;
this->grid_cells = cells;
}
Resize function
void Grid::resize(const unsigned int new_width, const unsigned int new_height) {
std::vector<Cell> new_cells(new_width*new_height);
unsigned int x, y;
for(x = 0; x < new_width; x++) {
for(y = 0; y < new_height; y++) {
if(x < this->width && y < this->height) {
new_cells[get_index(x,y)] = this->grid_cells[get_index(x,y)];
}
else {
new_cells[get_index(x,y)] = Cell::DEAD;
}
}
}
this->width = new_width;
this->height = new_height;
this->grid_cells = new_cells;
}
My code is being tested by running pre-made test suites on it, I believe the error comes from the body of the for loops because when I comment this out the test suite runs to the end but when its left it I get a segmentation fault and it stops running.
Edit:
get_index function for converting the 2D x,y coordinate to the 1D index:
const unsigned int Grid::get_index(const unsigned int x, const unsigned int y) const{
return ((y * this->width) + x + 1);
}
const unsigned int
Grid::get_index(const unsigned int x, const unsigned int y) const{
return ((y * this->width) + x + 1);
}
You are using the original grid width to compute the index in the new grid. They can be vastly different, so you will copy the wrong cell or access memory out of bound of new_cells
Furthermore, as it is, you always perform invalid memory accesses when trying to access the last cell with coordinate (width-1, height-1), which is at height*width - width + width -1 + 1 = height*width. The last valid index for the vector is height*width-1
You could use a free function
unsigned int
get_index(const unsigned int x, const unsigned int y, const unsigned int width) const{
return (y * width) + x ;
}
and have
new_cells[get_index(x,y, new_width)] = this->grid_cells[get_index(x,y, this->width)]
I'm looking to define a 2 dimensional character array where arguments I pass to a function holding the array will be used to determine the size each dimension of the array.
int func(const int x, const int y) {
char maze[x][y] = { 0 };
return 0;
}
When defining x & y inside the function as constant integers the array is defined just fine. When x and y are arguments to the function the program won't compile. Why is this and how do I get around it?
You can make a wrapper around 1-dimensial array like this:
class Maze {
friend class Row;
public:
/* This helper class represents a single row of the maze */
class Row {
friend class Maze;
Maze& owner;
std::size_t row;
Row(Maze& owner_, std::size_t row_) : owner(owner_), row(row_) {}
/* this operator resolves 2nd pair of brackets */
public:
inline char& operator[](std::size_t col) { return owner.data[col + row*owner.cols]; }
};
Maze(std::size_t rows_, std::size_t cols_)
: data(rows_ * cols_, 0)
, cols(cols_)
{}
/* this operator resolves 1st pair of brackets */
inline Row operator[](std::size_t index) { return Row(*this, index); }
private:
std::vector<char> data;
std::size_t cols;
};
...
Maze m(5, 10);
m[2][3] = 1;
You need to use dynamic memory allocation. Variadic length arrays is not a part of c++ standart. However variadic length arrays available as an extension for GCC. Although you can use STL or implement your class, dont forget about new[] and the one-dimensional representation for two-dimensional array:
char* maze = new char[x*y]; // create
maze[i + j * x]; // access
delete[] maze; // delete
It is compact and in most cases fast.
When defining x & y inside the function as constant integers the array is defined just fine
It works because the size of your array is defined and known by your compiler, known at compile-time
When x and y are arguments to the function the program won't compile.
When you wish to define you array only when you call your function, you ask your program to do that during runtime
. As answered by Dmytro Dadyka, you have to use use dynamic memory allocation and manage yourself memory deallocation (delete[] maze; // delete)
Here is an alternative of defining dynamically your 2D array using template! Notice that it is always done at compile-time.
template<int X, int Y>
int f()
{
char c[X][Y];
for(int x=0; x < X; ++x)
{
for(int y=0; y < Y; ++y)
{
c[x][y] = '1';
}
}
// write your algorithm now!....
c[2][2] = 'a';
for(int x=0; x < X; ++x)
{
for(int y=0; y < Y; ++y)
{
std::cout << c[x][y] << " ";
}
std::cout << std::endl;
}
return 0;
}
int main()
{
f<5,5>();
f<7,4>();
return 0;
}
Please take a look at the code below:
#include <iostream>
using namespace std;
int main(){
char matrix[2][2][2];
return 0;
}
int getMatrixData(char matrix[][2][2], int x, int y, int z) {
return matrix[x][y][z];
}
When matrix 3d array passed in as a parameter into a function, why is it ok not to specify the first [] size? How this missing dimension can be explained?
Your code is syntactically incorrect. Assuming you meant int getMatrixData(char matrix[][2][2], int x, int y, int z).
When you pass array arguments to function, array decays to pointer to first element (type char [2][2] in this case).
Now some syntax of array and pointer are similar so you don't find much difference.
When multidimensional array is passed, for example 3d in your case, it can be seen as array of 2-d arrays. So you need to give the type of each element char [2][2] in your case and you can skip the dimension of final array as it will decay to pointer anyway. char [2][2] is the information compiler needs to compute the offset of each element.
offset of matrix[x][y][z] = base address of matrix +
x * sizeof(char [2][2]) +
y * sizeof(char [2]) +
z
If you don't pass the dimensions of initial element, compiler can't resolve sizeof in above equation. Passing skipped dimension is optional.
In c++ I would use multidimensional arrays in a different way. There are many topics on the internet about it.
This topic explains how you could do it using a char***. E.g.:
char getMatrixData(char*** matrix, int x, int y, int z)
{
return matrix[x][y][z];
}
int main()
{
char ***matrix = new char**[2];
for (auto i = 0; i < 2; i++)
{
matrix[i] = new char*[2];
for (auto j = 0; j < 2; j++)
{
matrix[i][j] = new char[2];
}
}
getMatrixData(matrix, 1, 1, 1);
// N.B.! you should normally free the memory using delete []!!
// But in this case the program ends, so the memory is freed anyhow.
return 0;
}
But you could also use the std::vector type
#include <vector>
using std::vector;
using CharVector1D = vector<char>;
using CharVector2D = vector<CharVector1D>;
using CharVector3D = vector<CharVector2D>;
char getMatrixData(CharVector3D const& matrix, int x, int y, int z)
{
return matrix[x][y][z];
}
int main()
{
CharVector3D matrix(2, CharVector2D(2, CharVector1D(2)));
getMatrixData(matrix, 1, 1, 1);
return 0;
}
However, c++ is supposed to be an object oriented programming language. So it is probably better to define an matrix object.
#include <vector>
using std::vector;
template <class T>
class Matrix3D
{
private:
size_t _sizeX;
size_t _sizeY;
size_t _sizeZ;
vector<T> _data;
public:
Matrix3D(size_t const x_size, size_t const y_size, size_t const z_size)
: _sizeX(x_size)
, _sizeY(y_size)
, _sizeZ(z_size)
, _data(vector<T> (x_size*y_size*z_size))
{}
T GetData(size_t const x, size_t const y, size_t const z) const
{
return _data.at(x + (_sizeX * (y + (_sizeY * z))));
}
};
int main()
{
Matrix3D<char> matrix(2, 2, 2);
matrix.GetData(1, 1, 1);
return 0;
}
I'm sure this has been asked before, but the search terms are failing me for my specific situation.
Essentially, I'm creating a 2D tile engine. For overall efficiency and to reduce a bit of overhead, I have decided to implement my own simple memory management container...
The following code is what I'm using to do so:
Tile.h:
//Tile class
class CTile
{
public:
CTile();
~CTile();
unsigned int row;
unsigned int column;
};
//Container class
class CTileLayer
{
public:
CTileLayer(unsigned int nRows, unsigned int nCols, float w, float h);
~CTileLayer();
protected:
CTile** m_tiles;
unsigned long count;
void Allocate(unsigned int nRows, unsigned int nCols, float w, float h);
void Free();
};
Tile.cpp:
#include "Tile.h"
CTile::CTile() : row(0), column(0)
{
}
CTile::~CTile()
{
}
CTileLayer::CTileLayer(unsigned int nRows, unsigned int nCols, float w, float h) : m_tiles(NULL)
{
Allocate(nRows, nCols, w, h);
}
CTileLayer::~CTileLayer()
{
if(m_tiles != NULL) Free();
}
void CTileLayer::Allocate(unsigned int nRows, unsigned int nCols, float w, float h)
{
unsigned int column = 0, row = 0;
if(m_tiles != NULL) Free();
m_tiles = new CTile*[count = (nRows * nCols)];
for( unsigned long l = 0; l < count; l++ ) {
m_tiles[l] = new CTile[count];
m_tiles[l]->column = column + 1;
m_tiles[l]->row = row + 1;
//
//...
//
if(++column > nCols) {
column = 0;
row++;
}
}
}
void CTileLayer::Free()
{
delete [] *m_tiles;
delete [] m_tiles;
m_tiles = NULL;
count = 0;
}
So by seeing how each tile is allocated/freed, is it safe to say this won't produce any memory leaks?
If it will, what would be the most appropriate way to free this array, while making sure each object's destructor gets called? Should I loop through the array, manually deleting each object instead?
Why not do this:
CTile* m_tiles;
m_tiles = new CTile[nRows*nColumns];
delete[] m_tiles;
You called new CTile[count] count times, once for each m_tiles[l].
You need to therefore call delete[] count times, for each m_tiles[l].
It isn't clear what count is good for, however. You should be using nRows and nColumns for the two layers of the array. Right now you have nRows * nRows * nColumns * nColumns instances of CTile allocated, which is probably too many.
Instead try
m_tiles = new CTile*[nRows];
m_tiles[l] = new CTile[nColumns];