Can't delete element from vector, attempting to reference deleted function - c++

I am making my "version" of Space Invaders in C++ using SFML library but I have a problem when I try to delete an invader.
I have this error in my code:
Enemy &Enemy::operator =(const Enemy &)': attempting to reference a deleted function
I tried to check other solutions that were recommended in this forum but I either didn't understand them or it was a different case.
EnemyControler.cpp
EnemyControler::EnemyControler()
{
Enemy::Type types[] = {
Enemy::Type::Squid, Enemy::Type::Crab, Enemy::Type::Crab,
Enemy::Type::Octopus, Enemy::Type::Octopus
};
for (int y = 0; y < 5; y++) { // Add enemies to the vector
for (int x = 0; x < 11; x++){
float enemyX = x * 40 + (gapBetweenEnemies * x * 3) + Enemy::enemyWidth; // Make horizontal gap between them
float enemyY = y * 40 + (gapBetweenEnemies * y) + Enemy::enemyHeight; // Make vertical gap between them
enemies.emplace_back(sf::Vector2f{ enemyX, enemyY }, types[y]); // Add them to the vector
}
}
}
void EnemyControler::destroyEnemy()
{
for (auto iterator = begin(enemies); iterator != end(enemies);) {
auto& enemy = *iterator;
if (enemy.isAlive()) {
iterator++;
}
else {
iterator = enemies.erase(iterator);
}
}
}
Problem is in destroyEnemy function. Specifically in iterator = enemies.erase(iterator);
EnemyControler.h
class EnemyControler
{
public:
EnemyControler();
void destroyEnemy();
private:
const int gapBetweenEnemies = 10;
std::vector<Enemy> enemies;
};
Enemy.cpp
Enemy::Enemy(sf::Vector2f startingPosition, Type type) :
Collidable(enemyWidth, enemyHeight), newPosition(startingPosition), enemyType(type), startingPosition(startingPosition)
{
}
Enemy.h
class Enemy : public Collidable
{
public:
enum class Type // enum for different looking enemies
{
Crab, Octopus, Squid
};
Enemy(sf::Vector2f startingPosition, Type type);
constexpr static float enemyWidth = 30.0f;
constexpr static float enemyHeight = 30.0f;
private:
sf::Vector2f newPosition;
Type enemyType;
const sf::Vector2f startingPosition;
};
Collidable.cpp
Collidable::Collidable(float width, float height) :
spriteSize(width, height)
{
}
Collidable.h
class Collidable
{
public:
Collidable(float width, float height);
private:
sf::Vector2f spriteSize;
};
If there isn't an easy way of fixing this problem or this problem could be fixed only by rewriting all code maybe you could suggest another way of deleting invader from the vector.

If a class has a const member variable, then the copy assignment operator for that class operator= is default-deleted by the compiler.
From https://en.cppreference.com/w/cpp/language/copy_assignment :
A defaulted copy assignment operator for class T is defined as deleted if any of the following is true:
- T has a non-static data member of non-class type (or array thereof) that is const;
This is because the compiler can't guess what it means to copy assign object A into object B, if objects of their class contain a const member variable. Should the compiler just ignore that particular member variable? Should the compiler guess that you mean it to be const, except when copy assigning, then it's okay just this once - I'll look the other way? The compiler has no idea what you would prefer, so it just deletes it.
One option is to explicitly define a copy assignment operator for your class.
However, startingPosition is already declared private, so there's little chance of anything outside of the class inadvertently changing it. I recommend just removing the const specifier.
Why does the copy assignment operator matter? I'm trying to delete things, not copy assign them
When an element is erased from a vector, all elements in the vector "above" the erased element need to be moved down to fill the gap. This happens via the copy assignment operator.
From https://en.cppreference.com/w/cpp/container/vector/erase :
Complexity
Linear: the number of calls to the destructor of T is the same as the number of elements erased, the assignment operator of T is called the number of times equal to the number of elements in the vector after the erased elements

Related

occur no matching function for call to error in c++

I'm a novice in c++, i know there is a lot of similar questions but unfortunately didn't help me to solve this problem (which is a conceptual misunderstood i think)
So i have this constructor
Field::Field(const Position &posG, const Position &posC) {
//...
}
And i am creating a Field as follows
// Create positions
Position posG, posC;
posG.x = 3;
posG.y = 3;
posC.x = 4;
posC.y = 4;
// Create pointers to positions
const Position *pPosC(&posC);
const Position *pPosG(&posG);
// Create field
Field field (pPosG, pPosC);
Where position is
struct Position {
int x;
int y;
};
Then i am getting this exception:
main.cpp:27:30: error: no matching function for call to ‘Field::Field(const Position*&, const Position*&)’
Field Field (pPosG, pPosC);
In file included from main.cpp:2:0:
Any help? Regards
Field(const Position &posG, const Position &posC);
^^^^^ ^^^^^^
Those are references. So when you try to pass pointers
Field field (pPosG, pPosC);
^^^^^ ^^^^
pointer pointer
It can't compile.
Either make the constructor accept reference pointers (const Position *&posG) or pass the value of the pointers (*pPosG), or just pass the values directly (posG).
When you have a constructor defined as
Field(const Position &posG, const Posicion &posC);
You can use objects of type Position as arguments to it, not pointers to Position.
You can use:
Field field(posG, posC);
or
Field field(*pPosG, *pPosC);
Your constructor expects references, not pointers.
As a sidenote, it's not clear why you use references or pointers.
you must define a constructor, then use of const Position *pPosC(&posC); expression.
you must define constructor-copy like this :
struct Posicion {
public :
Position(const Position &pos) // constructor copy
{
// some code here
x = pos.x;
y = pos.y;
}
int x;
int y;
};

Have const variables in an object which is used in a vector container

So I am trying to make snake clone.
I have made a BodyPiece class which has certain variables I would like to be const, as they should not change. This was not a problem when I simply created an instance of Bodypiece to test out. However I want to be able to add pieces as the snake grows so I made a vector body variable. As you can see from the code below:
In BodyPiece.h:
#pragma once
#include "D3DGraphics.h"
#include "Keyboard.h"
#include "Direction.h"
class BodyPiece
{
public:
BodyPiece(int xPos, int yPos, Direction* dir);
~BodyPiece();
void Draw(D3DGraphics& gfx);
void UpdateDirection(KeyboardClient& kbd);
void Move();
private:
Direction* dir;
int xPos;
int yPos;
int width = 5;
int height = 5;
int vx = 5;
int vy = 5;
};
In Game.h file
BodyPiece vector body declared:
std::vector<BodyPiece> body;
In Game.cpp file
vector initialised in initialiser list.
body({ {400, 300, &d } })
If I make any variables in BodyPiece const, it will generate an error:
error C2582 'operator=' function is unavailable in class BodyPiece.
My questions here are:
What causes that? I have looked online and have a little bit of an idea but it really is only a little bit.
How can I have const variables, use a vector container(or another type of container if that will help, does not have to be vector) and keep the compiler happy? Or should I just completely change my approach?
Thank you in advance.
The problem is that if you don't have an assignment operator, then the compiler will generate one for you, that does simple shallow copying of the members. However, if you have actual const member variables (and not only default-initialized like yours) then those can't be copied and the compiler can't create an assignment operator.
The simple solution is to make the constants actual constants and also static so they are members of the class and not part of a single object:
class BodyPiece
{
...
static int const width = 5;
static int const height = 5;
};
The only drawback with this is that you need to actually define these member variables, in a single source file:
int const BodyPiece::width;
int const BodyPiece::height;
The other solution is to explicitly create an assignment operator that copies the data needed:
class BodyPiece
{
public:
...
BodyPiece& operator=(BodyPiece const& other)
{
// Copy data from other to this
xPos = other.xPos;
...
return *this;
}
...
};

Copying a vector of structs containing unique_ptrs

I'm working on a simple game using C++ and Allegro. I am running into an Access violation runtime error regarding a vector of structs that contain unique_ptrs to ALLEGRO_BITMAPs.
Here is my struct declaration.
struct Skin {
std::unique_ptr<ALLEGRO_BITMAP> img;
Skin();
Skin(ALLEGRO_BITMAP*);
Skin& operator=(const Skin& s);
Skin(const Skin& s);
};
And here are the definitions of the constructors in another file.
Skin::Skin() {
img.reset();
}
Skin::Skin(ALLEGRO_BITMAP* bitmap) {
img.reset(bitmap);
}
Skin::Skin(const Skin& s) {
img.reset(s.img.get());
}
Skin& Skin::operator=(const Skin& s) {
img.reset(s.img.get());
return *this;
}
Here is the code that gets called before my access violation.
generateBase(world, display.get());
Which calls this function.
void generateBase(World& world, ALLEGRO_DISPLAY* display) {
int x = TILESIZE - WIDTH;
int y = HEIGHT - TILESIZE;
int groundWidth = 3 * WIDTH - 2 * TILESIZE;
Point min{ x, y };
Point max{ x + groundWidth, y + (int)TILESIZE };
ALLEGRO_BITMAP* black = al_create_bitmap(groundWidth, TILESIZE);
ALLEGRO_BITMAP* white = al_create_bitmap(groundWidth, TILESIZE);
al_set_target_bitmap(black);
al_clear_to_color(al_map_rgb(0, 0, 0));
al_set_target_bitmap(white);
al_clear_to_color(al_map_rgb(255, 255, 255));
al_set_target_bitmap(al_get_backbuffer(display));
std::cout << "Errors incoming!" << endl;
createPlayer(world, x, y, 0, 0, 5, vector < AABB > { AABB(min, max) }, vector < Skin > { Skin(black), Skin(white) });
std::cout << "Did we make it?" << endl;
}
Which in turn calls this function.
unsigned int createPlayer(World& world, int x, int y, float dx, float dy, float speed, vector<AABB>& mesh, vector<Skin>& imgs) {
unsigned int entity = newEntityIndex(world);
world.masks[entity].set(COMPONENT_TYPE);
world.masks[entity].set(COMPONENT_POINT);
world.masks[entity].set(COMPONENT_UNITVECTOR);
world.masks[entity].set(COMPONENT_SPEED);
world.masks[entity].set(COMPONENT_COLLISIONMESH);
world.masks[entity].set(COMPONENT_SKINLIST);
world.types[entity] = TYPE_PLAYER;
world.points[entity] = Point(x, y);
world.unitVectors[entity] = UnitVector(dx, dy);
world.speeds[entity] = Speed(speed);
world.collisionMeshes[entity].mesh = mesh;
cout << "Starting vector copy" << endl;
for (auto skin : imgs) {
world.skinLists[entity].imgs.push_back(move(skin));
}
cout << "Ending vector copy" << endl;
return entity;
}
Here is my deleter for unique_ptr.
namespace std {
template<>
class default_delete < ALLEGRO_BITMAP > {
public:
void operator()(ALLEGRO_BITMAP* ptr) {
cout << ptr << endl;
al_destroy_bitmap(ptr);
}
};
}
Here is the output.
Errors incoming!
Starting vector copy
00AF9468
00AF9468
When I modified my createPlayer call in generateBase by removing Skin(white), the output changed to.
Errors incoming!
Starting vector copy
00799468
Ending vector copy
00799468
The change in output had me a bit puzzled, but my biggest question is what do I need to change about how I copy my vector of structs of unique_ptrs so that I don't try to delete the same pointer twice.
Thanks in advance!
The first thing to understand is you can only have one std::unique_ptr object containing a pointer to a particular object. Your Skin(const Skin& s) constructor violates this principle, resulting in two copies of a unique_ptr. If you have an object containing unique_ptr members, you'll need to do one of the following:
Not have a copy constructor or assignment operator.
In the copy constructor and/or assignment operator, allocate a new copy of the underlying resource. This would require calling al_clone_bitmap to duplicate the resource.
Second, when you are holding a resource in a unique_ptr, you want to initialize the unique_ptr at the same location where you create the resource. For example, instead of creating a local variable ALLEGRO_BITMAP* black, use the following:
std::unique_ptr<ALLEGRO_BITMAP> black(al_create_bitmap(groundWidth, TILESIZE));
Since this code is creating the unique_ptr directly from the result of al_create_bitmap, you'll want to remove the Skin constructor that takes an ALLEGRO_BITMAP* and replace it with this:
Skin::Skin(std::unique_ptr<ALLEGRO_BITMAP>&& bitmap)
: img(bitmap)
{
}
You can then create a Skin by moving your unique_ptr into it:
Skin(std::move(black))
Putting the above together, a working copy constructor might look like the following. It's not particularly efficient, but it's safe.
Skin::Skin(const Skin& s)
: img(al_clone_bitmap(s.img.get()))
{
}
The problem is here:
Skin::Skin(const Skin& s) {
img.reset(s.img.get());
}
Skin& Skin::operator=(const Skin& s) {
img.reset(s.img.get());
You are stealing the raw pointer from one unique_ptr and assigning it to another one. Now unique_ptr belongs to the RAII category. They expect to own the lifetime of an object as long as they are alive.
When you do this
img.reset(s.img.get());
You took out the pointer from one unique_ptr and handed over to the other unique_ptr. Now unique_ptr1 believes that it own the underlying object unaware that there is another unique_ptr2 that believes the same. So when they die they will happily free the memory allocated for _Ptr. So your code is bound to end up accessing/freeing memory which has already being freed by the first unique_ptr which dies.
You must either move the unique_ptr (thereby yielding ownership) or explicitly calling release on s.img

initialize array of structs

I have a class like this:
class Wall
{
private :
Quad faces[6];
};
I have the constructor like this :
Wall::Wall(Quad f[], const float &mass, Vector3 center)
I want to initialize faces to be f(or copy f to faces),Quad is struct that doesn't have a default constructor.
Now I solved the problem by using faces{f[0],f[1],f[2],f[3],f[4],f[5]} in the initializer list but this requires c++11 which I'm afraid some of my friends don't have it, and I need to pass my code to them.
There are many similar questions but all of them seem to not have solutions other than switching to vector or using some complicated code which I don't want, as you can understand from the classes' name, a Wall doesn't need a vector(it only has 6 faces so why a vector).
Is this really hopeless ? isn't there any way ?
PS
Whether in the constructor body or in the initializer list, it doesn't matter.
changing to dynamic arrays(Quad *) doesn't matter either but keeping with static arrays is preferable.
Several options. The easiest is probably to subclass Quad with something that has a default constructor:
class Wall {
public:
Wall(Quad f[], ...) {
for (int i = 0; i < 6; ++i) faces[i] = f[i];
}
private:
class MyQuad : public Quad {
MyQuad() : Quad(...) {}
}
MyQuad faces[6];
};
Another option is to use placement new - note that the code below doesn't work out of the box since it is not doing proper alignment/padding and dealing with some aliasing issues, which are left as an exercise to the reader. It should give you a starting point though.
class Wall {
public:
Wall(Quad f[], ...) {
for (int i = 0; i < 6; i++) {
// TODO: take padding into account
new (&faces_data + sizeof(Quad) * i) Quad(f[i]);
}
}
~Wall() {
for (int i = 0; i < 6; i++) {
face(i).~Quad();
}
}
Quad& face(int idx) {
// TODO: take padding into account
return (reinterpret_cast<Quad*>(faces_data))[idx];
}
private:
// TODO: force proper alignment and take padding into account
char faces_data[sizeof(Quad) * 6];
};

C++ Class initialized

C++ and Class
I have a class called “Sprite”, when this is initialized it takes a bool variable:
// constructor
Sprite::Sprite(bool type)
{
// set stuff
}
// two Sprites of different types
Sprite Hero(1)
Sprite Enemy(0)
Q. How do I initialize an array of 100 Sprites of type 0?
Sprite Enemies[100] ?
My suggestion is that you use a std::vector, and then use the constructor taking a value argument.
Like
std::vector<Sprite> Enemies(100, Sprite(false));
You might need proper copy-constructor and copy-assignment operators defined for Sprite for it to work.
If you don't have vectors (or std::array which might be better in your case), then you have to declare the array as a normal array, and then use a loop to initialize each entry:
Sprite Enemies[100];
for (size_t i = 0; i < 100; ++i)
Enemies[i].set(false);
You can use a default constructor, that will simply default to the 0 value like this:
//default constructor
Sprite::Sprite()
{
//set to false
}
Sprite::Sprite(bool type)
{
// set to type
}
Now
Sprite Enemies[100]
will work
Or with a little less code use a default value in the constructor like so:
Sprite::Sprite(bool type=false)
{
//set to type
{
Unless I'm wrong, you cannot directly use constructor with parameters for object arrays. One solution is using a std::vector :
std::vector<Sprite> Ennemies(100, Sprite(false));
std::vector<Sprite> Ennemies(100, {false}); // C++11 style
If you really want C-style array, you can get it, by an example :
Sprite* pEnnemies = &Ennemies.front();
One other solution is using the new C++11 container std::array which is only a C-Style array in STL syntax :
std::array<Sprite, 100> Ennemies(Sprite(false));
When creating an array of classes, they must be created by the default constructor.
You could add a default value "= false" for your type parameter, and then the code would work. It's not very flexible though, as you might want an array of the other type.
Another way is to allow your sprite to be reset after construction with a different type. After creating your array of blank sprites, call reset(type) on them in a for loop.
If you really need to use a non-default constructor on your elements, use std::vector. There are two ways to do it.
std::vector<Sprite> Enemies;
Enemies.reserve(100);
for (int i = 0; i < 100; i++)
{
Enemies.push_back(Sprite(0));
}
or
std::vector<Sprite> Enemies(100, Sprite(0));
For the sake of completeness, there is one last method using placement-new:
unsigned char data[sizeof(Sprite) * 100];
Sprite* ptr = reinterpret_cast<Sprite*>(&data[0]);
Sprite* end = ptr + 100;
for (; ptr != end; ++ptr)
new (ptr) Sprite(0);
This is pretty much what the other answers, that don't rely on the copy-constructor use internally.
If Sprite has a non-trivial destructor you will have to call it explicitly using another loop at the end of data's lifetime:
Sprite* ptr = reinterpret_cast<Sprite*>(&data[0]);
Sprite* end = ptr + 100;
for (; ptr != end; ++ptr)
ptr->~Sprite();
Many thanks all for taking a look, after reading comments I've found this works, overloading the constructor:
class Sprite
{
public:
Sprite();
Sprite(bool type);
void Move();
private:
unsigned int x, y, Ammo;
bool Alive;
bool Type;
};
// constructor
Sprite::Sprite()
{
Alive = true;
Type = 0;
Ammo = 25;
x = random(0, 82);
y = random(0, 20);
}
Sprite::Sprite(bool type)
{
Alive = true;
Type = 1;
Ammo = 25;
x = 20; // get x from btn press
y = 10; // get y from btn press
}
Sprite Hero(1);
Sprite Enemies[100];
Use a default argument:
Sprite::Sprite(bool type=false) : mymember(type)
{}
and then when you declare:
Sprite Enemies[100];
it will call the default ctor for all 100 elements.
You should learn about member-initializer lists, the proper way to write a constructor in C++ is:
Sprite(bool type=false); // declaration
...
Sprite::Sprite(bool type) : // definition
x(random(0, 82)),
y(random(0, 20)),
Ammo(25),
Alive(true),
Type(type)
{}