I've been working on a game written with C and SDL2 in my free time and I'm refining some of my base classes for drawable objects. These objects have a position (x and y) and a size (width and height). It makes more sense to me to store these objects in a fixed size array, so I've been using std::array to contain these values in my class since the length is fixed. I'm a little confused on how I should copy the std::array I pass as parameters to the constructor though (and in my setter methods as well)
This is how I have it now
class Drawable {
protected:
std::array<float, 2> position;
std::array<int, 2> drawsize;
SDL_Texture * texture;
public:
// constructor
Drawable(const std::array<float, 2> pos, const std::array<int, 2> size, SDL_Texture* text);
// setters
void set_pos(const std::array<float, 2> &pos) { position = pos; }
void set_size(const std::array<int, 2> &size) { objsize = size; }
};
Drawable::Drawable(const std::array<float, 2> &pos, const std::array<int, 2> &size, SDL_Texture* text) {
position = pos; // like this?
objsize = size; // ???
texture = text;
}
I'm omitting a lot of unnecessary details from the above class to focus on the main issue I'm asking about
I'm confused since I have read that you can copy std::array with assignment (so std::array<int, 5> arr2 = arr1;
) but I'm not sure if that is the same when you pass an array as a constant reference as I have (with const std::array<int, 2> &arr
). Since I am copying the array anyways, does it even make sense to pass arrays by constant reference? Since they are only of length 2, I have thought about just copying each element into the member variable, but if there is a more readable solution I welcome it.
I'm relatively new to C and the containers in the standard library. This is mainly to learn more and have fun :^)
CodePudding user response:
Either way, by-reference or by-value, is ok.
But don't assign individual elements manually. That is exactly what the assignment/constructor of std::array
does for you.
Whether, in general, constructor arguments which will be copied/moved into the class members should be passed by-value or by-reference is a more complicated topic (there are pros and cons to both) which I think should not concern you much if you are new to language.
I would say stick to passing by const
reference for now until you learn about move semantics which will give you the context for why by-value passing might be useful. But opinions on this will differ. I am saying it mostly because it is more consistent with advice about passing of other non-scalar types.
If you just pass-by-value (without adding std::move
which you probably don't know about yet), then you would make two copies of the object. One for construction of the parameter in the constructor and one to construct/assign the member. Of course, it is likely that the compiler will optimize away one of these away, anyway, assuming the constructor is inlined and the type is simple. (In the case of std::array<float, ...>
it doesn't even matter whether std::move
is used here.)
There is also an argument to be made that passing small objects by-reference can be worse performance-wise in some situations than passing them by-value, but again, that shouldn't concern you if you are new to the language. This will dependent on the particular situation anyway and it is not obvious when this will be more efficient and when it will be less efficient. In most cases the compiler will probably optimize either variant to the same code anyway (again assuming the constructor can be inlined).
From a performance point of view passing std::array<int,2>
and std::array<float,2>
specifically by-value is probably the better choice and will probably also be a better choice for slightly larger int
or float
arrays, but where the cut-off is or whether it actually matters is unclear.
You should however use member initializer lists. What you currently do in your code is to assign the members values. You are not initializing them with the passed objects. To actually initialize them you write:
Drawable::Drawable(const std::array<float, 2> &pos, const std::array<int, 2> &size, SDL_Texture* text)
: position(pos),
objsize(size),
texture(test) {
}
where the members must (theoretically should) be listed in the order in which they are declared in the class.
CodePudding user response:
In your case with std::array<float, 2>
and std::array<int, 2>
, there's no reason to pass by const &
. The point of passing by const &
is to prevent expensive/unnecessary copies. But there's nothing expensive here. On my 64-bit machine, sizeof
both std::array<float, 2>
, std::array<int, 2>
, or any reference (which is a pointer under the hood) is 8
. So you're just copying one 64-bit word, whether it's a whole array or a reference (pointer) to it.
In fact, using references may even slightly slow down your program by introducing more indirect memory access when you read the values.