I want to make a Class Color, and declare 2 Class Color objects to store a list of warm colors and cold colors respectively.
How do i initialize an array of char pointer and define it in the constructor in C++?
.h
Const int MAX = 5;
Class Color {
Char* list[MAX];
Int numList;
}
.cpp
//default constructor
Color() {
list[MAX] = nullptr;
numList = 0;
}
//custom constructor
Color(char* list_, int numList_) {
for (int i = 0; i < num_List_; i++) {
list[i] = list_[i];
}
numList = numList_;
}
main.cpp
//declaring 2 objects of Class Color
Color warm_color;
Char warmList = {red, pink, orange, yellow, brown};
Color cold_color;
Char coldList = {“green”, “blue”, “navy”};
warm_color = Color(warmList, 5);
cold_color = Color(coldList, 3);
You're trying to store a variable length list of strings of variable length. C++ offers very useful objects for this in the standard library.
std::vector: a variable length list, properly managed.
std::string: a variable length list of characters, all properly managed.
That way your code can be reduced to a simple:
#include <vector>
#include <string>
class Colors {
private:
std::vector<std::string> colorList;
public:
Colors(std::vector<std::string> const& colorList) : colorList(colorList) {}
};
int main() {
Colors warm_colors({ "red", "pink", "orange", "yellow", "brown" });
Colors cold_colors({ "green", "blue", "navy" });
}
Actually, in this case the constructor can be replaced by a move constructor:
Colors(std::vector<std::string>&& colorList) : colorList(std::move(colorList)) {}
... but that maybe too advanced for you yet.
So looking at your code you have some errors. Take the Menu constructor
Menu::Menu(const char* menuTitle, const char* items[], int numItems) {
m_menuTitle = new char[strlen(menuTitle) + 1];
strcpy_s(m_menuTitle, strlen(menuTitle) + 1, menuTitle);
for (int i = 0; i < numItems; i++) {
m_items[i] = &items[i];
}
m_numItems = numItems;
}
You have correctly handled the menu title, now you should use the same ideas on the menu items. That is you allocate each menu item with new, and then you copy each menu item with strcpy_s. Like this
Menu::Menu(const char* menuTitle, const char* items[], int numItems) {
m_menuTitle = new char[strlen(menuTitle) + 1];
strcpy_s(m_menuTitle, strlen(menuTitle) + 1, menuTitle);
for (int i = 0; i < numItems; i++) {
m_items[i] = new char[strlen(items[i]) + 1];
strcpy_s(m_items[i], strlen(items[i]) + 1, menuTitle);
}
m_numItems = numItems;
}
See? It's really no different to what you had already, except that m_items[i] replaces m_menuTitle.
Related
I'm doing an Arduino project and I need to pass arrays with different sizes as parameter to my function.
The problem is that std::vector is not an option.
How can I do that?
The fallback is to pass a pointer to the first element in the array and the size:
void foo(int* arr, size_t size);
The reason for std::vector not being available on some platforms is that on some platforms dynamic allocations is a bad idea. However, once you are dynamically allocating arrays:
int* x = new int[42];
foo(arr,42); // array decays to pointer
delete[] x;
then you could as well use std::vector.
If std::vector is not available to you, then either search for an alternative (maybe this?) or write your own. The pointer + size approach is fragile and not recommended unless absolutely necessary. The power of std::vector is from the abstract concept to encapsulate the array, its size and capacity. Nobody can prevent you to apply that concept even if you cannot use std::vector.
In case you are talking about statically sized arrays, then thats not quite the use case for std::vector. You do not need dynamic allocation, and you can pass arrays by reference. I won't repeat here what you can find in this answer (std::array) or here (c-arrays).
Something like this should work
template<size_t N>
void DaFunction(std::array<int, N>& daArray)
you can do it without having to deal with memory allocation or pointers just by creating a string variable and a limited size array and then you start shifting
#include <Arduino.h>
class ArrayShifter
{
private:
// String Reservoire Tank
String _text;
// a fixed size array of 5 in my case (depending on the amount of data you expect)
String _viewPortArray[5];
int _size = 0;
// Methode to fill the array
bool shiftArray(int position);
public:
ArrayShifter(/* args */);
// Method that gets the text from Serial
String getSerialText();
// get data from the array
String getArrayData(int index);
// array size getter
int getSize();
//clear the array
void clearArray();
//remove item
void removeArrayItem(int index);
};
ArrayShifter::ArrayShifter(/* args */)
{
}
String ArrayShifter::getSerialText()
{
// lesteing to the serial and returning the value
_text = Serial.readString();
return _text;
}
bool ArrayShifter::shiftArray(int position)
{
/*Assuming that the data is comming separated with ";" for each row and ":" for each value
to optimize the size of array in this way :
name:value;age:value;gender:value;
*/
String text = getSerialText();
int index = 0;
_size = 0;
if (text.length() > 0) // text isn't empty
{
if (position <= 5) // if the data belongs to the first 5 range
{
for (int i = 0; i < 5; i++)
{
// get the index of our separator that we've chosed to be ";"
index = text.indexOf(";");
if (index > 0)
{
// index found
_size++;
// putting the value before ";" in the array
_viewPortArray[i] = text.substring(0, index);
// deleting the value from the tank
text = text.substring(index + 1);
}
}
}
else
{
_size = 0;
// to wich range the desired index belongs
unsigned int dataRange = ((position - position % 5));
int ghostIndex = 0;
// looping throught all ";" to get indexes
for (int i = 0; i < dataRange; i++)
{
ghostIndex = text.indexOf(";");
if (ghostIndex > 0)
{
_size++;
text = text.substring(ghostIndex + 1);
}
}
// grabing just 5 of the data
for (int i = 0; i < 5; i++)
{
if (ghostIndex > 0)
{
_size++;
_viewPortArray[i] = text.substring(0, ghostIndex);
text = text.substring(ghostIndex + 1);
}
// updating ghost index
ghostIndex = text.indexOf(';');
}
}
return true;
}
return false;
}
String ArrayShifter::getArrayData(int index)
{
// turn the roulette
if (shiftArray(index))
{
if (index <= 5)
{
// yes we have this
return _viewPortArray[index];
}
else
{
// but we have to put it in the range of 5
index = index - 5;
return _viewPortArray[index];
}
}
}
int ArrayShifter::getSize()
{
return _size;
}
void ArrayShifter::clearArray()
{
for(int i = 0 ; i <5 ; i ++)
{
_viewPortArray->remove(i);
_size = 0;
}
}
void ArrayShifter::removeArrayItem(int index)
{
_viewPortArray->remove(index);
_size--;
}
main class :
#include <Arduino.h>
#include <ArrayShifter.h>
ArrayShifter array;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while (!Serial){}
}
void loop() {
if(Serial.available()>0)
{
Serial.println(array.getArrayData(7));
int sizeOption2 = array.getSize();
Serial.println(sizeOption2);
array.removeArrayItem(7);
Serial.println(array.getArrayData(7));
}
}
please check my github repository
https://github.com/Riadam/ViewPort-Array-Shifter-for-Arduino-Uno.git
I'm using a C++ engine called libtcod to print out ASCII characters in a rogue-like game.
In the game I want to represent the 'map' as a 2D array of type Map, which contains 'layer1' for tiles, e.g Grass, Sand, Water, and a second layer to represent objects on top of layer2, e.g Trees and Boulders.
I have created the Map class, which contains two attributes; one of type Tile, and the other of type Object.
However I seem to be having difficulties in the creation of the 2D array itself.
I am getting the error: "an array may not have elements of this type"
#include "libtcod.hpp"
#include <ctime>
#include <stdio.h>
#include <ciso646>
#include <cstring>
static const TCODColor colour_darkWall(128, 128, 128);
static const TCODColor colour_grass(0, 255, 0);
static const TCODColor colour_black(0, 0, 0);
static const TCODColor colour_white(255, 255, 255);
static const TCODColor colour_blue(0, 0, 255);
static const TCODColor colour_brown(139, 69, 19);
static const TCODColor colour_red(255, 0, 0);
int i, j;
class object {
public:
char name[20];
TCODColor colour;
char symbol;
bool passable;
object(int layer_id) {
if (layer_id == 0) { //EMPTY
strcpy(name, "NA");
symbol = ' ';
passable = true;
colour = colour_white;
}
else if (layer_id == 1) { //TREE
strcpy(name, "Tree");
symbol = '^';
passable = false;
colour = colour_grass;
}
else if (layer_id == 2) { //ROCK
strcpy(name, "Rock");
symbol = 'R';
passable = false;
colour = colour_black;
}
}
};
class tile {
public:
char name[20];
TCODColor colour;
bool passable;
tile(int layer_id) {
if (layer_id == 0) { //WATER
strcpy(name, "Water");
colour = colour_blue;
passable = false;
}
else if (layer_id == 1) { //GRASS
strcpy(name, "Grass");
colour = colour_grass;
passable = true;
}
else if (layer_id == 2) { //SAND
strcpy(name, "Sand");
colour = colour_brown;
passable = true;
}
}
};
class Map {
public:
tile layer1;
object layer2;
Map(int layer1_id, int layer2_id) {
layer1 = tile(layer1_id);
layer2 = object(layer2_id);
}
};
int main() {
const int window_x = 150;
const int window_y = 50;
Map[][] map = new Map[5][5];
TCODConsole::initRoot(window_x, window_y, "CivSim v0.1", false);
while (!TCODConsole::isWindowClosed()) {
TCODConsole::root->clear();
TCODConsole::root->putChar(0, 0, map.layer2.symbol);
TCODConsole::flush();
}
return 0;
}
Not sure how to create the 2D array in question.
Thanks in advance for any help!
Map[][] map = new Map[5][5]; is not legal. Only the first dimension can be omitted. You need Map[][5] map = new Map[5][5]; or better still Map (*map)[5] = new Map[5][5];.
The reason is that these notations using [] in a type are just another (and confusing) way of describing a pointer type. So int[] x is just a different way of writing int* x and Map[][5] m is just another way of writing Map (*m)[5] (which is to say a pointer to an array of five Maps).
I think you should always use the pointer version of these notations, it's more correctly describes what you are really doing. In the long term writing code that looks like you have an array, when all you actually have is a pointer is confusing.
So why isn't Map[][] another way of writing Map**? It's because the similarity between arrays and pointer in C and C++ only works for one level. A two dimensional array is a fundamentally different structure from a pointer to an array of pointers. So int[5][5] isn't compatible with int** so there is no reason to make int[][] mean the same as int**.
I'm new with using classes and I encountered a problem while delcaring an array into a class. I want to initialize a char array for text limited to 50 characters and then replace the text with a function.
#ifndef MAP_H
#define MAP_H
#include "Sprite.h"
#include <SFML/Graphics.hpp>
#include <iostream>
class Map : public sprite
{
private:
char mapname[50];
int columnnumber;
int linenumber;
char casestatematricia[];
public:
void setmapname(char newmapname[50]);
void battlespace(int column, int line);
void setcasevalue(int col, int line, char value);
void printcasematricia();
};
#endif
By the way I could initialize my 2d array like that
char casestatematricia[][];
I want later to make this 2d array dynamic where I enter a column number and a line number like that
casestatematricia[linenumber][columnnumber]
to create a battlefield.
this is the cpp code so that you have an idea of what I want to do.
#include "Map.h"
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace sf;
void Map::setmapname(char newmapname[50])
{
this->mapname = newmapname;
}
void Map::battlespace(int column, int line)
{
}
void Map::setcasevalue(int col, int line, char value)
{
}
void Map::printcasematricia()
{
}
thank you in advance.
Consider following common practice on this one.
Most (e.g. numerical) libraries don't use 2D arrays inside classes.
They use dynamically allocated 1D arrays and overload the () or [] operator to access the right elements in a 2D-like fashion.
So on the outside you never can tell that you're actually dealing with consecutive storage, it looks like a 2D array.
In this way arrays are easier to resize, more efficient to store, transpose and reshape.
Just a proposition for your problem:
class Map : public sprite
{
private:
std::string mapname;
int columnnumber;
int linenumber;
std::vector<char> casestatematricia;
static constexpr std::size_t maxRow = 50;
static constexpr std::size_t maxCol = 50;
public:
Map():
casestatematricia(maxRow * maxCol, 0)
{}
void setmapname(std::string newmapname)
{
if (newmapname.size() > 50)
{
// Manage error if you really need no more 50 characters..
// Or just troncate when you serialize!
}
mapname = newmapname;
}
void battlespace(int col, int row);
void setcasevalue(int col, int row, char value)
{
// check that col and line are between 0 and max{Row|Column} - 1
casestatematricia[row * maxRow + col] = value;
}
void printcasematricia()
{
for (std::size_t row = 0; row < maxRow; ++row)
{
for (std::size_t col = 0; col < maxCol; ++col)
{
char currentCell = casestatematricia[row * maxRow + col];
}
}
}
};
For access to 1D array like a 2D array, take a look at Access a 1D array as a 2D array in C++.
When you think about serialization, I guess you want to save it to a file. Just a advice: don't store raw memory to a file just to "save" time when your relaunch your soft. You just have a non portable solution! And seriously, with power of your computer, you don't have to be worry about time to load from file!
I propose you to add 2 methods in your class to save Map into file
void dump(std::ostream &os)
{
os << mapname << "\n";
std::size_t currentRow = 0;
for(auto c: casestatematricia)
{
os << static_cast<int>(c) << " ";
++currentRow;
if (currentRow >= maxRow)
{
currentRow = 0;
os << "\n";
}
}
}
void load(std::istream &is)
{
std::string line;
std::getline(is, line);
mapname = line;
std::size_t current_cell = 0;
while(std::getline(is, line))
{
std::istringstream is(line);
while(!is.eof())
{
char c;
is >> c;
casestatematricia[current_cell] = c;
++current_cell;
}
}
}
This solution is only given for example. They doesn't manage error and I have choose to store it in ASCII in file. You can change to store in binary, but, don't use direct write of raw memory. You can take a look at C - serialization techniques (just have to translate to C++). But please, don't use memcpy or similar technique to serialize
I hope I get this right. You have two questions. You want know how to assign the value of char mapname[50]; via void setmapname(char newmapname[50]);. And you want to know how to create a dynamic size 2D array.
I hope you are comfortable with pointers because in both cases, you need it.
For the first question, I would like to first correct your understanding of void setmapname(char newmapname[50]);. C++ functions do not take in array. It take in the pointer to the array. So it is as good as writing void setmapname(char *newmapname);. For better understanding, go to Passing Arrays to Function in C++
With that, I am going to change the function to read in the length of the new map name. And to assign mapname, just use a loop to copy each of the char.
void setmapname(char *newmapname, int length) {
// ensure that the string passing in is not
// more that what mapname can hold.
length = length < 50 ? length : 50;
// loop each value and assign one by one.
for(int i = 0; i < length; ++i) {
mapname[i] = newmapname[i];
}
}
For the second question, you can use vector like what was proposed by Garf365 need to use but I prefer to just use pointer and I will use 1D array to represent 2d battlefield. (You can read the link Garf365 provide).
// Declare like this
char *casestatematricia; // remember to initialize this to 0.
// Create the battlefield
void Map::battlespace(int column, int line) {
columnnumber = column;
linenumber = line;
// Clear the previous battlefield.
clearspace();
// Creating the battlefield
casestatematricia = new char[column * line];
// initialise casestatematricia...
}
// Call this after you done using the battlefield
void Map::clearspace() {
if (!casestatematricia) return;
delete [] casestatematricia;
casestatematricia = 0;
}
Just remember to call clearspace() when you are no longer using it.
Just for your benefit, this is how you create a dynamic size 2D array
// Declare like this
char **casestatematricia; // remember to initialize this to 0.
// Create the battlefield
void Map::battlespace(int column, int line) {
columnnumber = column;
linenumber = line;
// Clear the previous battlefield.
clearspace();
// Creating the battlefield
casestatematricia = new char*[column];
for (int i = 0; i < column; ++i) {
casestatematricia[i] = new char[line];
}
// initialise casestatematricia...
}
// Call this after you done using the battlefield
void Map::clearspace() {
if (!casestatematricia) return;
for(int i = 0; i < columnnumber; ++i) {
delete [] casestatematricia[i];
}
delete [][] casestatematricia;
casestatematricia = 0;
}
Hope this help.
PS: If you need to serialize the string, you can to use pascal string format so that you can support string with variable length. e.g. "11hello world", or "3foo".
In short, I have a C++ program that consists of a Car class with subclasses SportsCar and SUV, as well as a CarInventory that stores the Car objects.
I think the problem is in the insert function
void CarInventory::insert(Car *car) {
for (int i = 0; i < inventorySize; i++) {
if (carArray[i]) {
i++;
}
When you find a car already in in your carArray you increment i, but then you increment i again in the for-loop. That way you will skip every other position in the array, and they will remain uninitialized.
You allocated an array of 10 cars. But you only initialized it with 4 cars.
So when your loop in displayVehichles gets around to accessing carArray[5] it will likely be accessing uninitialized memory.
This statement seems troublesome:
carArray = new Car *[maxStock];
It should probably be:
carArray = new Car *[maxStock];
for (int i = 0; i < maxStock; i++)
{
carArray[i] = NULL;
}
totalNumCars = 0;
That way, your insert method will behave correctly. But your insert method could be much simpler:
void CarInventory::insert(Car *car) {
if (totalNumCars < maxStock)
carArray[totalNumCars] = car;
totalNumCars++;
}
}
Further, while it's OK the way you have it the cars you have declared go out of scope BEFORE your CarInventory goes out of scope, it will be referencing Car objects that have already been deleted.
Use std::vector to hold your cars:
class CarInventory
{
std::vector<Car*> _cars;
public:
void displayVehicles()
{
for (auto i = _cars.begin(); i != _cars.end(); i++)
{
i->printInfo();
}
}
void insert(Car* car)
{
_cars.push_back(car);
}
};
Now that still doesn't solve the problem of your CarInventory class holding onto pointers of stack objects.
This is even better:
class CarInventory
{
std::vector<std::shared_ptr<Car>> _cars;
public:
void displayVehicles()
{
for (auto i = _cars.begin(); i != _cars.end(); i++)
{
i->printInfo();
}
}
void insert(std::shared_ptr<Car>& spCar)
{
_cars.push_back(spCar);
}
};
Then your code to use the class:
std::shared_ptr<Car*> createCar(const char* vin, const char* make, const char* color, int year)
{
Car* car = new Car(vin, make, color, year);
return std::shared_ptr<Car>(car);
}
int main(int argn, char *argv[])
{
CarInventory cars;
std::shared_ptr<Car*> toyota = createCar("2GCGC34M9F1152828", "Toyota", "Camry", "Green", 2012);
std::shared_ptr<Car*> honda = createCar("1C4BJWAG4DL602733", "Honda", "Civic", "Blue", 2015);
...
CarInventory cars;
cars.insert(toyota);
cars.insert(honda);
cout << "\n";
cars.displayVehicles();
}
I am trying to write this C++ function in which I am trying to set each Sequence in the array of Sequences, however when I follow the code on debug I notice that the array is not changing. In particular:
compressed.data[compressedDataCounter].c = pic.data[i];
compressed.data[compressedDataCounter].times = counter+1;
don't seem to add any new variables to the array, just override the first one.
I am thinking that the root of the problem is the declaration:
CompressedPic compressed;
compressed.data = new Sequence[pic.height * pic.width];
This is the portion of the code:
struct Sequence
{
char c;
int times;
};
struct CompressedPic
{
int height;
int width;
Sequence* data;
};
struct Picture
{
int height;
int width;
char* data;
};
CompressedPic compressThePicture(Picture pic) {
CompressedPic compressed;
compressed.data = new Sequence[pic.height * pic.width];
compressed.height = pic.height;
compressed.width = pic.width;
int compressedDataCounter=0;
for(int i=0; i<(pic.height * pic.width)-1; i++)
{
int counter = 0;
while(pic.data[i] == pic.data[i+1])
{
i++;
counter++;
}
compressed.data[compressedDataCounter].c = pic.data[i];
compressed.data[compressedDataCounter].times = counter+1;
compressedDataCounter++;
}
compressed.data[compressedDataCounter].times = -1;
return compressed;
}
It would be great if someone could figure out why this is happening.
You might want to change:
compressed.data[compressedDataCounter].c = counter+1;
to:
compressed.data[compressedDataCounter].times = counter+1;
So you can change the .times member otherwise you will be overriding your .c member. Right now you are setting .c to 'a' for example. Then you set .c to 103 (counter+1). Which is an int and likely with your archetecture the high bytes are aligning with .c and setting it to 0 as well.
So .c is getting 0'd and .times is never set