I am making several items, and need to set them as being interactable with each other.
Item i1 = Item("Item 1");
Item i2 = Item("Item 2");
Item i3 = Item("Item 3");
Item i4 = Item("Item 4");
i1.setInteractable({i2,i3});
i2.setInteractable({i1,i4});
i3.setInteractable({i1});
This is the method header for the setInteractable method in Item.
void setInteractable(Item i[]);
The interactable items are stored as such:
static Item interactableItems[25];
This doesn't work, the error occurs after the first curly brace. What is the correct way to do this in C++?
You don't want to store objects in your array (otherwise, they are copied), but references to objects.
Change your storage and interface like that:
void setInteractable(Item& i[]);
static Item& interactableItems[25];
I advise to look in google for:
copy constructor
pointer vs object vs reference
C++ doesn't make dynamic lists as easily as you are trying.
To make the dynamic lists you need to first declare a dynamic list:
Item * dlist = new Item[2];
Set them:
dlist[0] = i1;
dlist[1] = i2;
then pass them into your function:
setInteractable(dlist);
Finally you have to remember to clean your memory:
delete [] dlist;
Or... you do this with the standard template library.
std::vector<Item> temp1;
temp1.push_back(i1);
//... other sets
Your function should be:
void setInteractable(std::vector<Item> & list)
{
///
}
The call should be
setInteractable(temp1);
It's a wrong C++ syntax to pass an array initialization like that:
i1.setInteractable({i2,i3}); // error: invalid
In various way you can achieve this. The most straight forward way is to use std::vector.
First declare interactableItems as,
static vector<Item> interactableItems;
Now you don't need setInteractable(), if you make above variable public
Usage:
i1.interactableItems.push_back(Item("Item 2"));
i1.interactableItems.push_back(Item("Item 3"));
If you want to have variable private then you can just put a wrapper and put the push_back inside it.
I would solve the problem like so:
Change the method to work on one object at a time, and loop over them:
void setInteractable(Item &i);
for (int i = 0; i < 25; i++)
for (int j = i + 1; j < 25; j++)
items[i].setInteractable(items[j]);
Much cleaner to deal with. You can store them in a std::vector inside of the Item, and just with those.
I would suggest using one of the STL containers. My implementation will proceed thus:
// Method header for the setInteractable method
#include <vector>
class Item {
private:
// some declarations
public:
void setInteractable(vector<Item &>);
// some other declarations
};
Your implementation should them selectively push Item objects onto vectors before passing them into the function.
Refer to the Cplusplus Reference Site for some more readings on STL containers.
Related
I have an issue with creating objects, adding them to a container class, and having them go out of scope in C++.
As an example, my main.cpp
Container container;
for (unsigned int i = 0; i < 10; i++) {
Item item;
container.add_item(item);
}
and the interface of container.h
struct Container {
std::vector<std::reference_wrapper<Item>> items;
void add_item(Item& item); // Push back of items vector.
};
and item.h
struct Item {
std::unique_ptr<AnotherThing> unrelated_thing;
};
The problem is later on in my main class, the Containers created inside the for-loop have gone out of scope. If I change add_item to pass by value, it gives me issues with the unique_ptr and copy constructor.
Is there some idiomatic way to create objects inside a scope and "transfer" them to another class?
If I change add_item to pass by value, it gives me issues with the unique_ptr and copy constructor.
Your vector need to hold objects by value:
std::vector<Item> items;
then you need to move your object into vector when passed by value:
void Container::add_item( Item item )
{
items.push_back( std::move( item ) );
}
and then you need to move your object in the loop as well:
for (unsigned int i = 0; i < 10; i++) {
Item item;
container.add_item( std::move(item) );
}
or you can simply pass temporary:
for (unsigned int i = 0; i < 10; i++) {
container.add_item( Item() );
}
There are a few ways to handle this. This is basically the exact use-case for passing xvalues. Passing by value will cause a copy of all members of item. If there are members that could have large amounts of data allocated on the heap, such as a std::vector, you will want to avoid this copy and allocation by moving your members.
The simple answer is to pass by what Scott Meyers calls a 'Universal Reference'
void Container::add_item( Item&& item) {
items.push_back( std::move(item))
}
I'm trying to realize two methds append() and clear().
In appened(), I need to newPoint to the end of a list. If the list is empty, then adds newPoint as the first(and only) point in the list.
In clear(),I need to remove all the points in a list.
Can you give me some advice to realize appened and clear.
Here is a code:
//
#pragma once
const int maxListSize = 10;
class Point
{
private:
float x;
float y;
public:
Point(float x0 = 0, float y0 = 0)
{
x = x0;
y = y0;
}
};
class PointList
{
private:
//Data members
int size;
int cursor;
Point points[maxListSize];
public:
PointList();
//List Manipalution operations
void append(Point newPoint);
void clear();
~PointList();
};
*I don't need you to write everything for me, just give me some advice. I would like to realize it by myself. Thank you for helping.
Since you store your list elements by value (Point points[maxListSize]),
it is quite easy to do:
PointList() :size(0) {}
void append(Point newPoint) { points[size++] = newPoint; }
void clear() { size = 0; }
This assumes that your Point object doesn't manage any resource, which is true for this particular example. Otherwise, inserted Point objects should be destroyed in clear.
To get the semantics that you're probably expecting for appending new items, and clearing out existing items, I suggest you look at the placement new operator, and manually calling the destructor of an item in the list.
Currently your class will construct all of the items in the list when you create the list. This can be quite time consuming for complex structures. Then, instead of the constructor for your elements being called, you'll instead be calling the copy-assignment operator, as the items are already constructed.
If you store your array as
char * points[sizeof(Point)*maxListSize];
Any only initialize the items when they're actually added, you avoid the construction cost when you create the list.
Your append function takes it's argument by value. Instead, I recommend you have two append functions. One that takes const&, and the other that takes an rvalue-reference. Then, inside the append function, call the placement new operator on the address of the next location in your array.
To clear the array, simple call the destructor for each element in the array one at a time.
I added also void Clear()-method.
https://redstoner.com/forums/threads/840-minimal-class-to-replace-std-vector-in-c-for-arduino
https://forum.arduino.cc/index.php?topic=45626.0
I'm asking about this Vector class.
void push_back(Data const &x) {
if (d_capacity == d_size) resize();
d_data[d_size++] = x;
}; // Adds new value. If needed, allocates more space
How to add "insert"-method to this Vector class (arduino use C++ but not have a standard vector methods)?
Vector<Sensor*> sensors;
I have a another class Sensor and I use vector like this.
push.back(new Sensor (1,1,"Sensor_1",2));
Is it possible to add values one by one to this vector class? And how to do it?
I like to ask also other question.
How can I call delete/call destructor for this Vector "sensors" so all pointers are deleted? Or sensors vector is deleted? I want to clear the data and then add data to it.
If you want to add an item to the end of the vector, use the push_back method you've quoted above. If you want to add an item somewhere else in the vector, you'll need to add your own method which re-sizes if necessary, shifts the elements above the insert location up one place and then copies the new element into the correct slot. Something like this (untested):
void insert_at(size_t idx, Data const &data) {
assert(idx < d_size);
if (d_capacity == d_size) {
resize();
}
for (size_t i = d_size; i > idx; --i) {
d_data[i] = std::move(d_data[i - 1]);
}
d_data[idx] = data;
++d_size;
}
As Nacho points out, you might be better off with a linked list if you're going to do a lot of these insert operations, especially if the data you're storing is large and/or has a complex move operator.
I have the next problem:
I created the structure:
struct Series : vector<Candle>
{
Security Sec;
int LookFor;
int TF;
int Corrector;
string ID;
int line;
Series(){};
Series (int _lookfor);
void Update();
};
Constructor:
Series::Series (int _lookfor)
{
LookFor=_lookfor;
for (int i = 1; i<=LookFor; i++)
{
Candle cantype = Candle(i);
push_back(cantype);
}
}
So, then we call this construcor it fills object by candle-values. LookFor - is a number of candles in the vector-series.
After initialization i want update this serie (if there is more new candle, i want delete last one and insert new on the begining of vector-serie)
void Series::Update()
{
if (size()==LookFor)
{
if (newer(cantype,c1))
{
Candle cantype = Candle(1);
Candle c1 = at(0);
pop_back();
emplace(begin(),cantype);
}
}
I need to initialize a vector of these series:
vector vec;
vec.push_back(Series(3));
And constructor does its job, everithing is fine.
But then i update them:
for (size_t x =0; x<=size()-1;x++) vec[x].Update();
I have a problem: it cann't save changes in vector. In Update method everithing is fine, it inserts needed candle in itself, but then method is ended - the state of vector (each element of vec) has no changes. In method we see changes, but after it vector become after constructor-like, the state still the same.
Tell me, please, what am I doing wrong?
As others already mentioned, do not derive from these containers (could cause nasty errors like missing dtor calls and memory leaks, no virtual destructor is present in these containers). Instead, add the vector as a member or leave it as is, if you do private inheritance.
You may use the iterator interface for such containers:
for(std::vector<Series>::iterator sIt = vec.begin();sIt != vec.end();++sIt) sIt->Update();
For your task, consider using a deque or a list as a circular buffer instead of the vector for the Candles. It would perform better for insertions and therefore allows you to use push_front() instead of emplace() or insert().
Alternatively, you could hold an index of the vector element just past the last element (which should be the first) and just assign the new candle, et voilĂ , you've got a dense circular buffer.
There are implementations of such circular buffers, for example the one of boost:
http://www.boost.org/doc/libs/1_52_0/libs/circular_buffer/doc/circular_buffer.html
Despite logic issues, which could prevent the modification in certain states, I can't see, why your code doesn't work at all, at least not when I went through the snippets you posted.
I'm compiling using Code::Blocks on Windows 7 using the MinGW compiler (which I can only assume is the latest version; both Code::Blocks and MinGW were installed this past week). My issue crops up under a particular circumstance, and my attempts to write a simpler script that demonstrates the problem have failed (which implies that there is something wrong with my structure). Also, my apologies for how long this post is.
Currently, I'm rolling with one class, FXSDL, which will act as an SDL wrapper:
class FXSDL
{
public:
FXSDL();
virtual ~FXSDL();
int Initialize();
int Render();
FXID CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims);
int SetAnim(FXID hRefID, FXID hAnimID);
FXID hPlayer;
protected:
private:
list<FXSurface> m_lstFXObjects;
list<FXSurface>::iterator m_liFirst;
SDL_Surface* m_lpsfSDLScreen;
Uint32 m_tmOld;
Uint32 m_tmFrame;
};
The value type of my list is:
struct FXSurface
{
FXID hRefID;
int wpxTile;
int hpxTile;
int wpxTotal;
int hpxTotal;
int cntTiles;
map<int, vector<int> > htAnims; // All animations
map<int, vector<int> >::iterator vCurr; // Currently active animation
vector<int>::iterator fiCurr; // Currently active frame
SDL_Surface* lpsfSDL;
SDL_Rect* lprcTiles; // Predefined frame positions
string* fpImage;
};
I've implemented very simple initialize and render function. The CreateCharacter function takes a few parameters, the most important of which is htAnims, a map of integer vectors (idea being: I define numeric ids with easy-to-remember representations, such as FXA_IDLE or FXA_WALK, as the key, and the series of number values representing frames for the animation as a vector). This could be fairly easily implemented as a multidimensional integer array, but animations are variable in length and I want to be able to add new anims (or redefine existing ones) without having to recast an array.
The CreateCharacter function is simple. It creates a new FXSurface, populates it with the required data, and pushes the new FXSurface onto the list:
FXID FXSDL::CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims)
{
//list<FXSurface>::iterator lpsfTemp;
FXSurface lpsfTemp;
list<FXSurface>::iterator lpsfPos;
SDL_Rect* lprcCurr = NULL;
int cntTileW = 0;
int cntTileH = 0;
int cntCurr = 0;
// Start off by initializing our container struct
//lpsfTemp = new FXSurface();
lpsfTemp.lpsfSDL = IMG_Load(fpImage.c_str()); // Try to load the requested image
if(lpsfTemp.lpsfSDL != NULL) // If we didn't fail to
{
// Assign some variables for tracking
lpsfTemp.hRefID = hRefID;
lpsfTemp.fpImage = &fpImage;
lpsfTemp.wpxTotal = lpsfTemp.lpsfSDL->w;
lpsfTemp.hpxTotal = lpsfTemp.lpsfSDL->h;
// If a tile width was specified, use it
if(wpxTile != 0)
{
lpsfTemp.wpxTile = wpxTile;
lpsfTemp.hpxTile = hpxTile;
} // Otherwise, assume one tile
else
{
lpsfTemp.wpxTile = lpsfTemp.wpxTotal;
lpsfTemp.hpxTile = lpsfTemp.hpxTotal;
}
// Determine the tiles per row and column for later
cntTileW = lpsfTemp.wpxTotal / lpsfTemp.wpxTile;
cntTileH = lpsfTemp.hpxTotal / lpsfTemp.hpxTile;
// And the total number of tiles
lpsfTemp.cntTiles = cntTileW * cntTileH;
lpsfTemp.lprcTiles = new SDL_Rect[cntTileW*cntTileH];
// So we don't calculate this every time, determine each frame's coordinates and store them
for(int h = 0; h < cntTileH; h++)
{
for(int w = 0; w < cntTileW; w++)
{
cntCurr = (h*cntTileW)+w;
lprcCurr = new SDL_Rect;
lprcCurr->w = lpsfTemp.wpxTile;
lprcCurr->h = lpsfTemp.hpxTile;
lprcCurr->x = w*lpsfTemp.wpxTile;
lprcCurr->y = h*lpsfTemp.hpxTile;
lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
lprcCurr = NULL;
}
}
// Now acquire our list of animations and set the default
//lpsfTemp.htAnims = new map<int, vector<int> >(*htAnims);
lpsfTemp.htAnims = htAnims;
lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();
this->m_lstFXObjects.push_back(lpsfTemp);
}
else
{
hRefID = 0;
}
return hRefID;
}
It is precisely as the object is pushed that the error occurs. I've stepped through the code numerous times. Initially, I was only able to tell that my iterators were unable to dereference to the FXSurface object. After using watches to identify the exact memory address that the iterator and list objects pointed to, and dereferencing the address, I noticed the reason for my segfaults: all the values which I put into the original FXSurface were pushed down two memory blocks when the list object copied it!
My process for doing this is simple. I set up a breakpoint at the return statement for CreateCharacter, which gives me a view of lpsfTemp (the FXSurface I later add to the list) and m_lstFXObjects (the list I add it to). I scroll through the members of m_lstFXObjects, which brings me to _M_node, which contains the memory address of the only object I have added so far. I add a watch to this address in the form of (FXSurface)-hex address here-
First, find the address:
(There should be a picture here showing the highlighted _M_node attribute containing the list item's address, but I can't post pictures, and I can only post one URL. The second one is by far more important. It's located at http://www.fauxsoup.net/so/address.jpg)
Next, we cast and deference the address. This image shows both lpsfTemp and the copy in m_lstFXObjects; notice the discrepancy?
http://www.fauxsoup.net/so/dereferenced.jpg - See? All the values are in the correct order, just offset by two listings
I had initially been storing fpImages as a char*, so I thought that may have been throwing things off, but now it's just a pointer and the problem persists. Perhaps this is due to the map<int, vector<int> > I store?
FXSDL has a destructor, but no copy constructor and no assignment operator. Yo you're using naked pointers, but violate the Rule of Three.
I'm not going to look any further.
Use smart pointers to manage resources. Do not put a naked resource into a type, except when that type's only intention is to manage this one resource. From another answer given yesterday:
As a rule of thumb: If you have to manually manage resources, wrap each into its own object.
At a glance, I'd say you're double-deleting lpsfSDL and/or lprcTiles. When you have raw pointers in your structure, you need to follow the rule-of-three (implement copy constructor, assignment operator, and destructor) to properly manage the memory.
These lines look wrong to me:
lprcCurr = new SDL_Rect;
lprcCurr->w = lpsfTemp.wpxTile;
lprcCurr->h = lpsfTemp.hpxTile;
lprcCurr->x = w*lpsfTemp.wpxTile;
lprcCurr->y = h*lpsfTemp.hpxTile;
lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
lprcCurr = NULL;
lpsfTemp.lprcTiles is a SDL_Rect*. lprcTemp.lprcTiles[cntCurr] is a SDL_Rect. You should be writing this, IMHO:
SDL_Rect tmpRect;
tmpRect.w = lpsfTemp.wpxTile;
tmpRect.h = lpsfTemp.hpxTile;
tmpRect.x = w*lpsfTemp.wpxTile;
tmpRect.y = h*lpsfTemp.hpxTile;
lpsfTemp.lprcTiles[cntCurr] = tmpRect;
Dump the lprcCurr entirely.
Now this code:
lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();
This is bad. These iterators are invalid as soon as the push_back completes. That push_back is making a copy of lpsfTemp. The map and vector members are going to copy themselves and those iterators will copy themselves but they will be pointing to lpsfTemp's members which are going to be destroyed as soon as CreateCharacter exits.
One way to fix that would be to push_back a FXSurface object at the beginning, use back() to get its reference and operate on that instead of lpsfTemp. Then the iterators would stay consistent and they should stay consistent since you are using a list which does not copy its objects around. If you were using a vector or deque or anything other than a list you would need to manage all those pointers and iterators in the copy constructor and assignment operator.
Another thing: Double and triple check your array bounds when you access that lprcTiles array. Any mistake there and you could be scribbling over who knows what.
I don't know if any of that will help you.