So I'm working on a school project right now in C++, although I'm not too familiar with the language yet. The whole project is divided in several Milestones. 1: Reading a list with different Types of Creatures and storing them in a vector 2: Reading a TGA-File and storing it in a class. ... 5: Reading a TGA-Picture for every read Creature-Type and storing it for further use. (Printing on GUI, Remove/add)
So I thought it is a good idea to store the picture for each type in the class itself, as it should only be loaded once. The load() function in my TGAPicture class will return std::unique_ptr so I added the type as an argument in my CreatureType class. After doing that, I got several error like this:
Error C2280 'biosim::CreatureType::CreatureType(const biosim::CreatureType &)': attempting to reference a deleted function bio-sim-qt E:\Development\C++\bio-sim-qt\bio-sim-qt\qtmain.cpp 58 1
Error (active) function "biosim::CreatureType::CreatureType(const biosim::CreatureType &)" (declared implicitly) cannot be referenced -- it is a deleted function bio-sim-qt e:\Development\C++\bio-sim-qt\bio-sim-qt\Model.cpp 15 26
So I read about 10 questions with similar titles like mine and every one pointed out, that you cant copy unique_ptr and suggested solutions like using std::move() or returning a reference. Although I tried to use these to fix my problem, I wasn't able to do it in the slightest, probably because I'm pretty new to C++ and have never worked with unique pointers.
This is the code, that seems relevant to me:
/**
* @class CreatureType
* Object of the various CreatureTypes ingame
*/
class CreatureType {
private:
std::string name;
int strengh;
int speed;
int lifespan;
std::vector<std::string> attributes;
std::string path;
std::unique_ptr<TGAPicture> picture; //What I tried to add in order to stre my pictures
public:
CreatureType(const std::string& name
, int strengh, int speed, int lifespan
, const std::vector<std::string>& basic_strings
, const std::string& path);
/**
* Initializes list with CreatureTypes by reading from a .txt-File
*/
static CreatureList load(const std::string& file);
/**
* Printing Data in various ways
*/
void getInfo() const;
void getInfoInOneLine() const;
std::string getName() const;
int getStrengh() const;
int getSpeed() const;
int getLifespan() const;
std::vector<std::string> getAttributes() const;
std::string getPath() const;
};
}
CreatureType::CreatureType(const std::string& name
, int strengh, int speed, int lifespan
, const std::vector<std::string>& basic_strings
, const std::string& path)
: name(name),
strengh(strengh),
speed(speed),
lifespan(lifespan),
attributes(basic_strings),
path(path),
picture(TGAPicture::loadPicture(Reference::PicturePath::creatureBasePath + path)){ }
/**
* Implementation Notes:
* - Does a line not fullfill the requirenments, it will be ignored
* - @see <a href="https://elearning.uni-bayreuth.de/pluginfile.php/644828/mod_resource/content/2/Aufgabenblatt_1.pdf">Formation</a>
* - Prints data with std::cout
*/
CreatureList CreatureType::load(const std::string& file) {
CreatureList creatureList;
std::ifstream fileStream; //Datei-Handle
int lineNumber = 0;
int correctLinesRead = 0;
fileStream.open(file, std::ios::in);
if (!fileStream.is_open()) {
throw FileNotFoundException(file);
}
logger << INFO << "Einlesevorgang wird gestartet\n";
//One line per loop
while (!fileStream.eof()) {
bool skipLine = false;
std::string line;
getline(fileStream, line);
lineNumber++;
... //Checking if data is valid
//Every Parameter does exist and is valid
creatureList.push_back(CreatureType(creatureArgs[0]
, strengh, speed, lifespan
, attributes, creatureArgs[5]));
correctLinesRead++;
}
return creatureList;
}
TGAPicture:
//no padding bytes
#pragma pack( push, 1 )
/**
* @struct TGAHeader
* Represents the standard TGA-Header.
*/
struct TGAHeader {
char idLength;
char colourmapType;
char imagetype;
short colourmapStart;
short colourmapLength;
char colourmapBits;
short xOrigin;
short yOrigin;
short width;
short height;
char bits;
char descriptor;
};
#pragma pack( pop )
/**
* @struct RGBA
* Represents a Pixel with a red, green, blue and possibly alpha value
*/
struct RGBA {
std::uint8_t B, G, R, A;
};
/**
* @class TGAPicture
* Class used to represent TGA-Files, that are used in the program
*/
class TGAPicture {
public:
TGAPicture(const TGAPicture& other)
: pixel(other.pixel),
header(other.header),
width(other.width),
height(other.height),
size(other.size),
bitsPerPixel(other.bitsPerPixel) {}
TGAPicture(TGAPicture&& other) noexcept
: pixel(std::move(other.pixel)),
header(std::move(other.header)),
width(other.width),
height(other.height),
size(other.size),
bitsPerPixel(other.bitsPerPixel) {}
TGAPicture& operator=(const TGAPicture& other) {
if (this == &other)
return *this;
pixel = other.pixel;
header = other.header;
width = other.width;
height = other.height;
size = other.size;
bitsPerPixel = other.bitsPerPixel;
return *this;
}
TGAPicture& operator=(TGAPicture&& other) noexcept {
if (this == &other)
return *this;
pixel = std::move(other.pixel);
header = std::move(other.header);
width = other.width;
height = other.height;
size = other.size;
bitsPerPixel = other.bitsPerPixel;
return *this;
}
private:
std::vector<RGBA> pixel; //Containes every pixel of the picture
TGAHeader header;
short width, height, size, bitsPerPixel;
...
public:
/**
* Loads and initializes a picture to be used in the program
* @throws TGAExpection if file could not be loaded
*/
static std::unique_ptr<TGAPicture> loadPicture(const std::string& path);
TGAPicture(const std::vector<RGBA>& pixel, const TGAHeader& header);
~TGAPicture();
....
};
}
#endif
cpp:
TGAPicture::TGAPicture(const std::vector<RGBA>& pixel, const TGAHeader& header)
: pixel(pixel),
header(header),
width(header.width),
height(header.height),
size(header.width * header.height * (header.bits / 8)),
bitsPerPixel(header.bits) { }
std::unique_ptr<TGAPicture> TGAPicture::loadPicture(const std::string& path) {
...
for (int i = 0; i < header.height * header.width; i++) {
pixel[i].B = *(bufferPosition++);
pixel[i].G = *(bufferPosition++);
pixel[i].R = *(bufferPosition++);
pixel[i].A = (header.bits > 24 ? *(bufferPosition++) : 0xFF);
}
/**
* Return unique_ptr
* - ObjectFactory
* - Automatic Deletion
*/
return std::unique_ptr<TGAPicture>{new TGAPicture(pixel, header)};
}
And one class with an error would be:
class Model {
public:
explicit Model(const CreatureList& creatureList);
~Model();
Terrain* getTerrain() const;
CreatureList& getCreatureList();
private:
CreatureList creatureList;
Terrain* terrain;
};
Model::Model(const CreatureList& creatureList) : creatureList(creatureList),
terrain(new Terrain()) {
for (CreatureType ty : creatureList) { //line with errror
ty.getInfoInOneLine();
}
}
What do I need to change for it to work? And what would be the optimal way? Pretty sure that I'm supposed to use unique_ptr as return for the TGA::load() method. I hope you can see through this mess and I'd like to apologize if my English isn't perfect, since it's not my first langugage.
std::unique_ptr
is not copyable. It wouldn't be unique anymore if you could copy it.
You create copies of the elements in creatureList
in your loop in Model
's constructor, but they have a non-copyable member, and so are non-copyable themselves by default. If you don't actually need a copy of the elements, you should use references:
Model::Model(const CreatureList& creatureList)
: creatureList(creatureList),
terrain(new Terrain())
{
for (CreatureType& ty : creatureList) { // changed to reference instead
// Note: this is still working with
// parameter, not the object's
// member.
ty.getInfoInOneLine();
}
}
You haven't actually provided a definition for CreatureList
, but I suspect it also isn't copyable. This means that Model::Model
's argument can't be copied into the object's member. You have two options to fix this: make sure to move your CreatureList
or make it copyable.
std::unique_ptr
is movable, which means that CreatureType
is by default as well, so you can do something like this:
Model::Model(CreatureList creatureList) // Take by value now
: creatureList(std::move(creatureList)), // Move the parameter to the member
terrain(new Terrain())
{
for (CreatureType& ty : this->creatureList) { // Use this-> to access member
// instead of now moved-from
// parameter. You could also
// just change them to have
// different names.
ty.getInfoInOneLine();
}
}
This changes Model
's constructor to take its parameter by value and moves that value into the object's creatureList
member.
If it makes sense, you could also make CreatureType
copyable by adding an explicit copy constructor that copys the object pointed to by picture
:
CreatureType::CreatureType(const CreatureType& other)
: name(other.name),
strengh(other.strengh),
speed(other.speed),
lifespan(other.lifespan),
attributes(other.attributes),
path(other.path),
picture(new TGAPicture(*other.picture))
{
}
If you do that, the implicit move constructor will no longer be generated by the compiler, so you'll want to define that yourself:
CreatureType::CreatureType(CreatureType&& other)
: name(std::move(other.name)),
strengh(other.strengh),
speed(other.speed),
lifespan(other.lifespan),
attributes(std::move(other.attributes)),
path(std::move(other.path)),
picture(std::move(other.picture))
{
}
There doesn't seem to be any reason for TGAPicture::loadPicture
to return a std::unique_ptr
though. If you just return by value from that function you will avoid all of these problems:
TGAPicture TGAPicture::loadPicture(const std::string& path) {
// ...
return TGAPicture{pixel, header};
}