Let's say I have a class:
class C{
int x_;
int y_;
public:
C(int x, int y): x_(x), y_(y){}
};
Then I want to add construction from a string, which would just parse x
and y
. Before reading Meyers's book I would usually make it another constructor in class C
. However, it's also possible to make it non-member non-friend:
C CFromString(const std::string& s){
int x, y;
//...parse them somehow, throw exception if needed...
return C(x,y);
}
To me this is standard situation for many "value classes", when there is a "main" constructor which sets private members to provided values (probably checking there correctness). Other constructors for such classes are often just calculate these values from some other arguments.
Are there any drawbacks in making such constructors non-member non-friends like in my example?
Upd. I understand the Meyers's advice, and what are the advantages of NMNF functions. There is just no examples of NMNF object construction in his book, so I want to ensure that his advice applies to construction as well.
If you start adding constructors for every possible way a class can be serialized, you are tightly coupling that class with those serialization methods.
It is preferred that you separate the class and the serializer in order to separate concerns. Let the class focus on what the class does and the serializer on what it does (read json or whatever)
You have to consider that your class might be serialized from a file, from a socket, from json, from xml, from a database...from any number of things.
That's why in modern programming we use interfaces. We also make use of the Factory pattern.
One drawback is a bit of inconsistency, which is an esthetic concern.
You're calling a CFromString
constructor function rather than invoking a constructor called C
. The relationship between them is arbitrary, just through the C
prefix in the name.
If you make it a static
member function, then you can call it C::FromString
so that it belongs to the class.
If this is done all over the place in a large project, some sort of convention would help. Like say, whenever we have a class C
, and a non-member constructor function for making C
-s, let's always call it CCons
, and then always use overloading for different types. Thus, if we have a Widget
class, we then call our overloaded family WidgetCons
:
Widget WidgetCons(const std::string &s) { /* make it from string */ }
Widget WidgetCons(int i) { /* make it from int */ }
and so on. If this is consistent in our 250,000 line codebase, whenever someone sees any FooCons
or BarCons
, they know exactly what it is.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With