How to initialize a constant int to use for array size? - c++

I have a static integer variable Game::numPlayers, which is read in as an input from user. I then have the following class defined as such:
class GridNode
{
private:
/* Members */
static const int nPlayers = Game::numPlayers;
std::vector<Unit> units[nPlayers];
//...
};
This won't compile ("in-class initializer for static data member is not a constant expression").
I obviously cant just assign the array size of Game::numPlayers, and I also tried not initializing it and letting a constructor do the work, but that didn't work either.
I don't understand what I'm doing wrong here and what else I could possibly do to get this to work as intended.
I'm just copying a value, how is that any different from static const int nPlayers = 8 which copies the value 8 into nPlayers and works?
Edit:
To clarify, I choose to have an array of vectors because I want each node to have a quick-access container of units, but one container for each user/player so as to distinguish which units belong to which player within each node (e.g. index 0 of the array = player 1, index 1 = player 2, index 2 = player 3, and so on), otherwise I would just have one vector or a vector of vectors. I thought a map might work, but I thought an array of vectors would be faster to access and push into.
Also, Game::numPlayers is read in as a user input, but only read and assigned once within one game loop, but if I close/restart/play a new game, it needs to read in the user input again and assign it once.

I don't see why you need to use an array of std::vector if the number of elements will be obtained at runtime.
Instead, create a std::vector<std::vector<Units>> and size it appropriately on construction. if you need to reset the size, have a member function resize the vector.
Example:
class GridNode
{
private:
/* Members */
std::vector<std::vector<Unit>> units;
public:
GridNode(int nPlayers=10) : units(nPlayers) {}
std::vector<Unit>& get_units(size_t player)
{ return units[player]; } // gets the units for player n
void set_num_players(int nPlayers)
{ units.resize(nPlayers); } // set the number of players
int get_num_players() const { return units.size(); }
};
int main()
{
int numPlayers;
cin >> numPlayers;
//...
GridNode myGrid(numPlayers); // creates a GridNode with
// numPlayers vectors.
//...
Unit myUnit;
myGrid.get_units(0).push_back(myUnit); // places a Unit in the
// first player
}
Also, it isn't a good idea to have extraneous variables tell you the vector's size. The vector knows its own size already by calling the vector::size() function. Carrying around unnecessary variables that supposedly gives you this information opens yourself up for bugs.

Only integral constant expressions are allowed as array sizes in array declarations in C++. A const int object initailized with something that is not an integral constant expression (your Game::numPlayers is not, since it is read from the user), does not itself qualify as integral constant expression.
The bottom line here is that regardless of how you slice it, it is not possible to sneak in a run-time value into an array declaration in C++. C++11 does support some semblance of C99-style Variable Length Arrays, but your case (a member array) is not covered by it anyway.
If the array size is a run-tuime value, use std::vector. In your case that would become std::vector of std::vectors.

Related

C++ Pointer not being updated?

Not exactly sure how to word the title but I'll explain as best I can.
I have a program that originally used a 2D array of a set size and so it was defined as:
typedef char Map[Row][Col];
I'm now trying to dynamically allocate memory for it and it has now also become of variable size based on input. It's now defined as:
typedef char** Map;
In my main method, I originally had:
Map map;
readUserInput(map);
Basically readUserInput takes the map array as a parameter, and assigns values to it based on user input. The map then contains values and is used in other functions.
I've updated the readUserInput function so that it dynamically sizes the array and it allocates/deallocates memory for it. This works fine, but the problem comes from the fact that now in the main method, map is not being updated. The above code in main now looks like:
Map map = nullptr;
readUserInput(map);
but after running the readUserInput function, map is still null. Inside of the function, map is updated fine, so I'm not understanding the difference made between the changes.
What you pass to function is a pointer to array and fuction can't change it. But replacing array with pointer to pointer is incorrect in most case.Pointer to pointer suggest that have a 1D array of pointers. Which may (or may not) point to other arrays. Such data organization sometimes referred to as jagged arrays, because it allows each row to be of separate length. But on practtice jagged arrays and their subclass, sparse matrices, usually implemented as 1D array to avoid re-allocation.
To avoid decaying and to actually store a monolithic array in memory, you should use 1d array and, preferably, encapsulation for pointer arithmetic and reallocation, and then pass reference to object that stores all required states. Reference ensures that object is mutable by function ( a smart-pointer-less version for an example):
class Map
{
int rows, cols;
char *data;
public:
Map() : rows(), cols(), data(nullptr) {}
Map(int r, int c) : rows(r), cols(c), data(new char[r*c]()) {}
~Map() { delete[] data; }
void resize(int r, int c) {
if(rows == r && cols == c) return;
char* tmp = new char[r*c]();
if(data)
{
// copy old data here if required
delete[] data;
}
row = r; col = c;
data = tmp;
}
char& operator() (int r, int c) { return data[r*cols + c]; }
char operator() (int r, int c) const { return data[r*cols + c]; }
};
NB: this class requires a copy and move operations to be implemented if any copy must be allowed.
The function prototype would be:
void readUserInput(Map& map);
With such class you can do dynamic resizing, store its size, and address element as simple as this:
int main()
{
Map test(4, 5); // declaring and allocating memory
test.resize(3,3); // reallocating
test(1,1) = 3; // writing
//reading
std::cout << +test(1,1) << std::endl;
}
The function should accept the array by reference in the C terms like
readUserInput( &map );
when the function is declared like
void readUserInput( Map *map );
or in the C++ terms when the function is declared like for example
void readUserInput( Map &map );
and called like
readUserInput(map);
Instead of allocating dynamically arrays you could use the container std::vector<std::string>.
The code you have used is a pure C-style code, and is prone to many mistakes:
You use typedef instead of: using Map = char**;
You use a function which gets a pointer and fills it, which is more common in C than in C++.
You use raw pointer instead of smart pointers (added in C++11), which may cause a memory leak in the end.
I've updated the readUserInput function so that it dynamically sizes the array and it allocates/deallocates memory for it.
This means that now it should be a class named Map, since it should be able to allocate/deallocate, insert and remove values, and is a valid container. Actually, you are creating a type of std::vector here, and if you don't create it for you own learning process, I strongly suggest you to use the std containers!
It is possible to pass both pointer and references in C++, notice that:
You can pass a reference only if the value isn't nullptr.
When there should be a value, reference is recommended.
In this case, your function should look like
void readUserInput(Map* map);
and should be called using:
readUserInput(&map);

Why is the dynamically allocated array attribute of my class template only able to store one item?

I am trying to expand the functionality of a class template I created. Previously it allowed you to use key-value pairs of any type but only if you knew the size of the arrays at compile time. It looked like this:
template <typename K, typename V, int N>
class KVList {
size_t arraySize;
size_t numberOfElements;
K keys[N];
V values[N];
public:
KVList() : arraySize(N), numberOfElements(0) { }
// More member functions
}
I wanted to be able to use this for a dynamic number of elements decided at run-time, so I changed the code to this:
template <typename K, typename V>
class KVList {
size_t arraySize;
size_t numberOfElements;
K* keys;
V* values;
public:
KVList(size_t size) : numberOfElements(0) {
arraySize = size;
keys = new K[size];
values = new V[size];
}
~KVList() {
delete[] keys;
keys = nullptr;
delete[] values;
values = nullptr;
}
// More member functions
}
The new constructor has one parameter which is the size that will be used for the KVList. It still starts the numberOfElements at 0 because both of these uses would start the KVList empty, but it does set arraySize to the value of the size parameter. Then it dynamically allocated memory for the arrays of keys and values. An added destructor deallocates the memory for these arrays and then sets them to nullptr.
This compiles and runs, but it only stores the first key and first value I try to add to it. There is a member function in both that adds a key-value pair to the arrays. I tested this with the Visual Studio 2015 debugger and noticed it storing the first key-value pair fine, and then it attempts to store the next key-value pair in the next index, but the data goes no where. And the debugger only shows one slot in each array. When I attempt to cout the data I thought I stored at that second index, I get a very small number (float data type was trying to be stored), not the data I was trying to store.
I understand it might be worth using the vectors to accomplish this. However, this is an expansion on an assignment I completed in my C++ class in school and my goal with doing this was to try to get it done, and understand what might cause issues doing it this way, since this is the obvious way to me with the knowledge I have so far.
EDIT: Code used to add a key-value pair:
// Adds a new element to the list if room exists and returns a reference to the current object, does nothing if no room exists
KVList& add(const K& key, const V& value) {
if (numberOfElements < arraySize) {
keys[numberOfElements] = key;
values[numberOfElements] = value;
numberOfElements++;
}
return *this;
}
EDIT: Code that calls add():
// Temp strings for parts of a grade record
string studentNumber, grade;
// Get each part of the grade record
getline(fin, studentNumber, subGradeDelim); // subGradeDelim is a char whose value is ' '
getline(fin, grade, gradeDelim); // gradeDelim is a char whose value is '\n'
// Attempt to parse and store the data from the temp strings
try {
data.add(stoi(studentNumber), stof(grade)); // data is a KVList<size_t, float> attribute
}
catch (...) {
// Temporary safeguard, will implement throwing later
data.add(0u, -1);
}
Code used to test displaying the info:
void Grades::displayGrades(ostream& os) const {
// Just doing first two as test
os << data.value(0) << std::endl;
os << data.value(1);
}
Code in main cpp file used for testing:
Grades grades("w6.dat");
grades.displayGrades(cout);
Contents of w6.dat:
1022342 67.4
1024567 73.5
2031456 79.3
6032144 53.5
1053250 92.1
3026721 86.5
7420134 62.3
9762314 58.7
6521045 34.6
Output:
67.4
-1.9984e+18
The problem (or at least one of them) is with this line from your pastebin:
data = KVList<size_t, float>(records);
This seemingly innocent line is doing a lot. Because data already exists, being default constructed the instance that you entered the body of the Grades constructor, this will do three things:
It will construct a KVList on the right hand side, using its constructor.
It will call the copy assignment operator and assign what we constructed in step 1 to data.
The object on the right hand side gets destructed.
You may be thinking: what copy assignment operator, I never wrote one. Well, the compiler generates it for you automatically. Actually, in C++11, generating a copy assignment operator automatically with an explicit destructor (as you have) is deprecated; but it's still there.
The problem is that the compiler generated copy assignment operator does not work well for you. All your member variables are trivial types: integers and pointers. So they just copied over. This means that after step 2, the class has just been copied over in the most obvious way. That, in turn, means that for a brief instance, there is an object on the left and right, that both have pointers pointing to the same place in memory. When step 3 fires, the right hand object actually goes ahead and deletes the memory. So data is left with pointers pointing to random junk memory. Writing to this random memory is undefined behavior, so your program may do (not necessarily deterministic) strange things.
There are (to be honest) many issues with how your explicit resource managing class is written, too many to be covered here. I think that in Accelerated C+, a really excellent book, it will walk you through these issues, and there is an entire chapter covering every single detail of how to properly write such a class.

Nested Struct and Array C++

Hello I am dealing with nested structs and arrays in C++, here is some background info:
struct Cells // a collection of data cells lines
cells :: [Cell] // the cells: a location and a value
nCells :: Integer // number of cells in the array
capacity :: Integer // maximum size of the array end
struct Cell
location :: int // a location of data cells lines
value :: int // the value end Cells
The code I have which won't compile (3 files, header, ADT implementation, main)
How am I declaring the nested struct in struct array wrong?
// Defines the cell.h ADT interface
struct Cell;
struct Cells;
struct Cells {
Cell cells[];
int nCells;
int capacity;
};
struct Cell {
int location;
int value;
};
//fill cells with random numbers
void initialize(Cells *rcells);
ADT Implementation
using namespace std;
#include <iostream>
#include <cstdlib>
#include "cell.h"
void initialize(Cells *rcells){
for(int i = 0 ; i < rcells->nCells; i++)
{
rcells->cells[i].location = rand() % 100;
rcells->cells[i].value = rand() % 1000;
}
}
main
using namespace std;
#include <iostream>
#include <cstdlib>
#include "cell.h"
int main(){
Cells *c;
c->cells[0].location=0;
c->cells[0].value=0;
c->cells[1].location=0;
c->cells[1].value=0;
c->nCells = 2;
c->capacity = 2;
initialize(c);
}
Your original declaration fails because in
struct Cells {
Cell cells[];
int nCells;
int capacity;
};
"cells" defined in this way is an array, which should have fixed size (unless it is the last member and you're using C99 standard). You may think it is the same as
Cell* cells
but it won't be converted to pointer type automatically in struct definition.
The C++ way to do such things is
typedef std::vector<Cell> Cells;
Your initialize function could be
void initialize(int ncell, Cells& cells) {
cells.resize(ncell);
for (Cell& cell : cells)
{
cell.location = rand() % 100;
cell.value = rand() % 1000;
}
}
Your main program should change a little bit
int main(){
Cells c;
initialize(2, c);
c[0].location=0;
c[0].value=0;
c[1].location=0;
c[1].value=0;
}
If you want cell count information, you can call
c.size()
There is no need for capacity variable because there is no upper limit on total number of cells.
By the way, this is not the nested struct people usually talk about. When one say nested struct, he often means nested struct definition. There is nothing special about an object containing other objects.
Unlike some other programming languages, when you declare an array in C or C++, it is created where you declare it. For example if you declare one as a function local variable, it will be created on the stack.
In you case Cell cells[]; declares an array that must be created within your class. Thus if you class have an array of four elements, the compiler needs to allocate to each instance 4*sizeof(Cell) bytes for the field so it can fit the array in the instance. If your array have 524 elements, it needs 524*sizeof(Cell) bytes.
You see the problem here : the compiler cannot guess what is the size of your object. Which is problematic to obtain the location of each field in an instance, especially if you declare two array without size. Note that this issue is not restricted to object fields : you cannot declare an array as a local variable in a function without giving a size either, for example. This because array have a fixed size determined upon creation. Thus wherever you create the array, you must provide its size.
When you write Cell array[] as a function argument, you are not creating an array, but only obtaining a pointer to it, so not giving its size is okay.
To solve your issue you must somehow make classes with a constant size. For example you can allocate you array dynamically with new[some_variable] and use pointer Cell *cells; in your class. A pointer has a fixed size, and you array is to be declared on the heap (don't forget to delete[] it).
Remark: instead of a size, giving an array initializer only is valid :
int x[] = {1, 2, 4}; //creates an array of three elements

C++ Class Variables: Initialization vs. Assignment and Initialization of vectors

I am working on a C++ program that has a series of class variables that contain vectors on some or all of the member variables. My question is three-fold:
Is it straight-forward to use constructors to initialize vector variables that are part of a class (see sample class definition below)? Could someone post an example constructor for the class below (or for at least the single and two-dimension vector variables)?
Is there a problem with simply initializing the variables myself in my code (i.e., iterating through each element of the vectors using loops to assign an initial value)?
Along the same lines, if the variables need to be initialized to different values in different contexts (e.g., zero in one instance, some number in another instance), is there a way to handle that through constructors?
Sample class definition:
class CreditBasedPoolLevel {
public:
int NumofLoans;
int NumofPaths;
int NumofPeriods;
double TotalPoolBal;
vector<int> NumofModeled;
vector<double> ForbearanceAmt;
vector<double> TotalAmtModeled;
vector<vector<int>> DefCountPoolVector;
vector<vector<double>> TermDefBalPoolVector;
vector<vector<double>> BalloonDefBalPoolVector;
vector<vector<double>> TermDefBalPoolVectorCum;
vector<vector<double>> TermSeverityAmt;
vector<vector<double>> TermELAmtPoolVector;
vector<vector<double>> BalloonELAmtPoolVector;
vector<vector<double>> TermELAmtPoolVectorCum;
};
In C++, initializing a variable calls its constructor. In a vector's case, this means it creates an instance of a vector with whatever the initial capacity is (10 I believe), with no values. At this point, you need to use push_back in order to fill the vector - even though it has a capacity, it will cause undefined behavior if you try to access unfilled areas directly (such as with NumofModeled[0]). You can also initialize it with some amount of space by using vector NumofModeled(x) (x being the number of spaces), but generally because vectors have dynamic size, it's easier to use push_back unless there is some reason you need to enter your data out of order.
Relates to the capacity part of one, if you try to access unfilled space in a vector you will get undefined behavior. It's pretty standard practice to fill a vector with a loop though, such as:
vector<int> v;
int in = 0;
while (cin)
{
cin >> in;
v.push_back(in);
}
Yes, but remember that like functions, constructors only differentiate by the type of input parameters. So, for example, you could have CreditBasedPoolLevel(int level) and CreditBasedPoolLevel(vector<int> levels), but not another with the definition CreditBasedPoolLevel(int otherint), because it would conflict with the first. If you want to be able to take different contextual input of the same type, you can use another variable to define the constructor type, such as CreditBasedPoolLevel(int input, string type) and use a switch block to define the initialization logic based on the type.
As for question number three, simply add a constructor with an argument that is the value you want to initialize the vectors with.
And if you just want the vectors to be default constructed, then there's nothing that needs to be done.
Constructor may look something like this:
CreditBasedPoolLevel::CreditBasedPoolLevel()
{
const int numDefCountPools = 13;
const int numDefCountPoolEntries = 25;
for(int i = 0; i < numDefCountPools; i++)
{
vector<int> v;
for(int j = 0; j < numDefCountPoolEntries; j++)
{
v.push_back(j + i * 5); // Don't know what value you ACTUALLY want to fill here
}
DefCountPoolVector.push_back(v);
}
}
Note that this is ONE solution, it really depends on what values you want, how you went them organized, etc, what is the "right" solution for your case.

C++ - Define Vector size with class argument

Hello I'm trying to create a genetic algorithm with C++ and I tried to use vector as the container the problem is I didn't know how to set the size of the vector because the vector have a class argument like this
class population
{
friend class chromosome;
private:
int population_number;
int best_index[2];
vector <chromosome *> chromosome_population;
public:
population(int numberOfPopulation);
population(int numberOfPopulation,int numberOfSynapse);
~population();
int worst_chromosome();
void mating();
void crossover(int parent_1,int parent_2);
};
this is the population class and here's the chromosome class
class chromosome
{
friend class population;
private:
int chromosome_id;
float fitness;
vector <gen *> gen_chromosome;
public:
chromosome();
~chromosome();
void fitness_function();
void mutation_translocation();
int get_chromosome_size();
};
how can I set the vector length in the population class constructor? I've tried to use vector.pushback and vector.resize but both will give me error because the argument doesn't match. Actually I'm understand why it become error but I didn't know how to match the argument inside the vector pushback here's my population constructor
population::population(int numberOfPopulation)
{
srand (time(NULL));
population_number = numberOfPopulation;
for(int a=0;a<population_number;a++)
{
chromosome_population.push_back();
}
cout<<chromosome_population.size();
for(int i=0;i<population_number;i++)
{
chromosome_population[i]->chromosome_id = i;
int chromosome_length = rand() % 10 + 1;
for(int j=0;j<chromosome_length;j++)
{
chromosome_population[i]->gen_chromosome[j]->basa_biner = rand()%1;
chromosome_population[i]->fitness = (rand()%99)+1;
}
}
}
If is there any other information you want you can tell me in the comment and I'll add the information you needed. Thanks before.
std::vector has several constructors and one of the variants accepts the initial number of elements to be stored in the vector.
Specify the size of the vector in the population constructor's initializer list:
population::population(int numberOfPopulation) :
population_number(numberOfPopulation),
chromosome_population(numberOfPopulation)
{
}
Give this approach, the population_number member variable is unnecessary as it can be obtained by chromosome_population.size().
Specifying an initial size on the vector will mean that it contains numberOfPopulation null pointers. Before accessing elements in the vector you need to create objects, in this case using new. If the elements are copyable and polymorphic behaviour is not required then recommend using vector<chromosome> instead. If you must use dynamically allocated elements in the vector then you must allocate first:
chromosome_population[i] = new chromosome();
and remember to delete when no longer required.
It also desirable to use a form of smart pointer instead of raw pointers. An advantage of using the smart pointer is that when the vector<unique_ptr<chromosome>> goes out of scope the elements will be destructed for you, without having to explicitly call delete on each of the elements. See What C++ Smart Pointer Implementations are available? for a useful listing of the available smart pointers.
Note that vector::push_back() accepts an argument, with same type as its element. So the correct invocation of push_back() is:
chromosome_population.push_back(new chromosome());
If you specify an initial size of the vector at construction, calling push_back() will add elements after the initial (null pointers in this case) elements in the vector.