I have an array of objects that have a vector as a class member (actually it is a struct). The struct is:
struct Cell {
vector<int> borderNodes;
vector<int> helperNodes;
int minNodePointer;
int maxNodePointer;
Cell() {
minNodePointer = -1;
maxNodePointer = -1;
}
Cell(int _minNodePointer, int _maxNodePointer) {
minNodePointer = _minNodePointer;
maxNodePointer = _maxNodePointer;
}
vector<int>& getHelperNodes() {
return helperNodes;
}
vector<int>& getBorderNodes() {
return borderNodes;
}
void setHelperNodesArray() {
sort(helperNodes.begin(), helperNodes.end());
}
void setBorderNodesArray() {
sort(borderNodes.begin(), borderNodes.end());
}
};
I have built an array of those objects as a global variable with :
Cell* cells = new Cell[maxNumberOfCells];
and I want to add some integers to the vectors inside the objects.
I tried this (inside a function):
cells[cellId].borderNodes.push_back(node_id);
or
cells[cellId].getBorderNodes().push_back(node_id);
and they compile fine, but nothing is added to the vector. How is the correct way to do this? Here is the function that reads from a db, and adds the integers. The query and reading from db is correct, so no mistakes there.
void loadBorderNodesPerCellBnOnly(bool shouldRearrangeNodes, int subtractor, int maxNumberOfCells, int** cellBorderNodes) {
cellBorderNodes = new int*[maxNumberOfCells];
try {
work W(Conn);
for (int rownum = 0; rownum < r.size(); ++rownum) {
const result::tuple row = r[rownum];
vector<string> s = split(row[1].as<string > (), ' ');
const int mySize = s.size();
int cellId = row[0].as<int> ();
cellBorderNodes[cellId] = new int[mySize];
for (int k = 0; k < mySize; k++) {
int node_id = atoi(s[k].c_str());
cellBorderNodes[cellId][k] = node_id;
(cells[cellId].getBorderNodes()).push_back(node_id);
try {
nodes[node_id - subtractor].cellId = cellId;
} catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
}
nodes[node_id - subtractor].isBorderNode = true;
}
s.clear();
}
} catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
}
for (int k = 0; k < maxNumberOfCells; k++) {
cout << k << ": " << cells[k].borderNodes.size() << endl;
cells[k].setBorderNodesArray();
if (maxBorderNodesPerCell < cells[k].borderNodes.size()) {
maxBorderNodesPerCell = cells[k].borderNodes.size();
}
}
}
void loadBorderNodesPerCellBnOnly([...], int** cellBorderNodes) {
cellBorderNodes = new int*[maxNumberOfCells];
The function above takes an int** by value. The copy is called cellBorderNodes inside the function. In the next line you change the value of the copy and continue updating that. None of those changes will be visible outside of the function. (And you will leak the acquired memory)
If you want to pass an int** and modify it inside the function, consider passing it by reference as in int**&. But you might be better off using higher level constructs (pass a vector<vector<int>>&, create a type that represents the data and pass it by value...)
Related
I am trying to build an inventory system in C++ for a game that I am working on. However, there is a bug in the inventory system when I call Inventory::AddItem(Item i), no item gets added and that slot still stays blank. Currently, I handle the inventory through std::vector<Item>, where Item is a struct containing the type, if it is stackable, the maximum number of blocks in a stack, the current number of blocks in the stack, and a couple of objects for animation. Moreover, I automatically fill the inventory in with 40 slots of air blocks, which have the ID of INVENTORY_EMTPY_SLOT_ID.
Here is the code:
typedef struct item {
int type; // this is whether the block is a foreground of background tile
int id; // use this for the id of objects
bool stackable; // true indicates that the block can be stacked
int max_num; // maximum number of blocks in a stack
int num; // the current number of blocks in the stack
Animation* use_animation; // the animation of the block or item when it is being used
Animation* other_animation; // secondary animation of item in case it is necessary
} Item;
How I initialize empty slots:
for (size_t x = 0; x < INVENTORY_MAX_SLOTS; x++) {
Item i = {0, INVENTORY_EMPTY_SLOT_ID, true, 1, 1, NULL, NULL};
this->items.push_back(i);
}
Adding items
/*********BUG HERE:******************/
void Inventory::AddItem(Item item) {
// find all indexes with the same item.id
std::vector<size_t> indexes_w_same_item;
for (size_t i = 0; i < this->items.size(); i++) {
if (this->items[i].id == item.id) {
indexes_w_same_item.push_back(i);
}
}
// find the next empty slot
int next_empty_slot = -1;
for (size_t i = 0; i < this->items.size(); i++) {
if (this->items[i].id == INVENTORY_EMPTY_SLOT_ID) {
next_empty_slot = i;
}
}
// go through all the indexes with the same item.id
// and see if at least one of them is empty.
// if one is empty and has sufficient capacity,
// add the item and return. if it isn't, keep moving forward
for (size_t x = 0; x < indexes_w_same_item.size(); x++) {
if (item.id == this->items[indexes_w_same_item[x]].id) {
if (this->items[indexes_w_same_item[x]].num + item.num <= this->items[indexes_w_same_item[x]].max_num) {
this->items[indexes_w_same_item[x]].num += item.num;
return;
}
}
}
// if there is an empty slot, make a new stack
if (next_empty_slot >= 0) {
this->items[next_empty_slot].id = item.id;
this->items[next_empty_slot].max_num = item.max_num;
// clamp item.num so it doesn't exceed item.max_num
if (item.max_num > item.num) {
this->items[next_empty_slot].num = item.num;
} else {
this->items[next_empty_slot].num = item.max_num;
}
}
}
I know you have found the error, but there are many issues in your code that lead to this error, and I wanted to help you understand how to write better code, so next time it will be easier for you to find it (and maybe even avoid it!).
You should divide the logic into as small pieces as possible - modularity is a key for more clear and clean code, which was helping you to understand the error much faster.
Instead of making a clear flow, you made two distinct flows on and off. The code is much clearer when you exhaust one possible flow, and only then start the other (look at the functions add_item_to_existing_stack_if_possible and add_item_to_new_stack_if_possible.
Your variables/functions/classes names must represent what they are standing for, it wasn't the case with the original code! Look at the Item struct now - it is much clearer what each member represents, without comments! (personally, I am not using comments at all)
C++ is not C with classes - things like typedef should not appear in your code, you should use operator<< to std::cout instead of printf and so on.
Make sure you add const specifiers as possible, it can help find many mistakes on compile time (and make your program run faster).
Performance related - you should pass objects as references as much as possible, it is much faster to pass an uint64 (memory location) than copy your entire Item object.
#include <vector>
#include <array>
#include <iostream>
struct Animation;
struct Item {
int type;
int id;
bool is_stackable;
int max_num_blocks_in_stack;
int curr_num_of_blocks_in_stack;
Animation* used_animation; // if it is non nullable, you should consider to use it without a pointer (possibly a reference)
Animation* secondary_animation; // nullable - can be a pointer or std::optional
};
class Inventory
{
public:
bool add_item(Item&& item);
private:
bool is_slot_empty(size_t idx) const { return items[idx].id == INVENTORY_EMPTY_SLOT_ID; }
std::vector<size_t> find_indexes_of(const Item& item) const;
size_t find_next_empty_slot() const;
bool add_item_to_existing_stack_if_possible(const Item& item);
bool add_item_to_new_stack_if_possible(Item&& item);
void print() const;
static constexpr size_t MAX_INV_SIZE = 40; // can transform into a class template!
std::array<Item, MAX_INV_SIZE> items;
static constexpr int INVENTORY_EMPTY_SLOT_ID = -1;
};
std::vector<size_t> Inventory::find_indexes_of(const Item& item) const
{
std::vector<size_t> indexes{};
for (size_t idx = 0; idx < MAX_INV_SIZE; ++idx)
{
if (items[idx].id == item.id)
{
indexes.push_back(idx);
}
}
return indexes;
}
size_t Inventory::find_next_empty_slot() const
{
for (size_t idx = 0; idx < MAX_INV_SIZE; ++idx)
{
if (is_slot_empty(idx))
{
return idx;
}
}
return MAX_INV_SIZE; // invalid value!
}
void Inventory::print() const
{
for (size_t i = 0; i < MAX_INV_SIZE; ++i)
{
if (this->items[i].id != INVENTORY_EMPTY_SLOT_ID)
{
std::cout << "Inventory slot: " << i << "\n"
<< "Item ID: " << items[i].id << "\n"
<< "Item Num: " << items[i].curr_num_of_blocks_in_stack << "\n"
<< "Item Max Num: " << items[i].max_num_blocks_in_stack << std::endl;
//<< "Item Texture: " << textures[items[i].id] << std::endl;
}
}
}
bool Inventory::add_item_to_existing_stack_if_possible(const Item& item)
{
auto indexes_with_same_item = find_indexes_of(item);
for (auto idx : indexes_with_same_item)
{
if (item.id == items[idx].id)
{
if (items[idx].curr_num_of_blocks_in_stack + item.curr_num_of_blocks_in_stack <=
items[idx].max_num_blocks_in_stack)
{
items[idx].curr_num_of_blocks_in_stack += item.curr_num_of_blocks_in_stack;
return true;
}
}
}
return false;
}
bool Inventory::add_item_to_new_stack_if_possible(Item&& item)
{
size_t next_empty_slot = find_next_empty_slot();
if (next_empty_slot >= 0)
{
this->items[next_empty_slot] = std::move(item);
return true;
}
return false;
}
bool Inventory::add_item(Item&& item)
{
bool was_possible_to_add_to_existing_stack = add_item_to_existing_stack_if_possible(item);
if (!was_possible_to_add_to_existing_stack)
{
return add_item_to_new_stack_if_possible(std::move(item));
}
return false;
}
Okay, I figured it out. There must be a break at the end of the second for loop, where it looks for the next empty slot, otherwise, it will detect the next empty slot as the last slot in the inventory, assuming that you are adding the first item in the inventory. Therefore, that item did not show up in the hopbar.
Here is the correct solution:
void Inventory::AddItem(Item item) {
// find all indexes with the same item.id
std::vector<size_t> indexes_w_same_item;
for (size_t i = 0; i < this->items.size(); i++) {
if (this->items[i].id == item.id) {
indexes_w_same_item.push_back(i);
}
}
// find the next empty slot
int next_empty_slot = -1;
for (size_t i = 0; i < this->items.size(); i++) {
if (this->items[i].id == INVENTORY_EMPTY_SLOT_ID) {
next_empty_slot = i;
break;
}
}
// go through all the indexes with the same item.id
// and see if at least one of them is empty.
// if one is empty and has sufficient capacity,
// add the item and return. if it isn't, keep moving forward
for (size_t x = 0; x < indexes_w_same_item.size(); x++) {
if (item.id == this->items[indexes_w_same_item[x]].id) {
if (this->items[indexes_w_same_item[x]].num + item.num <= this->items[indexes_w_same_item[x]].max_num) {
this->items[indexes_w_same_item[x]].num += item.num;
return;
}
}
}
// if there is an empty slot, make a new stack
if (next_empty_slot >= 0) {
this->items[next_empty_slot] = item;
}
for (size_t i = 0; i < INVENTORY_MAX_SLOTS; i++) {
if (this->items[i].id != '.') {
printf("\nInventory slot: %d\n", i);
printf("Item ID: %c\n", this->items[i].id);
printf("Item Num: %d\n", this->items[i].num);
printf("Item Max Num: %d\n", this->items[i].max_num);
printf("Item Texture: %x\n", this->textures[this->items[i].id]);
}
}
return;
}
I'm currently building a library In C++. I have met this problem few days ago and I'm unable to fix it. I have shorten the code so it can be seen easier.
Below is my code:
class String
{
private:
mutable char* v;
mutable int l = 0;
public:
String()
{
l++;
v = new char[1];
*v = '\0';
}
String(const char* value)
{
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
String(const String& value)
{
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
int len() const
{
return l - 1;
}
char* val() const
{
return v;
}
char* operator=(const char* value) const
{
delete[] v;
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char* operator=(const String& value) const
{
delete[] v;
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char operator[](const int& index) const
{
return v[index];
}
};
class StringArray
{
private:
union ArrayDef
{
public:
mutable String stringV;
mutable int intV;
ArrayDef()
{
}
ArrayDef(const String& value)
: stringV(value)
{
}
ArrayDef(const int& value)
: intV(value)
{
}
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
String operator=(const String& value) const
{
stringV = value;
return stringV;
}
int operator=(const int& value) const
{
intV = value;
return intV;
}
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
};
mutable ArrayDef* arrdef;
mutable int arrLen = 0;
public:
StringArray()
{
}
void add(const ArrayDef& value) const
{
ArrayDef temp[arrLen + 1];
for (int i = 0; i < arrLen; i++)
temp[i] = arrdef[i];
temp[arrLen] = value;
arrLen++;
delete[] arrdef;
arrdef = new ArrayDef[arrLen];
for (int i = 0; i < arrLen; i++)
arrdef[i] = temp[i];
}
int len() const
{
return arrLen;
}
ArrayDef val(const int& index) const
{
return arrdef[index];
}
};
And my driver code:
#include <iostream>
int main()
{
StringArray arr;
arr.add(String("Hello"));
arr.add(String("World"));
std::cout << "Length of the array: " << arr.len() << std::endl;
int indexOfString = 1;
int indexOfCharacter = 2;
char s = arr.val(indexOfString).stringV[indexOfCharacter];
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}
I have created two class, that is, String and StringArray class.
For String class, I need to always add a null character after the char pointer array for safety issue.
For StringArray class, I uses union because it's actually an array for multiple types.
It can be successfully compiled but it output some random character and it is different every time I run it.
Any answers will be appreciated, and please tell me why and how it don't works. Thank you.
From,
HaiQin.
This code is just a collection of antipatters that makes it difficult to study. What is the reason of making the internal data mutable? Why do you need to play with length and l where sometimes it is the length of the string, sometimes it is the size of array? The operator operator= returns char* which is a bad practice. Using const int& index as a parameter is a strange choice. You allocate arrays multiple times but you have no destructor that frees the memory.
Here your assignment operator returns a value, not reference!
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
Next comes even more dangerous practice:
// Recollect that this is a union
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
You are assigning both fields of the union at the same time! Did you mean struct?
Try to fix that. Start with changing union to structure.
One of the things that certainly will not work is the ArrayDef copy constructor and operator=(const ArrayDef & value). This is because you may only use the active value in the union, not both at the same time. This is usually solved by using a tagged union. Is there a reason you cannot use the Standard Template Library?
#include <iostream>
#include <string>
#include <vector>
int main() {
std::vector<std::string> arr;
arr.push_back(std::string("Hello"));
arr.push_back(std::string("World"));
std::cout << "Length of the array: " << arr.size() << std::endl;
constexpr int indexOfString = 1; // second string - starting from 0!
constexpr int indexOfCharacter = 2; // third character
char s = arr.at(indexOfString).c_str()[indexOfCharacter]; // using interfaces closest to the original
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}
I have the following problem:
I have main executable program called algatorc. In this program, I have class called TestCase, AbsAlgorithm and TestSetIterator. End user must create new algatorc program and then inherits these three classes.
Let's say that end user creates project Sorting. He would end up with three classes called SortingTestCase, SortingTestSetIterator and SortingAbsAlgorithm.
So here is the thing. End user has a method SortingTestSetIterator::get_current and return type of this function is TestCase*. In this method, he creates instance of SortingTestCase and return this instance. So he actually returned child of TestCase. In my main program I save this pointer to TestCase *t (and no, I can't save it as SortingTestCase, because before runtime, I don't know the name of the project) and then I send this pointer to method SortingAbsAlgorithm::init(TestCase* test_case). In this particulary method, end user will probably cast this object to his (SortingTestCase), and this is done like this:
sorting_test_case = dynamic_cast<SortingTestCase*>(test);
This SortingTestCase is derived from TestCase and has all members from parent class and two variables of his own: array and size. So when in init method I say
for (int i = 0; i<sorting_test_case->size; i++)
{
std::cout << sorting_test_case[i] << std::endl;
}
then I get nothing. It seems like array is empty.
Any idea what am I doing wrong?
Edit:
class SortingAbsAlgorithm : public AbsAlgorithm
{
private:
SortingTestCase *sorting_test_case;
public:
bool init (TestCase *test)
{
sorting_test_case = dynamic_cast<SortingTestCase*>(test);
std::cout << "INIT ARRAY" << std::endl;
for (int i = 0; i<sorting_test_case->size; i++)
{
std::cout << sorting_test_case->array_to_sort[i] << " ";
}
}
};
class SortingTestCase : public TestCase
{
public:
int size;
int *array_to_sort;
void init_array(int tab[], int size)
{
array_to_sort = new int[size];
for (int i = 0; i<size; i++)
{
array_to_sort[i] = tab[i];
}
}
};
class SortingTestSetIterator : public TestSetIterator
{
private:
std::string file_path;
std::string test_file_name;
public:
TestCase *get_current()
{
if (current_input_line.empty())
{
return nullptr;
}
std::vector<std::string> fields;
std::string token;
std::stringstream str(current_input_line);
while ( getline(str, token, ':') )
{
fields.push_back(token);
}
str.clear();
if (fields.size() < 3)
{
report_invalid_data_format("to few fields");
return nullptr;
}
std::string test_name = fields.at(0);
int prob_size;
try
{
prob_size = std::atoi(fields.at(1).c_str());
} catch (...)
{
report_invalid_data_format("'n' is ot a number");
}
std::string group = fields.at(2);
std::string test_id = "Test-" + std::to_string(line_number);
EParameter *test_id_par = new EParameter("TestID", "Test identificator", test_id);
EParameter *parameter1 = new EParameter("Test", "Test name", test_name);
EParameter *parameter2 = new EParameter("N", "Number of elements", std::to_string(prob_size));
EParameter *parameter3 = new EParameter("Group", "A name of a group of tests", group);
SortingTestCase *t_case = new SortingTestCase();
t_case->addParameter(*test_id_par);
t_case->addParameter(*parameter1);
t_case->addParameter(*parameter2);
t_case->addParameter(*parameter3);
int arr[prob_size];
int i = 0;
if (group == "INLINE")
{
if (fields.size() < 4)
{
report_invalid_data_format("to few fields");
return nullptr;
}
std::vector<std::string> data;
std::stringstream ss(fields.at(3));
while (getline(ss, token, ' ') )
{
data.push_back(token);
}
if (data.size() != prob_size)
{
report_invalid_data_format("invalid number of inline data");
return nullptr;
}
try
{
for (i = 0; i<prob_size; i++)
{
arr[i] = std::atoi(data.at(i).c_str());
}
} catch (...)
{
report_invalid_data_format("invalid type of inline data - data " + std::to_string((i+1)));
return nullptr;
}
}
else if (group == "RND")
{
srand(time(NULL));
for (i = 0; i<prob_size; i++)
{
arr[i] = rand() % prob_size + 1000;
}
}
else if (group == "SORTED")
{
for (i = 0; i<prob_size; i++)
{
arr[i] = i;
}
}
else if (group == "INVERSED")
{
for (i = 0; i<prob_size; i++)
{
arr[i] = prob_size - i;
}
}
t_case->init_array(arr, prob_size);
return t_case;
}
};
This are end users classes. I create library out of this classes and I load them in my program via dlopen and dlsym. I get instance of classes like this:
#ifdef __cplusplus
extern "C" {
#endif
TestSetIterator * create_iterator_object() {
return new SortingTestSetIterator;
}
void destroy_iterator_object(TestSetIterator* object) {
delete object;
}
#ifdef __cplusplus
}
and then in my main program I load symbol create_iterator_object
create_it = (TestSetIterator* (*)())dlsym(handle, "create_iterator_object");
and then I say this:
TestSetIterator *it = (TestSetIterator*)create_it();
I can call it->get_current() which loads line from file to variable curent_input_line. So I do this:
TestCase *t_case = it->get_current();
and then I say
a->init(t_case)
a is here actually SortingAbsAlgorithm loaded exactly the same as TestSetIterator (ofcourse, different symbol) and is "saved" into AbsAlgorithm a*.
So when a->init(t_case) is called, then in this init end-user cast t_case this is type of TestCase to SortingTestCase. And when I want to print elements in array, array is empty.
I'm new to C++, and I'm having significant trouble with creating an array of objects using a pass by pointer and reference. This is not the actual code; it's an example of what the code essentially does.
#include <iostream>
class MyClass
{
public:
MyClass();
static int doStuff(MyClass *&classArray);
void print_number();
private:
int number;
};
MyClass::MyClass()
{
}
int MyClass::doStuff(MyClass *&classArray)
{
int i = 0;
for (i = 0; i < 10; i++) {
*classArray[i].number = i;
}
return i;
}
void MyClass::print_number()
{
std::cout << number << "\n";
}
int main(void)
{
MyClass *test = nullptr;
int p = MyClass::doStuff(test);
std::cout << p << '\n';
for (int i = 0; i < 10; i++) {
test[i].print_number();
}
return 0;
}
When compiled, this gives a segmentation fault.
This is how you do it (don't forget do delete classArray with delete[] at the end of your program or destructor:
new operator has to have default constructor, if you want to use non-default it is easier to create copy constructor, then a temporary object and copy.
#include <iostream>
class MyClass
{
public:
MyClass();
MyClass(int x, int y);
MyClass(MyClass &OldClass);
static int doStuff(MyClass *&classArray, int Size, int x, int y);
void print_number();
private:
int number, x, y;
};
MyClass::MyClass()
{
number = 0;
x = 0;
y = 0;
}
MyClass::MyClass(int x, int y)
{
number = 0;
this->x = x;
this->y = y;
}
MyClass::MyClass(MyClass &OldClass)
{
this->number = OldClass.number;
this->x = OldClass.x;
this->y = OldClass.y;
}
int MyClass::doStuff(MyClass *&classArray, int Size, int x, int y)
{
if (Size > 0)
{
classArray = new MyClass[Size];
for (int i = 0; i < Size; i++)
{
classArray[i] = MyClass(x, y);
classArray[i].number = i;
}
return Size;
}
else
return 0;
}
void MyClass::print_number()
{
std::cout << number << " " << x << " " << y << "\n";
}
int main(void)
{
MyClass *test = nullptr;
int p = MyClass::doStuff(test, 10, 5, 6);
std::cout << p << '\n';
for (int i = 0; i < p; i++) {
test[i].print_number();
}
delete[] test;
std::cin.get();
return 0;
}
It is not working because you need to allocate the array, as the function is trying to access elements of an array which has yet not been initialized to hold that amount of elements. You can do this by
MyClass *test = new MyClass[array_size];
Or
MyClass test[array_size];
Or by using a resizable container such as std::vector, and changing the function parameters accordingly
*classArray[i].number = i;
You called doStuff with a null pointer, so classArray is null and is not an array. Dereferencing a null pointer results in undefined behavior and on most implementations you'll usually get a crash.
You're also dereferencing something that's not a pointer so this code will not even compile. The error I get is:
main.cpp:23:9: error: indirection requires pointer operand ('int' invalid)
*classArray[i].number = i;
^~~~~~~~~~~~~~~~~~~~~
Presumably this is just because, as you say, the code you're showing is not your real code and classArray[i].number corresponds to a pointer in your real code. But I thought I'd point this out anyway, just in case.
Given the context of your code, here's a working example of your code:
#include <iostream>
class MyClass
{
public:
MyClass() {}
static int doStuff(MyClass*& classArray, size_t sz)
{
int i = 0;
for (; i < sz; i++) {
classArray[i].number = i;
}
// not sure what you want here, but this will return sz+1 if sz>0
return i;
}
void print_number()
{
std::cout << this->number << std::endl;
}
private:
int number;
};
int main(void)
{
MyClass* test = new MyClass[10];
int p = MyClass::doStuff(test, 10);
std::cout << p << '\n';
for (int i = 0; i < 10; i++) {
test[i].print_number();
}
delete[] test;
return 0;
}
Though as others have pointed out, you are using C++, while it's a great exercise in understand how to pass pointers and arrays around, you might find the STL and C++stdlib contain a lot of these types of idioms in an 'easier to understand context'.
Here's your code with some C++ STL:
#include <iostream>
#include <vector>
class MyClass
{
public:
MyClass() {}
MyClass(int i) : number(i) {}
static int doStuff(std::vector<MyClass>& classArray, size_t sz)
{
int i = 0;
for (; i < sz; i++) {
classArray.push_back(MyClass(i));
}
// not sure what you want here, but this will return sz+1 if sz>0
return i;
}
void print_number()
{
std::cout << this->number << std::endl;
}
private:
int number;
};
int main(void)
{
std::vector<MyClass> test;
int p = MyClass::doStuff(test, 10);
std::cout << test.size() << '\n';
// can use iterators here if you want
std::vector<MyClass>::iterator itr = test.begin();
for (; itr != test.end(); itr++) {
itr->print_number();
}
return 0;
}
Hope that can help.
I need to implement a dynamic array by myself to use it in a simple memory manager.
struct Block {
int* offset;
bool used;
int size;
Block(int* off=NULL, bool isUsed=false, int sz=0): offset(off), used(isUsed), size(sz) {}
Block(const Block& b): offset(b.offset), used(b.used), size(b.size) {}
};
class BlockList {
Block* first;
int size;
public:
BlockList(): first(NULL), size(0) {}
void PushBack(const Block&);
void DeleteBack();
void PushMiddle(int, const Block&);
void DeleteMiddle(int);
int Size() const { return size; }
void show();
Block& operator[](int);
Block* GetElem(int);
void SetElem(int, const Block&);
~BlockList();
};
I need to overload operator[].
Block& BlockList::operator\[\](int index) {
try {
if (index >= size)
throw out_of_range("index out of range");
else
return (first[sizeof(Block)*index]);
}
catch(exception& e) {
cerr << e.what() << endl;
}
}
void BlockList::PushBack(const Block& b) {
if(!size)
first = new Block(b);
else {
Block* temp = new Block[size + 1];
int i = 0;
for (i = 0; i < size; i++)
temp[sizeof(Block)*i] = this->operator[](i);
delete []first;
temp += sizeof(Block);
temp->offset = b.offset;
temp->size = b.size;
temp->used = b.used;
first = temp;
}
size++;
}
When I use PushBack to push the first element, it works OK, but when it comes to the second, third, ..., the program didn't crash, but it just shows results I didn`t expect to see.
Here is how I get the contents of my array:
void BlockList::show() {
for (int i = 0; i < size; i++) {
Block current(operator[](i));
cout << "off: " << current.offset << " size: " << current.size << endl;
}
}
first is a Block pointer so you only need to pass in index.
Block* first;
...
first[0] //returns the first element
first[1] //returns the second element
In your example you are passing in too high of an index value when indexing first because you're using sizeof inside.
Corrected code:
Block& BlockList::operator[](int index) {
try {
if (index >= size)
throw out_of_range("index out of range");
else
return (first[index]);//<--- fix was here
}
catch(exception& e) {
cerr << e.what() << endl;
}
}
An array knows how big its elements are, so you don't have to do the math with sizeof(Block). Just use i as the index.
On a related note, the C++ FAQ Lite has a great section on operator overloading that covers all kinds of useful stuff.