I'm reviewing some code and I'm seeing a lot of this:
class Foo
{
public:
Foo()
{
// 'nuffin
}
void init()
{
// actual construction code
}
} ;
The only advantage I can see is if you create a Foo without using a pointer and you want to h开发者_如何学运维old off its construction code until later, then you can.
Is this a good idea or a bad idea?
I dislike it. It seems to me that after construction, an object should be... well... constructed. That code leaves it in an invalid state instead, which is almost1 never a good thing.
1 Weasel word inserted to account for unforeseen circumstances.
In general, I agree that it's something to be avoided. But something none of the answers so far have addressed is the possibility that initialization may fail. Constructors cannot fail, so if your constructor allocates memory, or opens a file, or does anything else that may fail, you need a way to tell the caller that an error occurred. If you do the initialization in the constructor, then you need to have a flag that indicates whether or not the initialization succeeded, and then ensure that the caller checks that flag.
If you have a separate init() routine that must be called before anything else works, callers are more likely to check that return code than to call a didInitializationSucceed()
method after creating the object.
Two-stage construction is generally considered a bad idea, if there are methods on the class which rely on the object being in some initialised state. Generally, I prefer constructors which guarantee the object is in a good state, or if that cannot be done (perhaps because some of the arguments to the constructor were invalid), throw an exception, so there is never an instances of your class which is in a bad state.
Requiring consumers of your object to remember to call init()
is a bad idea, because they won't.
One case where this may apply is when 'Foo' is a attribute of another class and cannot be fully constructed before the parent-class is done. Only then can 'Foo' be 'filled-in'.
I believe constructor should basically do the init() part as well. Unless the object is fully constructed, it shouldn't be used.
Also, initializing in constructor allows you to make use of RAII. The basic point of RAII is to represent a resource by a local object, initialize in constructor, so that the local object's destructor will release the resource. That way, the programmer cannot forget to release the resource.
In some languages (read: C++) you can't call a constructor from another constructor, so if you want a common part of several constructors you need to put it in a separate method, and I've seen the name init() used for that. But that is not what you're talking about?
I use the contructor and init if I am instantiating objects that are based on a database call. So, if I need an empty object so that I can populate it and then save it to the database, I construct with no parameters and don't call init(). Whereas if I need to retrieve the object members from the db, I'll contruct($param)
and pass the $param to init($param)
.
Generally, source code should be as simple as possible, and your example is presented without the context, so it's just more complicated than necessary and therefore your example is a bad idea.
However, there may be some semantic contexts, where it may make sense to be able to deliver uninitialized objects - for instance, if the context requires a container to have objects but you don't want to initialize them until later because initialization is slow and/or maybe the objects are not needed. In those cases, the additional complexity may make something else simpler.
Although it cannot be considered a normal or preferred way of constructing objects, under some circumstances that maybe a way to go. For example you may need to construct an object just to indicate its existence (in some list, where count does matter etc.), but to init it later only if this particular object is used for the first time as initializing whole collection of objects would take to much time.
In that case it's good to expose the fact that object may be not initialized by including a method like isInitialized()
. Also that way you can transfer initialization to another thread in order not to block the main thread of the application.
The difference is that initialization happens after the call to the super class's constructor but before any code is executed in your local class constructor. Therefore, it really depends on your needs.
精彩评论