c++ odd behaviour of an array of iterators - c++

So searching to see if someone has already asked this I see lots of questions about iterating over arrays. But what I've got is an array of iterators. Essentially here's what I'm up to:
I have a sorted std::list of a custom object. The object just contains an int and a double, and has methods for the sorts of things you would expect (constructors, setters, getters, operator< to make it sortable by the double, a toSting() method). That all works, including sorting.
Now I want a bunch of iterators that point into the list at different points. There will be one to the head of the list, one to the tail and several pointing into various points in the middle. I'm doing this using an old-style array (this may be the problem - I'll try it with a std::array, but I still want to understand why this hasn't worked). So I've got a subroutine that initializes this array. It almost works. I can build the array and output from within the subroutine and everything looks good. Outputting from outside the subroutine the last element of the array has changed and no longer appears to point into the list. Here's the relevant code:
using namespace std;
#include <iostream>
#include <list>
#include <cmath>
#include <algorithm>
#include "random.h"
#include "Double_list_struct.h"
/**********************************Subroutine declarations***************************/
template <typename Tplt>
void output_list(list<Tplt> to_out);
template <typename Tplt>
void initialize_list(list<Tplt> &alist, int size);
template <typename Tplt>
void initialize_iter_array(typename list<Tplt>::iterator* itar, int size, list<Tplt> alist);
/***************************************Main routine*******************************/
int main(void)
{
int list_size = 16;
// Make the list that will be tested.
list<Double_list_struct> list_to_play_with;
initialize_list(list_to_play_with, list_size);
list_to_play_with.sort();
cout << "Sorted list is: " << endl;
output_list(list_to_play_with);
// Make an array of list<Double_list_struct>::iterator of size floor(sqrt(N))
int iter_array_size = floor(sqrt(list_size));
list<Double_list_struct>::iterator* iter_array;
iter_array = new list<Double_list_struct>::iterator[iter_array_size];
// Initialize the iterators in iter_array to point to roughly evenly spaced locations in the list
initialize_iter_array(iter_array, iter_array_size, list_to_play_with);
for (int i = 0; i < iter_array_size; i++)
{
cout << "In main routine, iter_array[" << i << "]:" << (*(iter_array[i])).toString() << endl;
}
cout << "Reset it, and redo the output loop??" << endl;
iter_array[iter_array_size-1] = list_to_play_with.end();
iter_array[iter_array_size-1]--;
for (int i = 0; i < iter_array_size; i++)
{
cout << "In main routine, iter_array[" << i << "]:" << (*(iter_array[i])).toString() << endl;
}
}
/************************************************Subroutine code**************************************/
// Output all elements of a list to cout.
template <typename Tplt>
void output_list(list<Tplt> to_out)
{
...not important here
}
template <typename Tplt>
void initialize_list(list<Tplt> &alist, int size)
{
...not important here
}
template <typename Tplt>
void initialize_iter_array(typename list<Tplt>::iterator* itar, int size, list<Tplt> alist)
{
itar[0] = alist.begin();
itar[size-1] = alist.end();
itar[size-1]--; // Recall that .end() makes an iterator point *past* the end...
// Find out how big the list is
int listsize = 0;
for (typename list<Tplt>::iterator it = itar[0]; it != itar[size-1]; it++)
{
listsize = listsize + 1;
}
int spacing = floor(listsize/(size-1));
cout << "In initialize_iter_array(): created itar[0]: " << (*itar[0]).toString() << endl;
for (int i = 1; i < size-1 ; i++)
{
itar[i] = itar[i-1];
for (int j = 0; j < spacing; j++)
{
itar[i]++;
}
cout << "In initialize_iter_array(): created itar[" << i << "]: " << (*itar[i]).toString() << endl;
}
cout << "In initialize_iter_array(): created itar[" << size-1 << "]: " << (*itar[size-1]).toString() << endl;
}
This generates output
Sorted list is:
struct[15] = 0.135837
struct[1] = 0.200995
struct[12] = 0.217693
...SNIP...
struct[8] = 0.863816
struct[14] = 0.887851
struct[2] = 0.893622
struct[10] = 0.925875
In initialize_iter_array(): created itar[0]: struct[15] = 0.135837
In initialize_iter_array(): created itar[1]: struct[5] = 0.314127
In initialize_iter_array(): created itar[2]: struct[11] = 0.704419
In initialize_iter_array(): created itar[3]: struct[10] = 0.925875
In main routine, iter_array[0]:struct[15] = 0.135837
In main routine, iter_array[1]:struct[5] = 0.314127
In main routine, iter_array[2]:struct[11] = 0.704419
In main routine, iter_array[3]:struct[-1] = 6.21551e-71
Reset it, and redo the output loop??
In main routine, iter_array[0]:struct[15] = 0.135837
In main routine, iter_array[1]:struct[5] = 0.314127
In main routine, iter_array[2]:struct[11] = 0.704419
In main routine, iter_array[3]:struct[10] = 0.925875
So, you see, iter_array[3] is correct inside the initialization subroutine, but has "moved" after the subroutine exits. I then reset it from outside the subroutine, but obviously I'd like to not have to do that...
My best guess is that there is something subtle going on here with how the assignment operator works for iterators. But I'm very puzzled.

initialize_iter_array takes the list by value, which means it's putting iterators that point into the parameter copy of the list, not the original list. You probably meant to pass the list by const& instead.

Related

How can I add different names to a vector using classes?

#include <iostream>
#include <string>
#include <vector>
class Enemy
{
private:
std::string rank = "Boss";
std::string rank2 = "Miniboss";
public:
std::string type;
std::string get_rank(){
return rank;
}
std::string get_rank2(){
return rank2;
}
};
int add_enemy(std::vector<Enemy>&enemies, Enemy enemy) // I wanna pass by reference because I want to modify the vector
{
for(size_t i; i < enemies.size(); i++) {
if(enemies.at(i).type == enemy.type){ // here I'm saying, if I add an enemy that's of the same type, I don't wanna add it anymore
return 1; // it returns an error, because they are the same type, so it shouldn't add it?
}
}
enemies.push_back(enemy);
}
int main()
{
Enemy enemy;
enemy.type = "Dragon";
std::cout << enemy.type << " is a " << enemy.get_rank() << std::endl;
Enemy nrone, nrtwo, nrthree, nrfour, nrfive;
// I want to add these and keep them in a vector
std::vector<Enemy> enemies;
nrone.type = "Orc";
nrtwo.type = "Goblin";
nrthree.type = "Troll";
nrfour.type = "Ogre";
nrfive.type = "Orc";
std::cout << nrfour.type << " is of rank " << nrfour.get_rank2() << std::endl;
enemies.push_back(nrone);
enemies.push_back(nrtwo);
enemies.push_back(nrthree);
enemies.push_back(nrfour);
enemies.push_back(nrfive);
std::cout << add_enemy(enemies, enemy) << std::endl;
return 0;
}
Hi, I am studying Classes & Objects in C++ right now, and I'm trying to achieve the following: create a vector of NPC monsters and add a bunch of monster types to the vector. However, if the monster/enemy is of the same type, I don't want to add it to the vector, but discard it.
In my case, I have two Orcs, so the vector should discard one of the orcs, but it doesn't, and instead if showing me a strange number on the screen.
I tried it this way and I still can't figure it out :( Any solutions?
So the reason that both Orcs are added is because by the time you run add_enemy, you've already added them. All the enemies should be using the add_enemy function instead of push_back:
int main()
{
Enemy enemy;
enemy.type = "Dragon";
std::cout << enemy.type << " is a " << enemy.get_rank() << std::endl;
Enemy nrone, nrtwo, nrthree, nrfour, nrfive;
// I want to add these and keep them in a vector
std::vector<Enemy> enemies;
nrone.type = "Orc";
nrtwo.type = "Goblin";
nrthree.type = "Troll";
nrfour.type = "Ogre";
nrfive.type = "Orc";
std::cout << nrfour.type << " is of rank " << nrfour.get_rank2() << std::endl;
enemies.push_back(nrone); //Add one Orc
enemies.push_back(nrtwo);
enemies.push_back(nrthree);
enemies.push_back(nrfour);
enemies.push_back(nrfive); //Add another Orc
std::cout << add_enemy(enemies, enemy) << std::endl; //The Orcs are already in enemies!
return 0;
}
The reason you're seeing a strange number on the screen is that if you DO successfully add an enemy, the function doesn't return anything:
int add_enemy(std::vector<Enemy>&enemies, Enemy enemy) // I wanna pass by reference because I want to modify the vector
{
for(size_t i; i < enemies.size(); i++) {
if(enemies.at(i).type == enemy.type){
return 1; // Return an error
}
}
enemies.push_back(enemy); //OK, so we added the enemy, but where's the return?
}
Your add_enemies function must return a value, since it is declared as type int.
P.S... consider using a range based loop to make things a little easier:
for(Enemy& existingEnemy: enemies) {
if(enemy.type == existingEnemy.type) {
return 1;
}
}
The main problem is that you are not initializing the loop variable (i) in your add_enemy function (so the loop may never run, or it may skip some elements). Also, that function must return a value (presumably, 0) if the loop ends.
Try this:
int add_enemy(std::vector<Enemy>& enemies, Enemy enemy) // I wanna pass by reference because I want to modify the vector
{
for (size_t i = 0; i < enemies.size(); i++) { /// You forgot to initialize "i"!
if (enemies.at(i).type == enemy.type) { // here I'm saying, if I add an enemy that's of the same type, I don't wanna add it anymore
return 1; // it returns an error, because they are the same type, so it shouldn't add it?
}
}
enemies.push_back(enemy);
return 0; // The function MUST return an int value!
}
The strange number is easily explained. In your function you fail to return anything in the case where you do add the enemy. Add a return value and the strange number will go away.
int add_enemy(std::vector<Enemy>&enemies, Enemy enemy)
{
for(size_t i = 0; i < enemies.size(); i++) {
if(enemies.at(i).type == enemy.type){
return 1;
}
}
enemies.push_back(enemy);
return 0; // added a return value
}
The second problem with two orcs is also easily explained. You didn't use your add_enemy function when you added the orcs, you just used the regular vector push_back method so both orcs got added to the vector. You only used your add_enemy method for the dragon.
Also you fail to initialise i in the loop. I didn't spot that but I've corrected the code above.

How to create and increase the size of an array at runtime in C++

I want to create an array whose size I will only know at runtime, and then further increase that size during execution of the program.
This is from an /r/dailyprogrammer challenge which can be found here https://www.reddit.com/r/dailyprogrammer/comments/3twuwf/20151123_challenge_242_easy_funny_plant/
MSVisual gives me the error std::badd_array_new_length which means that it's having trouble instantiating the array?
I'm so tired with oftentimes copying code letter for letter from websites where it works and I constantly get errors. Is Visual a bad platform for learning C++? Should I try QT?
#include <iostream>
#include <string>
void main(int argc, char* argv[]) {
int currentPlants = std::stoi(argv[2]), targetPeople = std::stoi(argv[1]), currentProduce = 0, week = 0;
int * plants;
plants = new int[currentPlants];
for (int i = 0; i < currentPlants; i++) {
plants[i] = 0;
}
if (plants == nullptr) EXIT_FAILURE;
while (currentProduce < targetPeople) {
currentProduce = 0;
for (int i = 0; i < currentPlants; i++) {
currentProduce += plants[i];
plants[i]++;
}
if (currentProduce >= targetPeople) break;
else {
plants = new int[currentProduce];
for (; currentPlants < currentProduce; currentPlants++) {
plants[currentPlants] = 0;
}
}
week++;
}
std::cout << week;
}
You should use an std::vector.
As a summary :
// Create an array of size 10
std::vector<int> my_vector(10);
// Add '3' to my_vector
my_vector.push_back(3);
// Remove the last element
my_vector.pop_back();
Explanation and example here : www.cplusplus.com/reference/vector/vector/
Edit : you don't need to specify the array size when you construct your object.
// Create an array
std::vector<int> my_vector;
You can't increase the size of an array at runtime. You can create a new bigger array, and copy the contents of the old array to the new array.
The problem with your code is that on the first pass through plants all of your plants[x] are zero. You add all of these together and get zero => currentProduce == 0. You then try to new plants[currentProduce aka 0] which is illegal.
Your second problem is that each time you new you create a new array discarding the old values; new creates a new array, it doesn't know anything about the old one.
I rewrote your code using std::vector, which fixes the crash but produces an endless loop because on the first pass, currentProduce comes out to zero so the array is truncated.
#include <iostream>
#include <string>
#include <vector>
int main(int argc, const char* argv_real[])
{
const char* argv[] = { "programname", "5", "25" };
int currentPlants = std::stoi(argv[2]), targetPeople = std::stoi(argv[1]), currentProduce = 0, week = 0;
std::cout << "targetPeople = " << targetPeople
<< ", currentPlants = " << currentPlants
<< "\n";
std::vector<int> plants;
// Option 1:
// plants.resize(currentPlants);
// Option 2:
for (auto i = 0; i < currentPlants; ++i) {
plants.push_back(0);
}
while (currentProduce < targetPeople) {
std::cout << "cp: " << currentProduce
<< ", tp: " << targetPeople
<< "\n";
currentProduce = 0;
// plantCount is a reference to plants[i] for each i
for (auto& plantCount : plants) {
std::cout << plantCount << ", ";
currentProduce += plantCount;
plantCount++;
}
std::cout << " cp: " << currentProduce << "\n";
if (currentProduce >= targetPeople)
break;
// Option 1:
plants.resize(currentProduce);
// Option 2:
// while (currentPlants < currentProduce) {
// plants.push_back(0);
// }
week++;
}
std::cout << week;
}
Live demo: http://ideone.com/xGpoF6
Outside of using std::vector, you would need to allocate a new array on the heap, copy the contents over, and delete the old one. Then point your int* to the newly allocated array.
This wouldn't technically change the array size, but those accessing the object would see it as though it was changing.
This is dangerous:
int * plants;
plants = new int[currentPlants];
for (int i = 0; i < currentPlants; i++) {
plants[i] = 0;
}
if (plants == nullptr) EXIT_FAILURE;
This is what happens (if you are lucky):
the program attempts to create some memory and returns nullptr if it can't
the program then uses the memory in a loop even if nullptr was returned. (If nullptr was returned this will crash the program, silently corrupt the memory so you get the wrong results or otherwise doing something you don't want)
the program then checks to see if nullptr was returned.
If you are unlucky the compiler does time travel and destroys the entire universe. I am not kidding, have a look at:
https://blogs.msdn.microsoft.com/oldnewthing/20140627-00/?p=633
Undefined behavior causing time travel

C++ code improvement, array out of bounds

This is a class template for an Array. I overloaded the [ ] operator in hopes it would fix the "out of bounds" issue. The print outs work well, except if it falls out of range, the compiler enables the range by default and it displays a 6 digit number.
Perhaps looking for a better way to initialize the arrays with the appropriate element number for a better check and if it does fall out of range when looking up the element, display an error.
// implement the class myArray that solves the array index
// "out of bounds" problem.
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
template <class T>
class myArray
{
private:
T* array;
int begin;
int end;
int size;
public:
myArray(int);
myArray(int, int);
~myArray() { };
void printResults();
// attempting to overload the [ ] operator to find correct elements.
int operator[] (int position)
{if (position < 0)
return array[position + abs(begin)];
else
return array[position - begin];
}
};
template <class T>
myArray<T>::myArray(int newSize)
{
size = newSize;
end = newSize-1;
begin = 0;
array = new T[size] {0};
}
template <class T>
myArray<T>::myArray(int newBegin, int newEnd)
{
begin = newBegin;
end = newEnd;
size = ((end - begin)+1);
array = new T[size] {0};
}
// used for checking purposes.
template <class T>
void myArray<T>::printResults()
{
cout << "Your Array is " << size << " elements long" << endl;
cout << "It begins at element " << begin << ", and ends at element " << end << endl;
cout << endl;
}
int main()
{
int begin;
int end;
myArray<int> list(5);
myArray<int> myList(2, 13);
myArray<int> yourList(-5, 9);
list.printResults();
myList.printResults();
yourList.printResults();
cout << list[0] << endl;
cout << myList[2] << endl;
cout << yourList[9] << endl;
return 0;
}
First of all, your operator[] is not correct. It is defined to always return int. You will get compile-time error as soon as you instantiate array of something, that is not implicitly convertible to int.
It should rather be:
T& operator[] (int position)
{
//...
}
and, of course:
const T& operator[] (int position) const
{
//you may want to also access arrays declared as const, don't you?
}
Now:
I overloaded the [ ] operator in hopes it would fix the "out of bounds" issue.
You didn't fix anything. You only allowed clients of your array to define custom boundaries, nothing more. Consider:
myArray<int> yourList(-5, 9);
yourList[88] = 0;
Does your code check for out-of-bounds cases like this one? No.
You should do it:
int operator[] (int position)
{
if((position < begin) || (position > end)) //invalid position
throw std::out_of_range("Invalid position!");
//Ok, now safely return desired element
}
Note, that throwing exception is usually the best solution in such case. Quote from std::out_of_range doc:
It is a standard exception that can be thrown by programs. Some components of the standard library, such as vector, deque, string and bitset also throw exceptions of this type to signal arguments out of range.
An better option to redefining an array class is to use the containers from the std library. Vector and array(supported by c++11). They both have an overloaded operator [] so you can access the data. But adding elements using the push_back(for vector) method and using the at method to access them eliminates the chance or getting out of range errors, because the at method performs a check and push_back resizes the vector if needed.

openmpi/c++: defining a mpi data type for class with members of variable length (pointers pointing to malloced memory)?

i am currently learning to use openmpi, my aim is to parallelize a simple program whose code i will post bellow.
The program is for testing my concept of paralleling a much bigger program, i hope to learn all i need to know for my actual problem if i succeed with this.
Basically it is a definition of a simple c++ class for lists. A list consists of two arrays, one integer and one double. Entries with the same indicies belong together, in a way that the integer entry is some kind of list entry identifier (maybe an object ID) and the double entry is some kind of quantifier (maybe the weight if an object).
The basic purpose of the program is to add lists together (this is the task i want to parallelize). Adding works as follows: For each entry in one list it is checked if there is the same integer entry in the the other list, if so then the double entry gets added to the double entry in the other list, if there is no such entry in the other list then both the integer and the double entries gets added to the end of the list.
Basically each summand in this list addition represents a storage and each entry is a type of object with a given amount (int is the type and double is the amount), so adding two lists means putting the stuff from the second storage to the first.
The order of the list entries is irrelevant, this means that the addition of lists is not only associative but commutative too!
My plan is to add a very large number of such lists (a few billions) so parallelizing could be to let each thread add a subset of lists first and when this is finished distribute all such sublists (one for each thread) to all of the threads.
My current understanding of openmpi is that only the last step (distributing of finished sublists) needs any special non standard stuff. Basically i need a AllReduce but with a custom data type and a custom operaton.
The first problem i have is understanding how to create a fitting MPI data type. I came to the conclusion that i probably need MPI_Type_create_struct to create a struct type.
I found this site with a nice example: http://mpi.deino.net/mpi_functions/MPI_Type_create_struct.html
from which i learned a lot but the problem is, that in this case there are fixed member arrays. In my case i have lists with arbitrary sized member variables or better with pointers pointing to memory blocks of arbitrary size. So doing it like in the example would lead to creating a new MPI datatype for each list size (using fixed sized lists could help but only in this minimalistic case, but i want to learn how to do it with arbitrary sized lists are preparation for my actual problem).
So my question is: how to create a data type for this special case? What is the best way?
I even thought to maybe write some non mpi code to serialize my class/object, (which would be a lot of work for my real problem but in this example it should be easy) to a single block of bits. Then i could simply use a MPI function to distribute those blocks to all threads and then i just have to translate it back to the actual object, and then i could let each thread simply add the "number-of-threads" lists together to have the same full reduced list on all threads (because the operation is commutative it is not important if the order is the same on each thread in the end).
The problem is that i do not know which MPI function to use to distribute a such memory blocks to each thread so that in the end each thread has an array of "number-of-threads" such blocks (similar like AllReduce but with blocks).
But thats just another idea, i would like to hear from you whats the best way.
Thank you, here is my fully working example program (ignore the MPI parts thats just preparation, you can simply compile with: g++)
As you can see, i needed to create custom copy constructors because standard of the pointer members. I hope thats not a problem for MPI?
#include <iostream>
#include <cstdlib>
#if (CFG_MPI > 0)
#include <mpi.h>
#else
#define MPI_Barrier(xxx) // dummy code if not parallel
#endif
class list {
private:
int *ilist;
double *dlist;
int n;
public:
list(int n, int *il, double *dl) {
int i;
if (n>0) {
this->ilist = (int*)malloc(n*sizeof(int));
this->dlist = (double*)malloc(n*sizeof(double));
if (!ilist || !dlist) std::cout << "ERROR: malloc in constructor failed!" << std::endl;
} else {
this->ilist = NULL;
this->dlist = NULL;
}
for (i=0; i<n; i++) {
this->ilist[i] = il[i];
this->dlist[i] = dl[i];
}
this->n = n;
}
~list() {
free(ilist);
free(dlist);
ilist = NULL;
dlist = NULL;
this->n=0;
}
list(const list& cp) {
int i;
this->n = cp.n;
this->ilist = NULL;
this->dlist = NULL;
if (this->n > 0) {
this->ilist = (int*)malloc(this->n*sizeof(int));
this->dlist = (double*)malloc(this->n*sizeof(double));
if (!ilist || !dlist) std::cout << "ERROR: malloc in copy constructor failed!" << std::endl;
}
for (i=0; i<this->n; i++) {
this->ilist[i] = cp.ilist[i];
this->dlist[i] = cp.dlist[i];
}
}
list& operator=(const list& cp) {
if(this == &cp) return *this;
this->~list();
int i;
this->n = cp.n;
if (this->n > 0) {
this->ilist = (int*)malloc(this->n*sizeof(int));
this->dlist = (double*)malloc(this->n*sizeof(double));
if (!ilist || !dlist) std::cout << "ERROR: malloc in copy constructor failed!" << std::endl;
} else {
this->ilist = NULL;
this->dlist = NULL;
}
for (i=0; i<this->n; i++) {
this->ilist[i] = cp.ilist[i];
this->dlist[i] = cp.dlist[i];
}
return *this;
}
void print() {
int i;
for (i=0; i<this->n; i++)
std::cout << i << " : " << "[" << this->ilist[i] << " - " << (double)dlist[i] << "]" << std::endl;
}
list& operator+=(const list& cp) {
int i,j;
if(this == &cp) {
for (i=0; i<this->n; i++)
this->dlist[i] *= 2;
return *this;
}
double *dl;
int *il;
il = (int *) realloc(this->ilist, (this->n+cp.n)*sizeof(int));
dl = (double *) realloc(this->dlist, (this->n+cp.n)*sizeof(double));
if (!il || !dl)
std::cout << "ERROR: 1st realloc in operator += failed!" << std::endl;
else {
this->ilist = il;
this->dlist = dl;
il = NULL;
dl = NULL;
}
for (i=0; i<cp.n; i++) {
for (j=0; j<this->n; j++) {
if (this->ilist[j] == cp.ilist[i]) {
this->dlist[j] += cp.dlist[i];
break;
}
} if (j == this->n) {// no matching entry found in this
this->ilist[this->n] = cp.ilist[i];
this->dlist[this->n] = cp.dlist[i];
this->n++;
}
}
il = (int *) realloc(this->ilist, (this->n)*sizeof(int));
dl = (double *) realloc(this->dlist, (this->n)*sizeof(double));
if (!il || !dl)
std::cout << "ERROR: 2nd realloc in operator += failed!" << std::endl;
else {
this->ilist = il;
this->dlist = dl;
}
return *this;
}
};
int main(int argc, char **argv) {
int npe, myid;
#if (CFG_MPI > 0)
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD,&npe);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
#else
npe=1;
myid=0;
#endif
if (!myid) // reduce output
std::cout << "NPE = " << npe << " MYID = " << myid << std::endl;
int ilist[5] = {14,17,4,29,0};
double dlist[5] = {0.0, 170.0, 0.0, 0.0, 24.523};
int ilist2[6] = {14,117,14,129,0, 34};
double dlist2[6] = {0.5, 170.5, 0.5, 0.5, 24.0, 1.2};
list tlist(5, ilist, dlist);
list tlist2(6, ilist2, dlist2);
if (!myid) {
tlist.print();
tlist2.print();
}
tlist +=tlist2;
if (myid) tlist.print();
#if (CFG_MPI > 0)
MPI_Finalize();
#endif
return 0;
}

Two-Dimensional Array using Templates

I am trying to implement a dynamic array:
template <typename Item>
class Array {
private:
Item *_array;
int _size;
public:
Array();
Array(int size);
Item& operator[](int index);
};
template <typename Item>
Array<Item>::Array() {
Array(5);
}
template <typename Item>
Array<Item>::Array(int size) {
_size = size;
_array = new Item [size];
for (int i = 0; i < size; i++)
cout << i << " " << _array[i] << " " << &_array[i] << endl;
}
template <class Item>
Item& Array<Item>::operator[](int index) {
if (index < 0 || index > _size-1)
cout << "this: " << this << ". Index out of range" << endl;
return _array[index];
}
When used like this, it works as expected, i.e. prints 5:
Array< int > testArray(5);
testArray[0] = 5;
cout << testArray[0] << endl;
However, I would like to use the class for a two-dimensional dynamic array. I thought that the following would just magically work and print 5...
Array< Array<int> > testArray(5);
testArray[0][0] = 5;
cout << testArray[0][0] << endl;
...but it does not work. It crashes when I try to set the value at [0][0]. The debugger shows me that this has _size set to 0 and _array to NULL. this at that point points to the first element of the _array of the last created Array instance.
One of the things I don't get is when the "inner" array calls its constructor. Stepping through the code, I see that Array(int size) is called once and Array() five times. I would like to create the inner array with a specific size, but using Array< Array<int>(10) > testArray(5) does not compile.
Could you provide me with some insight on this? It seems I could not quite wrap my head around templates yet...
You cannot chain constructor calls in C++. Your first constructor implementation does nothing, so the 5 instances contained in the parent Array end up being uninitialized, resulting in undefined behavior.
To fix, you can either add a default value to the size parameter of the other constructor, or factor out the initialization logic in a separate (private) function, and call it from both constructors.
EDIT: The reason why the first constructor does nothing is that the line
Array(5)
does not call the constructor of the current instance, but instead allocates a new (unnamed) temporary Array instance, which is immediately destructed at the end of the line.
Use default value for constructor to call it without argument i.e. Array(int index = 5);
Please check this:
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.3
Can one constructor of a class call another constructor of the same class to initialize the this object?
You cannot call another ctor from your default ctor . If you want to have a default value you can combine the two into one.
template <typename Item>
Array<Item>::Array(int size = 5) {
_size = size;
_array = new Item [size];
for (int i = 0; i < size; i++)
cout << i << " " << _array[i] << " " << &_array[i] << endl;
}
However if you still prefer to have the two ctor then you can move the implementation to a private _setup function that can be used from both like this.
template <typename Item>
Array<Item>::Array() {
_setup(5);
}
template <typename Item>
Array<Item>::Array(int size) {
_setup(size);
}
template <typename Item>
void Array<Item>::_setup(int size) {
_size = size;
_array = new Item [size];
for (int i = 0; i < size; i++)
cout << i << " " << _array[i] << " " << &_array[i] << endl;
}
Edited to remove invalid initializer for newed array.