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)
{}
Related
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
I want to create a constant (preferably static but not necessary) member variable in my class.
I want it to be a 3-dimensional array with each length size 2.
The purpose: store some data that is time consuming to recreate on each change, for combinations of 3 types of boolean choices, without having to do complicated testing on each change.
What I don't know how to do: how to initialize the 3D array.
This is what I am trying (based on cplusplus.com/forum/Multi-Dimensional Arrays):
class MyClass {
public: ...
~MyClass(); // will I need to destroy m_previewIcons to prevent memory leak ?
private: ...
static const QIcon m_previewIcons[2][2][2]; // the array I need
static QIcon*** initializePreviewIcons(); // what type of return ?
};
const QIcon MyClass::m_previewIcons[2][2][2] = MyClass::initializePreviewIcons();
QIcon ***MyClass ::initializePreviewIcons()
{
QIcon ***iconArray = 0;
// Allocate memory
iconArray = new QIcon**[2];
for (int i = 0; i < 2; ++i)
{
iconArray[i] = new QIcon*[2];
for (int j = 0; j < 2; ++j)
iconArray[i][j] = new QIcon[2];
// is this even right ? it seems to me I miss out on a dimension ?
}
// Assign values
iconArray[0][0][0] = QIcon(":/image1.png");
iconArray[0][0][1] = QIcon(":/image2.png"); ...
iconArray[1][1][1] = QIcon(":/image8.png");
return iconArray;
}
As far as I got...
error: conversion from 'QIcon***' to non-scalar type 'QIcon' requested
How can I get this initialization to work ?
Note - QIcon is a built-in class in Qt, which is what I use (any class would be the same).
No C++ 11 though.
I could have used vectors I suppose but I wanted less overhead.
Edit: I have just thought of an alternate way to do it... give up on the 3D array, use simple 1D array and build an int for index using the booleans bit shifted. may be more effective.
But I would still want to know how to initialize a 3D array.
You're creating a a static array, and then trying to allocate its memory dynamically, which isn't necessary - the memory is already there by virtue of your declaration static const QIcon m_previewIcons[2][2][2];
You should initialize your 3d array using list initialization, a la this answer.
Heres an example with a non-POD type, std::string:
#include <string>
class MyClass {
public:
static const std::string m_previewIcons[2][2][2];
};
const std::string MyClass::m_previewIcons[2][2][2] = {
{ {":/image1.png",":/image2.png"},
{":/image3.png",":/image4.png"} },
{ {":/image5.png",":/image6.png"},
{":/image7.png",":/image8.png"} }
};
int main()
{
MyClass mc;
printf("%s\n", mc.m_previewIcons[0][0][0].c_str());
}
The problem is that C++ provides no class initialization: only static initializers or instance constructor.
But here, it is easy, because the construction of the elements of the array is straightforward, so you could use:
class MyClass {
public: ...
~MyClass(); // will I need to destroy m_previewIcons to prevent memory leak ?
private: ...
static const QIcon m_previewIcons[2][2][2]; // the array I need
};
const QIcon MyClass::m_previewIcons[2][2][2] = {
QIcon(":/image1.png"),
QIcon(":/image2.png"),
...,
QIcon(":/image8.png") };
As m_previewIcons has static linkage, its duration is the whole program so you have neither to provide storage for it nor to release it. Said differently, do not destroy it from the destructor.
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];
};
How do I initialize an array of objects from a function? I'm aware the code below is impractical; I'm just teaching myself C++.
Here is a structure that contains data.
struct pointStruct {
int numberPoints;
Point2D pointArray;
};
The Point2D class has instance variables x and y. In a separate function, I have:
void setPoints(void) {
pointStruct myPointData;
myPointData.numberPoints = 4;
myPointData.pointArray[4]; // here is the problem
// loop with i
myPointData.pointArray[i].x = ...;
myPointData.pointArray[i].y = ...;
}
I'm trying to initialize the array so that I can loop through it and set the x,y coordinates. I've tried using new and some other methods but I can't work through what I need to do. How can I fix this?
When I try to compile this code, I get the error "no match for 'operator[]' in 'myPointData.pointStruct::pointArray[4]' "
You should probably use std::vector like MadScienceDreams suggests.
However, if you want to learn about such things, you could use a pointer instead. For example:
struct pointStruct {
int numberPoints;
Point2D* pointArray;
};
void setPoints(void) {
pointStruct myPointData;
const int num_points = 4;
myPointData.numberPoints = num_points;
myPointData.pointArray = new Point2D[num_points];
for(int i = 0; i < num_points; ++i) {
myPointData.pointArray[i].x = ...;
myPointData.pointArray[i].y = ...;
}
// Do stuff with myPointData...
// Don't forget to have a "delete" for every "new" when you're done.
delete[] myPointData.pointArray;
}
Point2D pointArray;
pointArray is a single instance to Point2D. It is not an array of instances in which case it's type is Point2D [N].
myPointData.pointArray[4];
The above statement calls operator [] taking a parameter of type int, which is not you actually want. Since there is no such member function in Point2D, compiler complains. If you wish to create array of instances, use std::vector<Point2D>.
class Zombie
{
public:
Zombie();
Zombie(int howTall);
Zombie(int howTall, int howHeavy);
int getHeight();
private:
int height;
int weight;
};
Zombie::Zombie()
{
height = 6;
weight = 180;
}
int main()
{
Zombie army[4];
for(int i = 0; i < 4; i++)
army[i] = Zombie;
}
Why do I get the error when I try to set army[i] = Zombie? Army is an array of Zombies and I already wrote a default constructor for the Zombie class. when I replace Zombie with Zombie(), the code works, but shouldn't omitting the () call the default constructor as well?
Not shown: the other constructors and methods are implemented already.
I know that if I declare army to be an array of pointers to Zombies and do army[i] = new Zombie it will work, but I don't know why the above code doesn't.
Thanks
When you do:
Zombie army[4];
You are already constructing 4 Zombies of height 6 and weight 180. The default constructor is being called 4 times (try to add a call to std::cout in it and you'll see!).
Therefore what you are attempting to do in your loop is not needed, unless you want (for whatever reason), to construct new zombies again. In which case, the proper syntax would be:
army[i] = Zombie();
Because you're trying to assign a type and not an object of that type.
Besides that you have already the objects in army, you would only need to assign a new object now, when you want to overwrite the values with 'new Zombies'