Nested Constructors C++ [duplicate] - c++

This question already has answers here:
Delegate Constructor C++
(4 answers)
Closed 6 years ago.
I am using SFML and have a class like the following:
#include <SFML\Graphics.hpp>
class Overlay
{
public:
Overlay(int width, int height);
Overlay(const sf::Texture*);
Overlay(const sf::Sprite*);
~Overlay();
private:
sf::Texture _texture;
sf::Image _img;
};
Now the following constructor for Overlay(const sf::Sprite*) is working:
Overlay::Overlay(const sf::Sprite* spr) {
int width = spr->getTexture()->getSize().x;
int height = spr->getTexture()->getSize().y;
_texture.create(width, height);
_img.create(width, height, sf::Color::Transparent);
}
However, the following, nested constructors are not:
Overlay::Overlay(int width, int height)
{
_texture.create(width, height);
_img.create(width, height, sf::Color::Transparent);
}
Overlay::Overlay(const sf::Texture* tex) {
sf::Vector2u textureSize = tex->getSize();
Overlay(textureSize.x, textureSize.y);
}
Overlay::Overlay(const sf::Sprite* spr) {
Overlay(spr->getTexture());
}
To me it looks, like the two snippets should be doing the same thing if the following is executed:
sf::Sprite image;
Overlay mOverlay(&image);
Although both of them compile just fine, when the second code snippet (nested constructors) is called, _img ends up having a size of 0 and its m_pixels array is empty.

Delegating Constructors.
It sounds like you are looking for a Delegating Constructor.
To use a delegating constructor, it should appear in another constructor's member initializer list, not the constructor's body.
In your second snippet, you are constructing a temporary Overlay object the stack. When the constructor returns, the temporary is destroyed and has no effect.
Try defining the constructor like so:
Overlay::Overlay(const sf::Sprite& spr)
: Overlay(spr.getTexture())
{
}
A small code review.
Notice how I used a const sf::Sprite& rather than a const sf::Sprite*? Because the case of spr being a nullptr isn't being handled, it makes sense to pass it by reference to ensure it refers to an object. This also clears up any question as to who owns the texture after the constructor is called.
When you do this, you should also consider declaring the constructor with the explicit keyword like so:
class Overlay
{
public:
explicit Overlay(const sf::Texture&);
explicit Overlay(const sf::Sprite&);
};
This prevents Textures and Sprites from accidentally being turned into Overlays when passing them around.

Related

Will this void function return a value at all? Or do I have to add a "&"?

Suppose I have a previously written class called Box. I have the following constructor and void function:
Box::Box (double height, double width, double length)
{
setHeight(height);
setWidth(width);
setLength(length);
}
void Box::setHeight(double h)
{
height = h;
}
In my main () function, I later declare:
Box box1(2.4, 7.1, 5.0);
Will 2.4 become the height of my box?
How?
The voidBox::setHeight(double h) doesn't have a "return" value because it's a void function? So how will it set height?
Will 2.4 become the height of my box? How?
Yes, 2.4 will become the height stored in the member variable of your Box object.
Box::setHeight is a member function. It gets an implicit reference to the object on which it is called. When invoked from the constructor, member function gets the a reference to the object being constructed. That is why the assignment
height = h;
inside Box::setHeight would modify the height member variable of your Box object.
do I have to add a &?
No. A reference to the object being constructed is passed implicitly; no action is required on your part.
Note 1: In order for your code to compile, Box needs to have a member variable called height. This is different from the height in the constructor, which is a parameter passed to the constructor from the caller.
Note 2: You can assign member variable height in the constructor by referencing it with this keyword:
this->height = height;
You could also use initialization list to initialize height:
Box::Box (double height, double width, double length)
: height(height)
, width(width)
, length(length)
{
// The body can be left empty
}
Assuming Box looks something like this:
class Box {
private:
double height, width, length;
public:
Box(double height, double width, double length);
void setHeight(double h);
void setWidth(double w);
void setLength(double l);
};
Then Box::setHeight is setting the height member of the class. Member functions can access data members of the class as well as any parameters passed in, so the function doesn't need to return anything to assign a value to the member.

When to use {} () in constructors

In Game.h, I have a class named Game:
class Game
{
public:
Game();
void run();
private:
sf::Event e;
sf::RenderWindow app;
sf::Clock delay;
unsigned counter = 0;
Ice *ball = new Ice(app);
Players *players = new Players(app);
Collision collision;
void handleEvent();
};
Then, in Game.cpp, I use:
Game::Game() : app({1000,800},"NAME")
{
}
The syntax
Game::Game() : app(sf::VideoMode(1000,800),"NAME")
{
}
also works.
My question is: why can I omit sf::VideoMode in the first case, and if I do so, why do I HAVE to use {} for the videomode parameters? Using () in the first case results in an error.
Your Game constructor is calling the following sf::RenderWindow constructor:
RenderWindow (VideoMode mode, const String &title, Uint32 style=Style::Default, const ContextSettings &settings=ContextSettings())
You have to pass it a fully constructed sf::VideoMode object.
Both of your examples are constructing a temporary VideoMode object inline in the sf::RenderWindow constructor call. They are both calling the following sf::VideoMode constructor, just using different syntaxes:
VideoMode (unsigned int modeWidth, unsigned int modeHeight, unsigned int modeBitsPerPixel=32)
C++11 introduced a new feature called "Uniform Initialization", which unifies various different types of initialization syntaxes used in different contexts into a single syntax using curly braces, thus greatly simplifying coding. In this case, List Initialization is being used to initialize a temporary object using a braced-init-list. It is this feature that allows your {1000,800} example to construct a temporary sf::VideoMode object directly in a function parameter without having to explicitly state its class name.
Earlier C++ versions do not have "Uniform Initialization". In those versions, constructors can only be called explicitly by name. That is what allows your sf::VideoMode(1000,800) example to construct a temporary sf::VideoMode object (and it is still valid syntax in C++11 and later, too).
(1000,800) by itself is not valid C++ syntax for constructing an object, in any C++ version.
why can I omit sf::VideoMode in the first case
Because app's constructor's first parameter is declared as having type sf::VideoMode already.
It's the same reason you can write long v = 1; instead of having to write long v = long{1}; or something like that: the compiler knows the type of the variable or parameter being initialised, and can check that that type supports construction using the initialiser you specified, even if the initialiser doesn't have the same type.
and if I do so, why do I HAVE to use {} for the videomode parameters?
Because (1000,800) already had a different much older meaning: it means "get the value 1000, discard that, then get the value 800 instead". Admittedly that'd be pointless in this case, but there's no way of rewriting the language in such a way that (1000,800) works in your case while simultaneously sensible previously valid code continues to work too.

Confused by parameters example given in college lecture

Sorry to post such a specific question, but I'm confused by an example question given in college. The question is:
class BoundingBox {
private:
float m_width;
float m_height;
public:
BoundingBox(float width = 0, float height = 0) :
m_width(width), m_height(height) {}
};
class Enemy {
private:
EnemyType m_type;
BoundingBox m_box;
Enemy(BoundingBox const & box, EnemyType type = EnemyType::Boss) :
m_type(type);
m_box(box);
{}
};
Q: Is the following construction of Enemy legal? Explain.
Enemy enemy1(10);
The answer supplied says that it is, because the 10 is passed in as the width parameter and the default is used for the height, and also the default is used for the EnemyType. But from my understanding, the line:
BoundingBox const & box
is expecting a box object to be passed to it, not a parameter for it's constructor.
Has my lecturer made a mistake, or am I missing something? If I have misunderstood, could you provide me with a link that explains what is happening 'under the hood' so to speak. I didn't know this was a valid way to construct an object. I would ask my lecturer but he's been out sick for a week, and I can't find anything online about parameter based construction.
Yes, it is fine and will compile (barring the syntax and access to the constructor).
To create the type Enemy, a BoundingBox is required; in particular the Enemy constructor accepts the argument as a const& thus allowing temporary values to be used.
To create the BoundingBox, no argument, one float or two floats can be used. The variation is because default arguments are supplied in the BoundingBox constructor. The constructor is not marked explicit (and this is the key bit that makes it work), the compiler is thus allowed to implicitly create the BoundingBox temporary by itself - which it duly does and then creates the Enemy object.
Adding an explicit will result in a compilation failure; it would be constructive for you to do this, and observe the error messages you receive. I suspect this could the subject of future lectures as well.
In general and often, advice is given to mark constructors that could take a single argument (taking into account defaults) as explicit thus preventing unknown (or unseen) conversions. If the implicit conversion is required, then don't add the explicit.
Code with the syntax issues cleared up;
class BoundingBox {
private:
float m_width;
float m_height;
public:
/*explicit*/ BoundingBox(float width = 0, float height = 0) :
// ^^^^^^^ experiment with and without this being present
m_width(width), m_height(height) {}
};
class Enemy {
private:
EnemyType m_type;
BoundingBox m_box;
public:
// ^^^ added to allow access to the constructor
Enemy(BoundingBox const & box, EnemyType type = EnemyType::Boss) :
m_type(type),
// ^ syntax here
m_box(box)
// ^ syntax here
{}
};

Directly using the constructor of class in other class constructor

I have this:
class point{
public:
point()=default;
point(int x,int y):x(x),y(y){}
int x,y;
}
and this:
class quad{
public:
quad()=default;
quad(point a,point b,point c, point c):a(a),b(b),c(c),d(d){};
point a,b,c,d;
}
In the main, I can do this:
point a(0,0),b(1,1),c(2,2),d(3,3);
quad q(a,b,c,d);
Or directly this:
quad q(point(0,0),point(1,1),point(2,2),point(3,3));
but of course NOT this:
quad q(0,0,1,1,2,2,3,3); // I know it's wrong
The question:
Is it possible without declaring a new constructor that take 8 integers in quad to use the last code? the motivation of this question is the way that emplace_back works. To be more clear:
std::vector<point> points;
points.push_back(point(0,0)); // you have to pass object
points.emplace_back(0,0); // you have just to send the arguments of the constructor
This is not possible without declaring a new constructor.
One option is to just pass in brace initializers for each of the points:
quad q({0,0},{1,1},{2,2},{3,3});

Function in constructor initialiser list?

Heyy, I am trying to switch from initialising my variables within the constructor to using the constructor initialiser list.
So instead of writing
Class::Class(int width, int height) {
this->width = width;
this->height = height;
}
I am doing this:
Class::Class(int width, int height) :
width(width),
height(height) {
}
That's all working, but now my problem... Say I have the following constructor:
Class::Class(int width, int height) {
this->width = width;
this->height = height;
this->state.setCurrState(this->state.stateMenu);
this->state.setPrevState(this->state.getCurrState());
}
"state" is just an object of the class "State" which I create in my header. The functions setCurrState and setPrevState are of the type void and just set the class's private variable.
How do I convert the constructor? I know it is possible to write functions in the initialiser list, but the functions I want to add do not return anything... they are void, so I do not know how I would call them?
Class::Class(int width, int height) :
width(width),
height(height)
// call functions here... but how?
{
}
Thank you so much I hope you can help me <3
There is no additional point and advantage to call those functions in initializer list, at least in your case.
Simply call them in the constructor body.
Important note:
You said state is a member of Class. So in constructor's level, state is not constructed yet, then constructing it by itself is somehow meaningless:
state.setCurrState(state.stateMenu);
state.setPrevState(state.getCurrState());
Try to write a well constructor for state's class to set curr/prev to a initial states.
Easy solution: Leave the initializing functions in the body of the constructor.
Slightly more difficult solution:
Add a constructor to State to initialize it from an argument.
Write new functions that return the value that you want to pass to the constructor of State.
setCurrState and setPrevState are not formally initializing your state object, they are merely changing it's "state" (no pun intended) after it has been initialized.
If you consider them to be semantically initializing you state object, then you might as well formalize that by incorporating them in state's constructor (which should receive the needed states and set them right away). You can then initialize it in the initializer list.
Add a function that returns a State:
State GiveMeAState() {
State state;
state.setCurrState(state.stateMenu);
state.setPrevState(state.getCurrState());
}
and use it in your initialization list:
Class::Class(int width, int height) :
width(width),
height(height),
state(GiveMeAState()) {
}