Best Way to Write Constructors in C++?
Posted by Ace9750@reddit | learnprogramming | View on Reddit | 32 comments
I'm in my first semester as a computer science major, and I just learned about classes. I think I have a pretty good understanding of them, but I had a question on constructors. My professor exposed me to two different ways to write them, and I wanted to ask which one is better to use in a professional setting.
Note that these constructors correspond to the following class Box:
class Box
{
private:
int length;
int width;
int height;
public:
Box();
};
The two ways we could write the constructor were
Option 1:
Box::Box()
: length(1), width(1), height(1)
{}
Option 2:
Box::Box()
{
length = 1;
width = 1;
height = 1;
}
Which one would be more acceptable in a professional setting? Is there an even better alternative than these two?
jaynabonne@reddit
Between the two, I'd go with the first, as you're directly initializing the variables as opposed to default constructing and then assigning them. But for this simple case, you can actually just do:
For a primitive type like int, it may not matter. You could check the generated code. But there are other types where you definitely have to use the former syntax, particularly to initialize base classes, references, const members or objects that take parameters in their constructors, which aren't possible to assign after the fact in the constructor body.
GarThor_TMK@reddit
This is a great option when compile times don't matter. If you have a ton of files depending on box.h file, then any change to the default values here is going to trigger a big recompile of all of the dependencies.
However, it is a great option for simple ints like this, because it makes them really hard to forget to initialize.
jaynabonne@reddit
I probably wouldn't do that myself with a value of 1. Zero maybe :) I find it handy for setting dynamic bool state to false initially.
TomDuhamel@reddit
It depends what your case is. A value of zero cannot possibly be valid (physics), so you'd do that if you want to initialise with an (obvious) invalid state. But sometimes you need the object to be valid immediately, especially if some value is so common as to be considered a sane default.
GarThor_TMK@reddit
This might be a bigger philosophical discussion on weather you should default initialize something to a valid state or an invalid state.
GarThor_TMK@reddit
Personally, I like to put all my publics up front. The class declaration should read like an index to users of that class, so putting all the important information that they need up front makes the most sense.
This turns your Box class into
Now, you can also add inline initializers, so that your class looks like
class Box
{
public:
Box();
private:
int length = 1;
int width = 1;
int height = 1;
};
This will make it so that your constructor will automatically default these to the desired values... However, if those defaults ever need to change, this file will need to be recompiled, along with anything that depends on this file. This is fine for small codebases, but for larger ones, it can really have a big impact if this is included in a lot of places, so I don't know that I really recommend it as a habit. The one advantage it does have is that it makes it hard to forget to initialize something, and it makes it really easy to tell when it's not.
Weather you go with Option1 or Option2 may just depend on how picky the people you work with are about their coding standards. Afaik there is no practical difference. It's just style. Pick what you like, and stick to it.
Personally, I'd put the initialization each on a new line for option1, and I'd probably indent them a bit, but maybe that's just me? 🤷‍♀️
jakovljevic90@reddit
Option 1 (using an initialization list) is definitely the preferred approach in professional settings. Here's why:
Here's a pro tip: Always use initialization lists when possible. They're especially important for:
So in your example,
Box::Box() : length(1), width(1), height(1) {}
is the way to go. Nice job learning this early in your CS journey!Bonus recommendation: Consider adding a parameterized constructor too, something like:
_nepunepu@reddit
When you use initializer lists with objects that have preconditions (in your example, a box with negative length, width or height would be invalid), what is the best way to handle that? Should you verify in the constructor body?
jakovljevic90@reddit
Great question! When dealing with objects that have preconditions, there are a few solid approaches:
My recommendation? The factory method. It: - Separates creation logic - Allows for more complex validation - Keeps constructor simple - Prevents invalid object creation
Pro tip: Always validate inputs before setting member variables. It's a hallmark of robust, professional-grade code.
LazyIce487@reddit
What in the chat gpt are these answers
_nepunepu@reddit
Thanks for the answer!
Underhill42@reddit
Others have pointed out the potential performance differences - so I'll just point out the fact that those are unlikely to make a noticeable real-world difference except in cases of very simple objects that get created in very large quantities.
So if for some reason another strategy makes more sense for clarity in a particular context, that's probably the way to go.
While you're getting acclimated to constructors though - let me just throw out the sometimes-overlooked fact that you should (almost) never make a single-parameter constructor without declaring it "explicit", because it will act as an implicit conversion operator. E.g. if you created a non-explicit Box(int size) constructor to initialize all three dimensions to the specified size, you would then be able to pass an integer to any function that expects a Box, or assign an integer to any Box variable (assuming operator= is defined for boxes), with no compiler complaints.
That and a touch of carelessness can be the source of some really annoying bugs.
Chance-Letter-6242@reddit
I like option 1 better. More concise! No clue if it is more professional
echtma@reddit
Option 1 initializes the members.
Option 2 default-initializes them and assigns the other values to it after that.
It doesn't really make a difference with primitive types, but it is important when the members are themselves of user-defined type. Then in Option 1, they will be constructed using the appropriate constructor, while in Option 2, they are initialized by their default constructor and then assigned to via their overloaded operator=. Not only it this doing more work than necessary, it may simply not be possible, because not all classes can be default constructed or assigned to. Therefore, idiomatically, Option 1 is preferred. That said, there is Option 3, I think it needs C++11:
class Box {
private:
int length = 1;
...
};
This will also initialize length (not assign).
Random_throwaway0351@reddit
Hi, do you mind explaining why the options don’t matter for primitive types and why some classes can’t be default constructed?
echtma@reddit
Default-initializing an int or other primitive type simply does nothing (the value starts out undetermined) so, especially after optimization, I would expect the machine code to be exactly the same in both option 1 and 2. That might even be true if the types are more complicated, that depends on what the constructor and assignment operators actually do and how smart the compiler is.
A class that can't be default-constructed is simply one that doesn't have a default constructor.
Random_throwaway0351@reddit
Makes sense. Thanks for the explanation!
OldWolf2@reddit
The fewer functions you can write, the better. IMHO the clear best solution is to use initializers on the variable definitions, and don't write a constructor.
dev_ski@reddit
Option 1, the one using the initializer list, is the preferred way.
carminemangione@reddit
You asked a very subtle but spectacular question. It is rare to meet any one who knows the answer.
The difference is order on initialization for the variables. The second guarantees that those three variables (called initializers) will be initialized in that order. Out is the only way in C++to guarantee order and it is greatly preferred to number 1.
As a class becomes more complex, there is sometimes important to control the order as one variable may require others be set before its value of calculated using them
Lumpy_Ad7002@reddit
As always, the answer is: it depends.
In this particular example, it doesn't matter. The compiler will easily optimise these to the same binary.
If you're using more complex initializers that call other constructors/funtions, then option 1 is going to be better. Not only is it certain to generate the better code, it also puts the initialization at the beginning of the constructor where somebody else won't later be as tempted to put something else, possibly dependent, in front of the option 2 initializors.
Macree@reddit
The first one is faster.
Jonny0Than@reddit
Probably not for this example, but it could be if the members had nontrivial default constructors. For that reason it’s a good idea to get in the habit of using member initializer lists.
_user1980@reddit
if you want to start with default value 1
Box(int l=1, int w=1, int h=1)noexcept : length{l}, width{w}, height{h}{}
if you want to start type defaults
Box(int l=int{}, int w=int{}, int h=int{})noexcept : length{l}, width{w}, height{h}{}
you can't start static values second way. there are other technicalities too.
TheyWhoPetKitties@reddit
The C++ Core Guidelines are a great resource for questions like this. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-default
sephirothbahamut@reddit
In order
obp5599@reddit
Prefer option 1. This constructs the object (for primitives it initializes them), and for user made types calls their constructor instead of them being default initialized, then assigned later
Bee892@reddit
Option 1 is faster, generally speaking. The compiler doesn't allocate extra memory for duplicate objects. However, option 1 does you little good if you need to do additional logic when setting the variables, so option 2 would be preferred in that scenario. If it's going to be as simple as set the variable to the value passed in, then go with option 1.
Wonderful-Habit-139@reddit
Either Box::Box() : length(1), width(1), height(1) {}
Or if you have a newer version of c++ (I think C++11):
Box::Box() : length{1}, width{1}, height{1} {}
LazyIce487@reddit
If you need conditional initialization logic, the second one, otherwise you can use the initializer list and avoid calling default constructor and then assigning to an object after it’s already default constructed.
Jealous_Tomorrow6436@reddit
i’m not a professional but i’d imagine that this depends somewhat on the team you work with. some teams have certain conventions they stick to and those conventions don’t always match “best practices” so to speak.
personally though, id go with 1. that’s what im most used to seeing when studying graphics, and i trust my graphics prof to have good judgement
Comprehensive_Bar545@reddit
2