Accessing a protected member - c++

I'm new to C++ and struggling to setup a proper class with private members and accessing them. Basically, I have a vector of Layers which make up a Stack. I'd like to create a simple function which simply adds a layer to the stack. I've tried to simplify this example to explain my problem.
// Stack.h
namespace NS {
class Stack
{
public:
Stack() {
}
virtual ~Stack() {
}
std::vector<Layer> const &getLayers() const;
virtual Layer* AddLayer(TextureBase texture);
protected:
std::vector<Layer> _layers;
}
This is my cpp file
//Stack.cpp
namespace NS {
std::vector<Layer> const &Stack::getLayers() const {
return _layers;
}
Layer* Stack::AddLayer(TextureBase texture) {
Layer* newLayer = new Layer();
newLayer->setTexture(texture);
std::vector<Layer> layerStack = Stack::getLayers();
layerStack.push_back(*newLayer);
return newLayer;
}
}
In my main file I create the stack and then try to add the layer like this:
auto myStack = getStack();
myStack->AddLayer(myTexture);
However, when I place a breakpoint after this line, myStack doesn't contain any layers (the size is 0). I can step through the AddLayer function and it does appear to add the Layer to the Stack... but perhaps it's not referencing the vector correctly. Can anyone provide some guidance as to why this is occurring?

The problem is that layerStack is a local copy of _layers:
std::vector<Layer> layerStack = Stack::getLayers();
You are pushing your new layer to this local copy, not to your data member. You need to take a reference to your data member instead:
std::vector<Layer>& layerStack = Stack::getLayers();
Alas, this won't compile because your getLayers function returns a const reference. You need to add a non-const counterpart:
std::vector<Layer>& getLayers();

There are 3 issues you have to address.
1. Layer* newLayer = new Layer() will dynamically allocate a Layer object, but when you insert it into your vector, you dereference it, so you end up doing a push_back on a COPY of your Layer object. You could have just used Layer newLayer = Layer(), or if I understand your intent correctly a vector of Layer pointers:
vector<Layer*> _layers;
In order to be editable, your getLayers() function must NOT return a const-ref to the _layers vector. Change it to std::vector& getLayers();
Finally, std::vector layerStack = Stack::getLayers(); creates a COPY of the vector returned by getLayers(). Change it to std::vector& layerStack = Stack::getLayers()

Related

Copy Pointers of Object into New Pointer of this Object

I have a class, GameObject, which has a std::vector<Component*> mComponents and I have overloaded the GameObject(const GameObject&). I am trying to copy the mComponents over from one to the other, but making each of the contained Component*'s into a new object entirely, but keeping the objects contents exactly the same. This is what I have at the moment:
GameObject.cpp
GameObject::GameObject(const GameObject& other)
{
if (this != &other)
{
this->mComponents = other.mComponents; // EDIT 1
for (int i = 0; i < (int)mComponents.size(); i++)
{
// FILL this->mComponents with NEW objects but
// containing the exact same data and
// variables from other.mComponents
this->mComponents[i] = other.Copy(); // EDIT 2 EXAMPLE OF IDEA IN COMMENTS
this->mComponents[i]->setParent(this);
}
}
}
Engine.cpp (Extract)
GameObject cube, cube2;
cube.addComponent(new DirectionalLight(glm::vec3(-0.2f, -1.0f, -0.3f)));
cube.addComponent(new Texture("Resources/Textures/container.png", "Resources/Textures/container_specular.png"));
cube.addComponent(new Shader("Resources/Shaders/cube.shader"));
cube.addComponent(new Cube());
cube2 = GameObject(cube);
When I instantiate cube2, the mComponents Components* contents all stay exactly the same but I would like to create a new Component*'s to fill this std::vector from the GameObject(const GameObject&) fucntion, whilst keeping all the variables the same.
P.s. I know that most other operators such as '=' will not create new Components for inside the vector but I will be implementing that aswell after I figure out how to fill the vector with new Component*'s.
this->mComponents[i]->Copy(other); Will not work. At least, not from a pure inheritance standpoint. A supertype (base) type pointer cannot be implicitly cast to a derived type. This is called downcasting, and no language supports it implicitly.
A simpler way to do it is to define a virtual "clone" function in each component:
virtual Component* clone()=0; // base declaration
virtual Component* Texture::clone() //derived implementation
{
return new Texture(*this);
}
Then in your game object copy constructor:
for (int i = 0; i < (int)other.mComponents.size(); i++)
{
// FILL this->mComponents with NEW objects but
// containing the exact same data and
// variables from other.mComponents
this->mComponents.push_back(other.mComponents->clone());
this->mComponents[i]->setParent(this);
}
This way, you let the component itself handle the copying process.

C++ Empty Pointer

So I am new to C++ and I am trying to use an inner class like this:
struct PreviousButton {
SelectionScreen* ss;
PreviousButton(SelectionScreen* sss) {
ss = sss;
}
void ClickAction() {
//images is a vector with images. in here it is empty
ss->images;
}
};
This inner class is inside the class SelectionScreen and I need to perform a click action and need some of the variables of the selectionscreen.
SelectionScreen:
class SelectionScreen {
public:
void AddImage(Image img);
std::vector<Image> images;
How I fill the vector:
Image* image = new Image{ };
AddImage(*image2);
AddImage method:
void SelectionScreen::AddImage(Image img)
{
images.push_back(img);
}
But when I try to use the selectionscreen inside the class all of the variables are empty. But when I look into selectionscreen all of the variables are set.
The way I pass the SelectionScreen:
PreviousButton* previousButton = new PreviousButton(*ren, this);
How SelectionScreen gets initialized: (this method is called from the main)
int Program::Render() {
bool quit = false;
MenuScreen m = SelectionScreen{ Sdl_Renderer };
// change current screen to selectionscreen
ScreenController::GetInstance().ChangeMenu(m);
while (!quit) {
// handle user input and repaint
}
// delete all windows if quit is true
SDL_DestroyRenderer(Sdl_Renderer);
SDL_DestroyWindow(Sdl_Window);
SDL_Quit();
return 0;
}
Does anyone knows why my variables are empty?
This isn't addressing the immediate issue you describe, but you have a serious problem here:
ss->currentImageIndex = ss->images.capacity() - 1;
images.capacity() - 1 is not guaranteed to return a valid index into your vector. You should use images.size() - 1.
std::vector::capacity() tells you the storage space currently allocated for the vector, not the number of elements it actually contains.
UPDATE
When you assign a SelectionScreen to a MenuScreen like this
MenuScreen m = SelectionScreen{ Sdl_Renderer };
you "slice" the SelectionScreen object (see What is object slicing?).
This line creates a new MenuScreen object, m, by copy-constructing it from a temporary SelectionScreen object. The new MenuScreen object, m, is not a SelectionScreen. It is also a completely distinct object from the temporary SelectionScreen created on the right-hand side of this expression.
Your PreviousButton, which I assume is created in the constructor of SelectionScreen, holds a pointer to the temporary SelectionScreen, which the compiler is free to delete once this line has completed.
To fix this, you could use this initialisation instead:
MenuScreen& m = SelectionScreen{ Sdl_Renderer };
In this case, m is a reference to your (temporary) SelectionScreen - this is good because (a) references are polymorphic, so it still knows that it's really a SelectionScreen, not just a MenuScreen and (b) it's a reference to the exact same SelectionScreen object created on the right-hand side, which your PreviousButton has a pointer to. I've also bracketed the "temporary" bit now, because taking a local reference to a temporary guarantees it will exist for as long as the reference does (see e.g. Does a const reference prolong the life of a temporary?).
Note that I am assuming here that you only make use of this SelectionScreen within the while (!quit) loop that follows, i.e. the screen can safely be deleted at the end of this int Program::Render() method.

calling a method to change a private variable in c++

I have 3 classes, Player, Monster and Attack.
Class Player
{
public:
void addMonster(string line);
// ...
}
Class Monster
{
public:
void addAttack();
// ...
private:
vector <Attack> _attacks;
}
Now, addMonster() creates a new monster mn, and then has mn call its method addAttack() to fill the vector _attacks
void Player::addMonster(string line){
//...
Monster mn(line); //creates a new monster
while(condition){
mn.addAttack();
}
}
void Monster::addAttack(){
//...
Attack *att = gio->findAttack(ID) //takes the attack from another part of the program
_attacks.push_back(*att);
}
and it seems to work, if I check the contents of _attacks while inside addAttack(), it is pushing the correct elements in, the size of _attacks changes and evreything,
but when the program returns to addMonster(), the _attacks vector of mn is still (or again) empty, as if it had not called the method at all.
Why?
it's as if the object that gets modified is just a copy of the one calling the method, so the original one is not affected, but then how am I supposed to change that vector of that specific object?
When you create the *att pointer in your addAtack you created it on the stack. Stack variables are deleted when their original function ends and so att is deleted at the and of add attack. One possible workaround for this is to create att on the heap, using the keyword new.

Pushing pointers into c++ vectors, and cleanup

I am converting some code between different systems, and I have a question regarding c++ vectors.
If I do something like this:
In header file:
struct Vertex
{
float x;
float y;
float z;
}
struct submesh
{
Vertex *meshdata;
}
std::vector<submesh> meshes;
In a routine in the c++ file:
{
Vertex *data = new Vertex[1024];
submesh g;
g.meshdata = data;
meshes.push_back(g);
delete [] data;
}
Will I be in trouble? My assumption is that the vector would hold a pointer to data that is no longer valid once I called delete on it. Do I need to write a copy constructor for Vertex so that the data is copied first?
Additional:
The question was more to do with how do I put a pointer to allocated memory into a std::vector<> and still cleanup the locally allocated data. Essentially, how do I copy the data into the vector so I can still clean up my copy.
The original code was in DirectX. I am porting it to the iPhone. The original code allocated a submesh locally in a routine using:
{
ID3DXMesh* subMesh = 0;
D3DXCreateMesh(SubGrid::NUM_TRIS, SubGrid::NUM_VERTS, D3DXMESH_MANAGED, elems, gd3dDevice, &subMesh));
//
// ... do some magical things to submesh
//
SubGrid g;
g.mesh = subMesh;
g.box = bndBox;
mSubGrids.push_back(g);
}
I am trying to duplicate how ID3DXMesh is able to be added to a vector, then lose it's scope in the routine.
As I don't have access to D3DXCreateMesh(), I figured I would simply allocate the vertices I needed, throw them into a vector, and clean up.
Sorry, I wanted to keep the nitty gritty details out of it, as the question is simply how do I allocate a chunk of data, put a pointer into a std::vector<>, then clean up the locally allocated memory. :)
I assumed a copy constructor had to be written somewhere. Just wasn't sure where or how.
A subgrid looks like this:
struct SubGrid
{
ID3DXMesh* mesh;
AABB box;
// For sorting.
bool operator<(const SubGrid& rhs)const;
const static int NUM_ROWS = 33;
const static int NUM_COLS = 33;
const static int NUM_TRIS = (NUM_ROWS-1)*(NUM_COLS-1)*2;
const static int NUM_VERTS = NUM_ROWS*NUM_COLS;
};
And the vector they get added to looks like:
std::vector<SubGrid> mSubGrids;
Don't directly dynamicly-allocate when you don't need to, and in this case you don't. Since you're filling your own submesh data rather than using ID3DXMesh, the container of that data should be RAII-compliant. If I were coding this I would remove the submesh class entirely and just use:
// vector containing list of vertices.
typedef std::vector<Vertex> SubMesh;
Your SubGrid class can then become a simple container that holds, as one of its properties, a submesh collection. I noticed you also have a class AABB for a box object. You would continue to keep that inside SubGrid. I don't have ton to work with here, so I'm making some of these up as I go along, but something like the following:
// a simple 3-value triplet of floats
struct Vertex
{
float x,y,z;
};
// a Submesh is an arbitrary collection of Vertex objects.
typedef std::vector<Vertex> SubMesh;
// I'm defining AABB to be an 8-vertex object. your definition
// is likely different, but I needed something to compile with =)
typedef Vertex AABB[8];
class SubGrid
{
public:
SubGrid() {};
// comparator for container ordering
bool operator <(const SubGrid&);
// submesh accessors
void setSubmesh(const SubMesh& mesh) { submesh = mesh;}
SubMesh& getSubmesh() { return submesh; }
const SubMesh& getSubmesh() const { return submesh; }
// box accessors
AABB& getBox() { return box; }
const AABB& getBox() const { return box;}
private:
SubMesh submesh;
AABB box;
};
// arbitrary collection of SubGrid objects
typedef std::vector<SubGrid> SubGrids;
When adding this to your global SubGrid collection g, you have several possibilities. You could just do this:
// declared globally
Subgrids g;
// in some function for adding a subgrid item
SubGrid subgrid;
AABB& box = subgrid.getBox();
SubBesh& submesh = subgrid.getSubmesh();
// ... initialize your box and submesh data ...
g.push_back(subgrid);
But you'd be copying a lot of data around. To tighten up the memory access you could always do this instead:
// push an empty SubGrid first, then set it up in-place
g.push_back(SubGrid());
Subgrid& subgrid = *(g.back());
AABB& box = subgrid.getBox();
SubMesh& submesh = subgrid.getSubmesh();
//... initialize your box and submesh data ...
This will establish a reference to the SubGrid just added to the global collection, then allow you to modify it in-place. This is but-one of a number of possible setup options. It should be noted that if you have C++11 in your toolchain (and if you're doing this on MacOS or iOS, you likely do, as Apple LLVM 4.2's clang is pretty good on C++11 compliance) this can get even more efficient with judicious usage of move-constructors and move-assignment-operators.
Most importantly, not a new or delete to be seen.
Anyway, I hope this gives you some ideas.
Your code looks fine in single threaded application. Your code only allocate data memory once and delete [] data once.
Do I need to write a copy constructor for Vertex so that the data is copied first?
Your code is clean as you shown, meshes points to only allocated data. If you meant to copy data when call meshes.push_back(g), then your code doesn't do what you meant to.
You might want to use std::vector instead:
struct submesh
{
std::vector<Vertex> meshdata;
}
vector<submesh> meshes;
void Func()
{
meshes.emplace_back(submesh());
meshes.at(0).meshdata.resize(100);
}
STL container uses RAII idiom, it manages memory deallocation for you automatically.
Yes of course, vector will have a pointer to deleted memory. What you need is either:
Create copy constructor for submesh (not Vertex).OR
Changesubmesh to have array of Vertex (not just a pointer).
Copy constructor can be done like this:
struct submesh
{
Vertex *meshdata;
unsigned meshsize;
submesh(Vertex* v = 0, unsigned s= 0) : meshdata(v), meshsize(s){}
submesh(const submesh& s)
{
if(meshdata) /*we have stored data, delete it.*/ delete(meshdata);
meshdata = new Vertex[s.meshsize];
meshsize = s.meshsize;
memcpy(meshdata, s.meshdata, sizeof(Vertex) * meshsize);
}
};
For sure it is much recommended to use unique_ptr (if you use c++11) or auto_ptr for old c++. To avoid the nightmare of memory management as much as you can.
Check How to avoid memory leaks when using a vector of pointers to dynamically allocated objects in C++?

Pointer and Array problem

In C++, I'm having trouble with pointers etc. How can I fix the following problem?
error: no match for 'operator=' in '(stage->Stage::tiles + ((unsigned int)(((unsigned int)t) * 12u))) = (operator new(12u), (, ((Tile*))))'|
note: candidates are: Tile& Tile::operator=(const Tile&)|*
stage.h
#include "Tile.h"
class Stage {
public:
Tile *tiles;
int size;
void init(int size);
};
stage.cpp
void Stage::init(int size) {
this->size = size;
this->tiles = new Tile[size];
}
application.cpp
#include "Stage.h"
#include "Tile.h"
bool setTiles( Stage * stage ) {
for( int t = 0; t < stage->size; t++ ) {
stage->tiles[t] = new Tile();
}
return true;
}
stage.init(1234);
setTiles( &stage );
Also, I don't really know when to use object.attribute and when to use object->attribute?
stage->tiles[t] = new Tile();
You're calling new on something that's not a pointer. True, tiles is a pointer to an array, however, each element of that array is NOT a pointer. In order for that work, you would need an array of pointers, or a pointer to a pointer ,such as:
Tile **tiles;
What you could also do is create a separate pointer object, allocate it, and then copy the data to your array element by using
stage->tiles[i] = *somePointer;
and then deleting the pointer afterwards to free that allocated memory. This will preserve the copy because you invoked the copy constructor.
You are trying to allocate a pointer with a pointer to an array. Try this one:
class Stage {
public:
Tile **tiles;
void init(int size);
};
stage->tiles[t] = new Tile();
The above is not a valid C++ code, which you are perhaps confusing with the way new is used in other language such as C#. Though new can be used to allocate dynamic memories, but assigning an object to a particular element in the dynamically created array doesn't need the new construct. In fact, the object is already created as soon as you called new Tile[size]. What you may want to do is, create an object of type Tile and assign it to a particular element in tiles.
Tile myTile;
// do something with myTile
this->tiles[0] = myTile;
new Tiles() returns a pointer to a Tiles instance.
Tile *tiles defines an array out Tiles, not pointers.
Start with Tile **tiles instead.