I want to define a const variable which is part of a class like:
// camera.h
class Camera{
public:
Camera(std::string file);
~Camera() {}
const size_t FRAME_HEIGHT;
const size_t FRAME_WIDTH;
private:
std::string file;
cv::VideoCapture cap;
Read();
};
_____________________________________
// camera.cpp
Camera::Camera(std::string file) : file("settings.yml") {
//...
read();
cap.open(0);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, FRAME_HEIGHT);
cap.set(CV_CAP_PROP_FRAME_WIDTH, FRAME_WIDTH);
}
void Camera::read(){
// read something
}
However this does not work using an initialisation list because I first have to read this data from a settings file.
After calling read() I set my const variables FRAME_HEIGHT and FRAME_WIDTH.
Is it possible to leave them const and if yes how/where should I do that?
It is possible if you use an intermediate class, like this:
class Camera {
struct Reader {
int value;
Reader(bool x) {
// Do whatever you like
value = x ? 42 : 0xDEAD;
}
};
Camera(Reader&& r) : value(::std::move(r.value)) { }
public:
int const value;
template<typename... V>
Camera(V... args)
: Camera(Reader(std::forward<V>(args)...))
{ }
};
This effectively adds another stage to the initialization, but still keeps encapsulation (Reader and the corresponding constructor are private to Camera!). The performance impact should be negligible (forward and move operations are usually cheap), unless you initialize millions of these objects in some tight inner loop.
One option is to initialize a settings object from a file, and take needed values from it:
class Camera
{
// note this section is moved to the beginning,
// so file and settings are initialized before FRAME_HEIGHT and FRAME_WIDTH
private:
std::string file;
Settings settings;
public:
Camera(std::string file)
: file(file)
, settings(file)
, FRAME_HEIGHT(settings.getFrameHeight())
, FRAME_WIDTH(settings.getFrameWidth())
{
}
~Camera() {}
const size_t FRAME_HEIGHT;
const size_t FRAME_WIDTH;
};
Also it could make sense to rewrite Camera constructor after that as
Camera(const Settings& settings)
: FRAME_HEIGHT(settings.getFrameHeight())
, FRAME_WIDTH(settings.getFrameWidth())
{
}
-- this way you can take settings from anywhere, not only from a file.
Related
I work on a project made with cocos2d-x framework (c++).
In my Player class, I have to manage the animations.
Iinitially I had this code that worked without any problem:
First, the animation object is a cocos2d Class cocos2d::Animation. Just remember that this object contains a cocos2d::Vector<AnimationFrame*> _frames; member.
Doc: http://www.cocos2d-x.org/reference/native-cpp/V3.5/d3/dc5/classcocos2d_1_1_animation.html#a0fdc0be158df7e09d04644be353db056
class Player : public cocos2d::Sprite {
private:
cocos2d::Map<std::string, cocos2d::Animation*> animations;
cocos2d::Vector<cocos2d::SpriteFrame*> getAnimation(const char *format, int frameStart, int count);
void update(float delta) override;
bool init() override;
public:
static Player* create();
bool init() override;
//...
};
And the implementation side:
bool Player::init() {
//...
animations.insert("idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1));
//...
}
Vector<SpriteFrame*> Player::getAnimation(const char *format, int frameStart, int count) {
auto spriteCache = SpriteFrameCache::getInstance();
Vector<SpriteFrame*> animFrames;
char str[100] = {0};
for (int i = 1; i <= count; i++)
{
sprintf(str, format, frameStart);
log("%s", str);
animFrames.pushBack(spriteCache->getSpriteFrameByName(str));
frameStart++;
}
return animFrames;
}
//later in the code execution
void Player::manageIdle() {
auto idleAnim = Animate::create(animations[0].anim);
runAction(idleAnim);
}
You can see each Animation is contained in cocos2d::Map<std::string, cocos2d::Animation*> and as I say before, this code worked perfectly, no error.
But I needed some more informations in addition to the name and the object itself so I decided to use a structure to store all infos for each animation. And I replaced the cocos2d::Map<std::string, cocos2d::Animation*> by std::vector<animData> with animData as structure. I refactored the code like so:
class Player : public cocos2d::Sprite {
public:
typedef struct animation {
std::string name;
cocos2d::Animation* anim;
//all others info needed, not relevant here, (just several enum type variables)
} animData;
private:
std::vector<animData > animations; //the new container
//rest of code stay unchanged
};
The changes in the implementation side:
bool Player::init() {
//...
animations.push_back({"idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1)});
//no problem here...
}
But now, when I try to create a new anim with a animation saved in my container (vector) I get a SegV on this line:
void Player::manageIdle() {
auto idleAnim = Animate::create(animations[0].anim); //SegV here, in Animate::create() funct
runAction(idleAnim);
}
After search, I find that each structure member anim which is type of cocos2d::Animation*, now conatains a empty cocos2d::Vector<AnimationFrame*> _frames; and there is the problem !
It’s as if they lose the cocos2d::Vector<AnimationFrame*> ref or something like that.
So my question is why cocos2d::Vector<AnimationFrame*> become empty with my refactored code and not whith the previous one ?
I found this with test like that:
auto test = animList[0].anim->getFrames();
if (test.empty()) {
log("empty"); //The test output empty
}
Debugguer screen in the end of the init() funct:
Debugguer screen in Player::manageIdle() funct:
Edit: when I add animations.back().anim->retain(); right after the line to add an element in the vector, it solves the problem !
animations.push_back({"idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1)});
animations.back().anim->retain();
Because cocos2d::Animation* inherit from cocos2d::Ref, it is an auto-release object. When used inside a cocos2d container like cocos2d::Map or cocos2d::Vector, it is auto managed by the container itself. But I use a std::vector so I lose the ref I think. Something like that.
Now I need to find a way to get rid of this additional line of code because this multiple by twice my number of line here !
So new question here: How I can get rid of the fact I have to call animations.back().anim->retain(); each time I add a new element in my vector ?
You might create a wrapper around Ref, which "retains" ownership, and store this wrapper instead, sort of a std::unique_ptr e.g.
template<typename T> class RefOwner {
public:
RefOwner(T *t) : ref(t) {
ref->retain();
}
~RefOwner() {
ref->release();
}
T *operator->() { return ref; }
private:
T *ref;
};
and then use it as
struct animData {
std::string name;
RefOwner<cocos2d::Animation> anim;
//all others info needed, not relevant here, (just several enum type variables)
};
Disclaimer: I have no experience with cocos2d-x, just looked at Animation and Ref
I asked a question yesterday, but it seems I did ask it incorrectly, so here I go again. I quickly mocked up this code, and I'm wondering if this is a proper way to handle the objects (outside of just putting everything in main(). This would be my first time trying to complete a larger project, so I've never had a need to question how to contain and handle the objects.
skill.h
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
class Skill;
typedef std::vector<std::shared_ptr<Skill>> SkillVector;
typedef std::unordered_map<std::string, std::shared_ptr<Skill>> SkillMap;
class Skill {
private:
std::string _skill_name;
std::string _skill_description;
friend std::ostream& operator<< (std::ostream& stream, const Skill& skill);
void print(std::ostream& stream) const;
public:
Skill(const std::string& skill_name, const std::string& skill_description);
const std::string& getSkillName() const;
const std::string& getSkillDescription() const;
bool isMatch(const std::string& substring) const;
};
application.h
#pragma once
#include "skill.h"
extern std::string SKILL_FILE;
class Application {
private:
std::unique_ptr<SkillMap> _skill_map;
void loadData();
void loadSkills();
void printSkillMap();
void printSkillVector(const SkillVector& skills);
std::unique_ptr<SkillVector> skillSearch(const std::string& input) const;
public:
Application();
void run();
};
main.cpp
#include "application.h"
int main() {
std::unique_ptr<Application> application(new Application);
application->run();
return 0;
}
This is going to be a command-line application, so other than this I can't think of doing anything else except putting all the methods from the application class straight into main as regular functions. That doesn't seem like the correct approach though. I've tried researching this pretty extensively, but haven't really found anything that answers this.
What are the other options for containing all the objects in an orderly fashion? What are the advantages of doing it certain ways? And is what I'm doing here acceptable?
NOTE: This isn't an exact representation of my project, and I'm just using it as an example to understand my question at hand.
TL;DR There isn't a single answer on how to move forward. It depends on:
What your goal is.
What subsystems the project will have, and how they integrate with one another.
The type and quantity of data that the project will require.
That said, it's almost always a good idea to apply separation of concerns, segregating data & functionality between types that correspond to each of your project's concerns. Here's an example using a finite state machine to get you started:
// Note that this example uses C++11 features.
// Note that the stubs are intentional, to keep this example as terse as possible.
// For consistency with your naming style.
typedef std::string String;
template<typename T>
using Vector = std::vector<T>;
template<typename T>
using UniquePtr = std::unique_ptr<T>;
// Skill.hpp: Represents a single skill.
class Skill {
private:
String _name;
String _description;
public:
Skill(String name, String description);
const String& getName() const { return _name; }
const String& getDescription() const { return _description; }
};
// Player.hpp: Represents a single player.
class Player {
private:
Vector<Skill*> _skills;
bool _isVirtual; // Is this player from another computer? (networked multiplayer)
public:
Player(bool isVirtual = false) :
_skills(),
_isVirtual(isVirtual) {}
Player(const Player& other) = delete; // Non-copyable.
bool hasSkill(Skill* skill) const;
void addSkill(Skill* skill);
bool removeSkill(Skill* skill); // Returns true if removed, false otherwise.
bool isVirtual() const { return _isVirtual; }
};
// Level.hpp: Represents a single level of the game.
class Level {
private:
// Per-level data...
public:
Level();
Level(const Level& other) = delete; // Non-copyable.
};
// GameState.hpp: Represents the state of the game.
enum class GameState {
MainMenu, // The user is in the main menu.
World, // The user is navigating the world.
Combat, // The user is in combat.
};
// Game.hpp: Represents a single game instance.
class Game {
private:
Application* _app;
Vector<UniquePtr<Player>> _players;
Vector<UniquePtr<Level>> _levels;
Level* _level; // Current level the player(s) are on.
GameState _state; // Current state of the game.
bool _isRunning; // Is the game running?
void mainMenuLoop() {
while (_isRunning) {
// Implement your main menu here.
// Update the menu display after user input.
// When a selection is confirmed, execute it and return.
}
}
void combatLoop() {
while (_isRunning) {
// Implement your combat system here.
// When combat is over, update _state and return.
}
}
public:
Game(Application* app) :
_app(app),
_players(),
_levels(),
_level(),
_state(GameState::MainMenu),
_isRunning(true) {
// Finish initializing and:
// If loading is fast enough, do so here.
// If loading is slow, spin up a thread and load asynchronously.
}
Game(const Game& other) = delete; // Non-copyable.
void run() {
// If loading is slow, wait on the loading thread to complete.
// Possibly show an introduction banner.
while (_isRunning) {
switch (_state) {
case GameState::MainMenu:
mainMenuLoop();
break;
case GameState::Combat:
combatLoop();
break;
}
}
}
void quit() { _isRunning = false; }
const Player* getPlayer(int index) const;
Player* getPlayer(int index);
void addPlayer(Player* player);
bool removePlayer(Player* player); // Returns true if removed, false otherwise.
const Level* getLevel(int index) const;
Level* getLevel(int index);
void addLevel(Level* level);
bool removeLevel(Level* level); // Returns true if removed, false otherwise.
};
// Options.hpp: Represents command line options.
struct Options {
// Add your command line options here.
Options();
Options(const Options& other);
};
// Application.hpp: Represents a single application instance.
class Application {
private:
Options _options;
Game _game;
public:
// For compatibility with your code.
Application() :
_options(),
_game(this) {}
// For later, when you add command line options.
Application(const Options& options) :
_options(options),
_game(this) {}
Application(const Application& other) = delete; // Non-copyable.
const Options& getOptions() const { return _options; }
const Game* getGame() const { return &_game; }
Game* getGame() { return &_game; }
// For compatibility with your code. You could
// remove this and directly use getGame()->run()
void run() { _game.run(); }
};
In main using unique_ptr for the Application object is unnecessary. Use the stack instead:
// Added arguments because you'll probably want command-line options later.
int main(int argc, char** argv) {
Application app; // Better locality, no dynamic allocation.
app.run();
return 0;
}
Follow the following rules:
When you have inheritance, the classes should respond to 'is-a'. For example if you have class Speed : public Skill. That seems logical and: Speed is a Skill.
When you have composition, the classes should respond to 'has-a'. In your case: Application has a Skill map.
If the class was Hero it would be really logical: Hero has a Skill map. And this hero will develop the skills also the hero might need to print its skills... So the Hero will manage his Skills.
Sorry if I misunderstood your question.
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 Formation
* - 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};
}
I'm having a little problem setting values in my private struct of my Class. It is like the following:
//ProcessImage.h
class Process_Image
{
private:
struct ImageData
{
Mat imageMatrix;
int V_Min;
int V_Max;
Imagedata(Mat img, int Vmin=0, int Vmax=255):
imageMatrix(img), V_Min(Vmin), V_Max(Vmax) {}
};
public:
bool set_V_Min(int Value);
};
//ProcessImage.cpp
bool Process_Image::set_V_Min(int Value)
{
if(value>0&&value<256)
{
ImageData.V_Min=value; //it is not working setting it like this
return true;
}
return false;
}
Where am I wrong? I think it should be possible to set the value in my struct that way but I don't know what I'm missing. Please give me a hint or a direction how to do it the right Way.
You haven't created the structure yet, only described it. To have constant structure inside class write it down like this:
class Process_Image
{
private:
struct ImageData
{
Mat imageMatrix;
int V_Min;
int V_Max;
Imagedata(Mat img, int Vmin=0, int Vmax=255):
imageMatrix(img), V_Min(Vmin), V_Max(Vmax) {}
}ImageData; // <- your missing part
public:
bool set_V_Min(int Value);
};
I want to create an immutable data structure which, say, can be initialized from a file.
class Image {
public:
const int width,height;
Image(const char *filename) {
MetaData md((readDataFromFile(filename)));
width = md.width(); // Error! width is const
height = md.height(); // Error! height is const
}
};
What I could do to fix the problem is
class Image {
MetaData md;
public:
const int width,height;
Image(const char *filename):
md(readDataFromFile(filename)),
width(md.width()),height(md.height()) {}
};
However
It forces me to save MetaData as a field in my object. Which I don't always want.
Sometimes the logic in the constructor is much more complex than a single read (say, error handling can take a few lines)
So the only solution I thought of is along the lines of
class A {
int stub;
int init(){/* constructor logic goes here */}
A():stub(init)/*now initialize all the const fields you wish
after the constructor ran */{}
};
Is there a better idea? (In Java, you're allowed initializing finals in the constructor).
You could move width and height into one type and move the initialization code into an initialization helper function:
// header:
struct Size {
int width, height;
Size(int w, int h) : width(w), height(h) {}
};
class Image {
const Size size; // public data members are usually discouraged
public:
Image(const char *filename);
};
// implementation:
namespace {
Size init_helper(const char* filename) {
MetaData md((readDataFromFile(filename)));
return Size(md.width(), md.height());
}
}
Image::Image(const char* filename) : size(init_helper(filename)) {}
You can simply use the NamedConstructor idiom here:
class Image
{
public:
static Image FromFile(char const* fileName)
{
MetaData md(filename);
return Image(md.height(), md.width());
}
private:
Image(int h, int w): mHeight(h), mWidth(w) {}
int const mHeight, mWidth;
};
One of the main advantage of Named Constructors is their obviousness: the name indicates you are building your object from a file. Of course it's slightly more verbose:
Image i = Image::FromFile("foo.png");
But that never troubled me.
If it was C++0x, I would recommend this (delegating constructors):
class Image
{
public:
const int width, height;
Image(const char* filename) : Image(readDataFromFile(filename)) { }
Image(const MetaData& md) : width(md.width()), height(md.height()) { }
};
You should add inline getters for the width and height instead of public const member variables. The compiler will make this solution as fast as the original try.
class Image {
public:
Image(const char *filename){ // No change here
MetaData md((readDataFromFile(filename)));
width = md.width();
height = md.height();
}
int GetWidth() const { return width; }
int GetHeight() const { return height; }
private:
int width,height;
};
P.S.: I used to write private things at the end because they are less important for the user of the class.
First, you should understand the constructor body is just for running code to complete initializing your object as a whole; the members must be completely initialized before the body is entered.
Ergo, all members are initialized in an (implicit unless made explicit) initialization list. Clearly, const variables must be initialized in the list because once you enter the body, they are already suppose to be initialized; you'd simply be trying to assign them.
Generally, you don't have const members. If you want those members to be immutable, just don't give any public access to them that could change them. (Also, having const members make your class non-assignable; typically unnecessarily.) Going this route easily fixes your problem, as you'd just assign them values in the body of the constructor like you wish.
A method to do what you want while maintaining const could be:
class ImageBase
{
public:
const int width, height;
protected:
ImageBase(const MetaData& md) :
width(md.width()),
height(md.height())
{}
// not meant to be public to users of Image
~ImageBase(void) {}
};
class Image : public ImageBase
{
public:
Image(const char* filename) : // v temporary!
ImageBase(MetaData(readDataFromFile(filename)))
{}
};
I don't think this route is worth it.
You could cast away the constness in the constructor:
class Image {
public:
const int width,height;
Image(const char *filename) : width(0), height(0) {
MetaData md(readDataFromFile(filename));
int* widthModifier = const_cast<int*>(&width);
int* heightModifier = const_cast<int*>(&height);
cout << "Initial width " << width << "\n";
cout << "Initial height " << height << "\n";
*widthModifier = md.GetWidth();
*heightModifier = md.GetHeight();
cout << "After const to the cleaners " << width << "\n";
cout << "After const to the cleaners " << height << "\n";
}
};
That would achieve what you want to do but I must say I personally would stay away from that because it causes undefined behavior according to the standard (excerpt from cppreference)
const_cast makes it possible to form a reference or pointer to
non-const type that is actually referring to a const object ...
Modifying a const object through a non-const
access path ... results in undefined behavior.
I would fear any public data members(at least in regarding your particular example). I would go with Georg's approach or make the data private and provide only the getter.
How about passing MetaData as an argument to the constructor. This gives a lot of benefits:
a) The constructor interface makes it clear about the dependency on MetaData.
b) It facilitates testing of the Image class with different types of MetaData (subclasses)
So, I would probably suggest similar to follows:
struct MD{
int f(){return 0;}
};
struct A{
A(MD &r) : m(r.f()){}
int const m;
};
int main(){}
I'd use a static method:
class Image {
public:
static Image* createFromFile( const std::string& filename ) {
//read height, width...
return new Image( width, height );
}
//ctor etc...
}
class A
{
public:
int weight,height;
public:
A():weight(0),height(0)
{
}
A(const int& weight1,const int& height1):weight(weight1),height(height1)
{
cout<<"Inside"<<"\n";
}
};
static A obj_1;
class Test
{
const int height,weight;
public:
Test(A& obj = obj_1):height(obj.height),weight(obj.weight)
{
}
int getWeight()
{
return weight;
}
int getHeight()
{
return height;
}
};
int main()
{
Test obj;
cout<<obj.getWeight()<<"\n";
cout<<obj.getHeight()<<"\n";
A obj1(1,2);
Test obj2(obj1);
cout<<obj2.getWeight()<<"\n";
cout<<obj2.getHeight()<<"\n";
return 0;
}
As far my understanding i think this mechanism will work.
This is one of my least favorite aspects of C++ when compared to Java. I'll use an example I was working on when I needed to solve this problem.
What follows is the equivalent of a readObject method. It deserializes a Video key from a provided file path.
#include <fstream>
#include <sstream>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
using namespace std;
using namespace boost::filesystem;
using namespace boost::archive;
class VideoKey
{
private:
const string source;
const double fps;
const double keyFPS;
const int numFrames;
const int width;
const int height;
const size_t numKeyFrames;
//Add a private constructor that takes in all the fields
VideoKey(const string& source,
const double fps,
const double keyFPS,
const int numFrames,
const int width,
const int height,
const size_t numKeyFrames)
//Use an initializer list here
: source(source), fps(fps), keyFPS(keyFPS), numFrames(numFrames), width(width), height(height), numKeyFrames(numKeyFrames)
{
//Nothing inside this constructor
}
public:
//Then create a public static initializer method that takes in
//the source from which all the fields are derived
//It will extract all the fields and feed them to the private constructor
//It will then return the constructed object
//None of your fields are exposed and they are all const.
const static VideoKey create(const path& signaturePath)
{
const path keyPath = getKeyPath(signaturePath);
ifstream inputStream;
inputStream.open(keyPath.c_str(), ios::binary | ios::in);
if (!inputStream.is_open())
{
stringstream errorStream;
errorStream << "Unable to open video key for reading: " << keyPath;
throw exception(errorStream.str().c_str());
}
string source;
double fps;
double keyFPS;
int numFrames;
int width;
int height;
size_t numKeyFrames;
{
binary_iarchive inputArchive(inputStream);
inputArchive & source;
inputArchive & fps;
inputArchive & keyFPS;
inputArchive & numFrames;
inputArchive & width;
inputArchive & height;
inputArchive & numKeyFrames;
}
inputStream.close();
//Finally, call your private constructor and return
return VideoKey(source, fps, keyFPS, numFrames, width, height, numKeyFrames);
}