another preference question....
-
If you want to bring it up with the team again, and I would personally, the name of the pattern that you use is called Resource Acquisition Is Initialization. The best reason for using this pattern, in my opinion, is that it is exception safe. If an exception happens it the object will be cleaned up automatically when the destructor is called, without you having to specifically needing to call the cleanup method. A quick google search for RAII will bring up plenty of reasons to discuss with your team should you ever find yourself in that argument again.
Janzen wrote:
the name of the pattern that you use is called Resource Acquisition Is Initialization.
Amen. Although it is surprising how many people refuse to grasp it.
-
I think it depends. Normally I would lean towards the constructor based initialization but not always. In the end you have to decide whether it improves code readability. Here is an horror story which ought to go to the Coding Horrors. In my previous company, I was fixing an issue and in that file I found the following code:
void PostInvoice()
{
ApbhTivhStatusChange * pChg = new ApbhTivhStatusChange(m_pContext);
delete pChg;//Some other code follows this is where the real bug I was
//supposed to fix was
}After fixing the issue, I removed the first 3 lines of the function thinking that it was not doing anything as the process function was commented out. My bug fix worked correctly and I checked it in. I modified like this:
void PostInvoice()
{
//Some other code follows this is where the real bug I was
//supposed to fix was
}Later on is QA testing it was discovered that seem other piece of application functionality broke. It was embarrassing to find that the code I removed caused the issue. It was not a big issue to discover that the work was being done in the constructor and destructor of ApbhTivhStatusChange class.
class ApbhTivhStatusChange
{
private:
Context* m_pContext;public:
ApbhTivhStatusChange(Context* pContext)
{
pContext->ConnectToDatabase();
pContext->BalanceInvoice();
}~ApbhTivhStatusChange()
{
pContext->Cleanup();
}
}The above code was poorly written even though it worked. It caused a huge confusion. But one thing is for sure, you should always add clean up code in the destructor. There are very few cases where I found that it is not advisable to do so and of course you need to make sure that the destructors do not throw any exceptions but at the same time they log the exceptions.
Co-Author ASP.NET AJAX in Action
Whoever wrote the original PostInvoice() and did not comment the purpose of the first two lines should be taken out and shot. BDF
-
Whoever wrote the original PostInvoice() and did not comment the purpose of the first two lines should be taken out and shot. BDF
The code actually evolved passing through several developers, the original developer might have done it correctly. It was probably the idea of a later developer who was a newbie to use the constructor an destructor to do all the work.
Co-Author ASP.NET AJAX in Action
-
The code actually evolved passing through several developers, the original developer might have done it correctly. It was probably the idea of a later developer who was a newbie to use the constructor an destructor to do all the work.
Co-Author ASP.NET AJAX in Action
Rama Krishna Vavilala wrote:
The code actually evolved passing through several developers, the original developer might have done it correctly. It was probably the idea of a later developer who was a newbie to use the constructor an destructor to do all the work.
we had something like that happen to us. It was in an Ascii to Latitude/Longitude routine. I forget the name of the method, but it supported several types of latitude or longitude references (dd.decimal or dd mm.dec or dd mm ss.dec). Another programmer got ahold of the method and continued addition additional inputs beyond even latitude and longitude, attempting the equivalent generalality of a scanf() for geodetic values. Pass about 6 months and no one knew these changes occured and everything was still going fine, until we tried to run a specific data set in a specific format, and intermittent jumps were occuring in the data. Turns out the new changes were not thread-safe, and the routine had grown so complex supporting so many types that it was a pain to track down! mind you, the method was still directly referenced as something to the extent of ascii 2 latlong in the name, so everyone thought it still did just that.
_________________________ Asu no koto o ieba, tenjo de nezumi ga warau. Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
-
Nemanja Trifunovic wrote:
constructors can't return values to tell us if something went wrong
I've recently started using a library that makes all constructors inaccessible, and all objects must be created by a overloads of a static
Create
method on the object. eg:class MyClass { private MyClass() {} public static MyClass Create() { return new MyClass(); } }
I first thought that this was because constructors could not return failure codes, but as I type this, I realize that a static
Create
method with a specific return type can't return a status code either! Then I thought it was for serializing the object or something of that nature, but with reflection (in .NET), you can call a constructor as easily as any other method. My final thoughts on this, are that the Create method does not have to return a specific instance, while a constructor does. This means that in a complex hierarchy, you can be given a descendent object instead of the object you asked actually asked for, depending on how theCreate
method works. A highly contrived example:class ClassA { private ClassA() {} public static ClassA Create( int x ) { if ( x < 0 ) return new ClassA(); else return ClassB.Create(); } } class ClassB : ClassA { private ClassB() {} public static ClassB() { return new ClassB(); } } static void main() { ClassA myClass = ClassA.Create( 3 ); // Will really create a ClassB }
Is this what it's used for? Or am I heading into La-La Land with this line of thinking?
Sunrise Wallpaper Project | The StartPage Randomizer | A Random Web Page
http://en.wikipedia.org/wiki/Factory_method_pattern[^]
[My Blog]
"Visual studio desperately needs some performance improvements. It is sometimes almost as slow as eclipse." - Rüdiger Klaehn
"Real men use mspaint for writing code and notepad for designing graphics." - Anna-Jayne Metcalfe -
Just to add on to what others have said, even with exceptions, in C++, an exception raised inside a constructor still poses a problem. For example, if you are using a pointer, and the constructor thew an exception, do you delete the object through that pointer? Is it needed/safe? It was safer to do just simple initialisations within the constructor, and put anything that can go wrong in a separate method.
Bijesh wrote:
For example, if you are using a pointer, and the constructor thew an exception, do you delete the object through that pointer?
If the object was allocated using the non-placement (i.e. normal) version of
new
, the object is supposed to be "undone". However (last checked on VC++ 6.0), members of that object will only be cleaned up if they themselves were fully constructed at the time of the exception, so the object is destructed (as best it could be),new
returnsNULL
, so there is nothing to calldelete
on. If the object was allocated on the stack, that stack context will be destroyed by the stack unwinding that takes place, so again there is nothing to manually destroy/cleanup. Things change when you are dealing with placementnew
because you are basically directly calling the constructor to construct an object in a pre-allocated block of memory. What you do in that case depends on what you are using the block of memory for. You may free the entire block, or simply log the exception and continue with the next object reusing that block of memory. You just have to watch out for that exception so that you do not end up using that "partially constructed" object (which I have done before :doh: - that is why I know it is possible, and know that thevtbl
is/was the last thing to get initialized during construction). Not many developers need to implement their own fixed-size-block-allocator-based object cache, or otherwise deal with placementnew
, so issues with throwing exceptions from constructors is a non-issue for many. Peace!-=- James
Please rate this message - let me know if I helped or not! * * *
If you think it costs a lot to do it right, just wait until you find out how much it costs to do it wrong!
Avoid driving a vehicle taller than you and remember that Professional Driver on Closed Course does not mean your Dumb Ass on a Public Road!
See DeleteFXPFiles -
Just curious. Had this disagreement with my team a while back and lost. I didn't want to bring it up then as it would be pointless. But I am curious on a design issue with classes. I was brought up (admittedly on books for C++, not college), that constructors not only construct, but initialize everything so that the class is "ready to be used" alternately the destructor removes everything and cleans up after itself. This way there is never an issue as far as timing when you use the contents of a class. Others have promoted an init()/configure() methodology and cleanup()/clear() so that constructors construct the class, not the content, init()/configure() sets up the content, cleanup()/clear() removes the content (which I have used for specific reusable storage type classes, but never as a hard rule of everything) and destructor pretty much does nothing unless memory allocation is involved. Of course this also means, if threads are involved there requires an additional start() after configure() to get the ball rolling which creats a set of "do this first directives" in the documentation. :~ I do freely admit no college training, not sure if this is a college thing or not. I am not a lover of absolutes, but I did always like my classes clean so that they start and stop by scope properly (everything handled by constructors and destructors), and now I find myself staring at my code as if it is growing green, black and white mold with the recent changes to it. X| Is this a college teaching method? Have I been putting too much effort into organizing my classes as independant entities?
_________________________ Asu no koto o ieba, tenjo de nezumi ga warau. Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
One of the shortcomings of constructors (at least in the languages I've used) is that they aren't
virtual
. In this example, the Child class must define a constructor, but it simply calls the base constructor without adding any unique behaviour. If the constructor were inherited by the Child this wouldn't be necessary.public class Base
{
public string Name ;
public Base ( string Name )
{
this.Name = Name ;
}
}public class Child : Base
{
public Child ( string Name ) : base ( Name ) {}
}By defining a virtual Initialize method to do what the constructor would do (and allowing the compiler to provide an empty parameterless constructor), you get the desired behaviour.
public class Base
{
public string Name ;
public virtual Base Initialize ( string Name )
{
this.Name = Name ;
return ( this ) ;
}
}public class Child : Base {}
As you can see, the Child class needn't define a constructor, the empty parameterless constructor the compiler adds is enough and then the Child inherits the Initialize method, which it may override if and only if it needs to. (Note that Initialize returns a reference to the calling instance.) On the downside, using a class like this is a litte more complex, basically:
Child c = new Child() ;
c.Initialize ( "Zaphod" ) ;or
Child c = (Child) (new Child()).Initialize ( "Zaphod" ) ;
As you can see, saving time and effort writing and maintaining the classes costs some time and effort using the classes, so you have to decide for yourself which you prefer. I'd much rather have virtual constructors.
-
Just curious. Had this disagreement with my team a while back and lost. I didn't want to bring it up then as it would be pointless. But I am curious on a design issue with classes. I was brought up (admittedly on books for C++, not college), that constructors not only construct, but initialize everything so that the class is "ready to be used" alternately the destructor removes everything and cleans up after itself. This way there is never an issue as far as timing when you use the contents of a class. Others have promoted an init()/configure() methodology and cleanup()/clear() so that constructors construct the class, not the content, init()/configure() sets up the content, cleanup()/clear() removes the content (which I have used for specific reusable storage type classes, but never as a hard rule of everything) and destructor pretty much does nothing unless memory allocation is involved. Of course this also means, if threads are involved there requires an additional start() after configure() to get the ball rolling which creats a set of "do this first directives" in the documentation. :~ I do freely admit no college training, not sure if this is a college thing or not. I am not a lover of absolutes, but I did always like my classes clean so that they start and stop by scope properly (everything handled by constructors and destructors), and now I find myself staring at my code as if it is growing green, black and white mold with the recent changes to it. X| Is this a college teaching method? Have I been putting too much effort into organizing my classes as independant entities?
_________________________ Asu no koto o ieba, tenjo de nezumi ga warau. Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
Reading the above comments it seems that most people agree on a RAII model anyway. Requiring a sequence of steps to use simply sucks. RULE: the external interface should provide no surprises, and reduce complexity as much as possible. Both are usually violated by a "required sequence" class. So constructor-initialization and destructor-cleanup are my first choice, too, but there's a small footnote I'd add. In C++, creating a dynamic object is - compared to a "normal" data member - considered expensive and painful (unless you use smart pointers, which usually make it bearable). Also, there are some aggregation initialization scenarios where a direct initialization isn't feasible, e.g.:
class Gamma { public: void Gamma(Container * container) { m_container * container; } }; class Container { Gamma m_gamma; public: Container() : m_gamma(this) {} **//BAD IDEA!** }
You can now either make gamma dynamic:class Container { scoped_ptr m_gamma; public: Container() { m_gamma.reset(new Gamma(this)); } }
or you need to use an Init-Method. Still, in this case, there's a fixed patthern (rather two of them) I strictly follow:class Gamma
{
public:
Gamma() {... } // valid "not open" initialization
bool IsOpen();
void/errcode Open(...); // fails if IsOpen()
void Close(); // fails if IsOpen
~Gamma() { if (IsOpen() Close(); }
};i.e. the class is initialized correctly, if a resource is acquire can be queried using IsOpen, and the destructor cleans up if you don't close. (so Close is just an "early release of the resource", rather than a mandatory cleanup) Further, the class state must remain well-formed if you try to access the ressource if it isn*t acquired. Also, you must be able to use open and close a single instance as often as you like. One shot classes and "you can't call foo ever again after you called bar" are - unless this is a requirement of the ressource - teh h3ll. (just for the curious: the second pattern is just more relaxed, for the lazy me:
class Gamma
{
public:
bool IsOpen();
void/errcode Open() { Close(); ... }
void Close() { if (IsOpen()) { ... } }
~Gamma() { Close(); }
}the idea here is to move the IsOpen() calls from the caller into the class, so caller has to type less
We are a big screwed up dysfunctional psychotic happy fami
-
Just curious. Had this disagreement with my team a while back and lost. I didn't want to bring it up then as it would be pointless. But I am curious on a design issue with classes. I was brought up (admittedly on books for C++, not college), that constructors not only construct, but initialize everything so that the class is "ready to be used" alternately the destructor removes everything and cleans up after itself. This way there is never an issue as far as timing when you use the contents of a class. Others have promoted an init()/configure() methodology and cleanup()/clear() so that constructors construct the class, not the content, init()/configure() sets up the content, cleanup()/clear() removes the content (which I have used for specific reusable storage type classes, but never as a hard rule of everything) and destructor pretty much does nothing unless memory allocation is involved. Of course this also means, if threads are involved there requires an additional start() after configure() to get the ball rolling which creats a set of "do this first directives" in the documentation. :~ I do freely admit no college training, not sure if this is a college thing or not. I am not a lover of absolutes, but I did always like my classes clean so that they start and stop by scope properly (everything handled by constructors and destructors), and now I find myself staring at my code as if it is growing green, black and white mold with the recent changes to it. X| Is this a college teaching method? Have I been putting too much effort into organizing my classes as independant entities?
_________________________ Asu no koto o ieba, tenjo de nezumi ga warau. Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
I only use two-stage construction when initialization can fail for a multitude of reasons AND the code creating the object needs to know which on the many reasons caused the failure. Constructors can't return values, so all you know is that it failed to construct. I use two-stage quite often when the class wraps some kind of communication. The class is not fully up and running until the comms are active, and I need to know why comms aren't active as the class starts up. Judy
-
IMO, it boils down to this: 1) If the object is a first class entity - i.e., not a handle - then do initialization and finalization in constructor and destructor 2) If the object is a simple handle (such as a CWnd derivative), then there are two life lines - the handle object itself and whatever it's handling. Then I use init()/cleanup() to handle the life line of the handled object. My reason for 2) is that the destruction of handled object may fail - something I don't want to deal with in a destructor (if you say exception, then you've not only shot yourself in the foot, but you've also blown your brains out). Also, I might want to be able to share the handled object between handles, or I might just want to transfer the responsibility of it elsewhere. Note that this isn't something I've learned in school. I've picked it up as I go... :)
-- Raaaaaaaaaaaaaaaaaaaaa!
Very good criterion to separate these. HOWEVER, (muhahahah!)
Joergen Sigvardsson wrote:
the destruction of handled object may fail - something I don't want to deal with in a destructor
however, if the caller doesn't call Close() - because he's lazy or an exception got into the way, you still have to deal with that.
Joergen Sigvardsson wrote:
Also, I might want to be able to share the handled object between handles, or I might just want to transfer the responsibility of it elsewhere.
But Joergen! That's why there is
shared_ptr
!
We are a big screwed up dysfunctional psychotic happy family - some more screwed up, others more happy, but everybody's psychotic joint venture definition of CP
My first real C# project | Linkify!|FoldWithUs! | sighist -
Very good criterion to separate these. HOWEVER, (muhahahah!)
Joergen Sigvardsson wrote:
the destruction of handled object may fail - something I don't want to deal with in a destructor
however, if the caller doesn't call Close() - because he's lazy or an exception got into the way, you still have to deal with that.
Joergen Sigvardsson wrote:
Also, I might want to be able to share the handled object between handles, or I might just want to transfer the responsibility of it elsewhere.
But Joergen! That's why there is
shared_ptr
!
We are a big screwed up dysfunctional psychotic happy family - some more screwed up, others more happy, but everybody's psychotic joint venture definition of CP
My first real C# project | Linkify!|FoldWithUs! | sighistpeterchen wrote:
however, if the caller doesn't call Close() - because he's lazy or an exception got into the way, you still have to deal with that.
Yes, that's where RIAA comes into play. :)
peterchen wrote:
But Joergen! That's why there is shared_ptr!
Well, that works wonders, if it's a pointer to a handle. That is not always the case. MFC and ATL/WTL objects come to mind.
-- Larva-Tested, Pupa-Approved
-
I only use two-stage construction when initialization can fail for a multitude of reasons AND the code creating the object needs to know which on the many reasons caused the failure. Constructors can't return values, so all you know is that it failed to construct. I use two-stage quite often when the class wraps some kind of communication. The class is not fully up and running until the comms are active, and I need to know why comms aren't active as the class starts up. Judy
But constructors can throw exceptions... but I also appreciate that exceptions are not everyones cup of tea. :)
Kicking squealing Gucci little piggy.
The Rob Blog -
Dario Solera wrote:
Dispose/Finalize is invoked automatically (but not necessarily immediately) when the instance goes out of scope.
I don't think
Dispose
is invoked automatically. I know the finalizer is, but by the time it's invoked, you can't count on any of the references in your object as still being valid. They may have already been collected. You can release unmanaged resources (which is important), but that's about it.You're references will not be collected, they just might have been finalized if they also have a finalizer. You can even revive references from finalizers. But you should never call another object's Dispose() method from a finalizer - if that object needs to do cleanup, it should have its own finalizer.
-
Nemanja Trifunovic wrote:
constructors can't return values to tell us if something went wrong
I've recently started using a library that makes all constructors inaccessible, and all objects must be created by a overloads of a static
Create
method on the object. eg:class MyClass { private MyClass() {} public static MyClass Create() { return new MyClass(); } }
I first thought that this was because constructors could not return failure codes, but as I type this, I realize that a static
Create
method with a specific return type can't return a status code either! Then I thought it was for serializing the object or something of that nature, but with reflection (in .NET), you can call a constructor as easily as any other method. My final thoughts on this, are that the Create method does not have to return a specific instance, while a constructor does. This means that in a complex hierarchy, you can be given a descendent object instead of the object you asked actually asked for, depending on how theCreate
method works. A highly contrived example:class ClassA { private ClassA() {} public static ClassA Create( int x ) { if ( x < 0 ) return new ClassA(); else return ClassB.Create(); } } class ClassB : ClassA { private ClassB() {} public static ClassB() { return new ClassB(); } } static void main() { ClassA myClass = ClassA.Create( 3 ); // Will really create a ClassB }
Is this what it's used for? Or am I heading into La-La Land with this line of thinking?
Sunrise Wallpaper Project | The StartPage Randomizer | A Random Web Page
I use static Create() methods only when creating an instance has a side-effect, e.g. opening a TCP connection. If the Create() method can return derived classes, I would put it in a factory class and not in the base class.
-
I use static Create() methods only when creating an instance has a side-effect, e.g. opening a TCP connection. If the Create() method can return derived classes, I would put it in a factory class and not in the base class.
Daniel Grunwald wrote:
If the Create() method can return derived classes, I would put it in a factory class and not in the base class
I agree. Like I said, it was a terrible contrived example, and after I posted it I realized how bad it actually was! Having a base class create derived classes would be a complete WTF... :-O
Sunrise Wallpaper Project | The StartPage Randomizer | A Random Web Page
-
You're references will not be collected, they just might have been finalized if they also have a finalizer. You can even revive references from finalizers. But you should never call another object's Dispose() method from a finalizer - if that object needs to do cleanup, it should have its own finalizer.
Daniel Grunwald wrote:
You're references will not be collected, they just might have been finalized if they also have a finalizer. You can even revive references from finalizers. But you should never call another object's Dispose() method from a finalizer - if that object needs to do cleanup, it should have its own finalizer
Thanks for the clarification. I appreciate it.
-
Just curious. Had this disagreement with my team a while back and lost. I didn't want to bring it up then as it would be pointless. But I am curious on a design issue with classes. I was brought up (admittedly on books for C++, not college), that constructors not only construct, but initialize everything so that the class is "ready to be used" alternately the destructor removes everything and cleans up after itself. This way there is never an issue as far as timing when you use the contents of a class. Others have promoted an init()/configure() methodology and cleanup()/clear() so that constructors construct the class, not the content, init()/configure() sets up the content, cleanup()/clear() removes the content (which I have used for specific reusable storage type classes, but never as a hard rule of everything) and destructor pretty much does nothing unless memory allocation is involved. Of course this also means, if threads are involved there requires an additional start() after configure() to get the ball rolling which creats a set of "do this first directives" in the documentation. :~ I do freely admit no college training, not sure if this is a college thing or not. I am not a lover of absolutes, but I did always like my classes clean so that they start and stop by scope properly (everything handled by constructors and destructors), and now I find myself staring at my code as if it is growing green, black and white mold with the recent changes to it. X| Is this a college teaching method? Have I been putting too much effort into organizing my classes as independant entities?
_________________________ Asu no koto o ieba, tenjo de nezumi ga warau. Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
I have not previously encountered this approach (and I have been coding professionally for nearly fourteen years). I disagree with the separate init() method approach for a number of reasons: a.) Expectations (one should follow convention) - most frameworks will initialise objects through the constructor, and users of your code (who can even be your co-workers or your future co-workers) are likely to expect that too. They will easily do something wrong if the class usage semantics are unconventional. b.) You create an additional source of failure (read source of bugs), because nother method must be called before the object can be used. Omission of this call can create a hard to track down bug. Why create this possibility? c.) Error checking. You have created an additional state for your objects to exist in. You have made it possible to use your class without it being initialised, and so you ALWAYS have to ensure that when a public method is called the object has been correctly initialised. Usually you would only have to place guards on the methods that are intended to modify the object's contents in order to ensure that the contents are valid. If your object can be inherited from (this is C++ so unless you only construct objects through a factory method, your class can be inherited from), then you have to place guards in the protected methods as well. Ugly! d.) Calling init() (or whatever you call it) multiple times becomes possible but probably does not make sense. If you need this capability for the object, then you are either reset()ing the object, or you need to construct a new one. e.) Copy constructors - when copy semantics are needed for a class, the constructor has to create a fully initialised object. This breaks the paradigm. f.) C++ will chain the calling of constructors. The compiler will tell you if you get the chaining wrong. You will have to implement this manually if you use an init() method, and there will be no compile-time errors to tell you that you got it wrong. It's even more error prone when you are using multiple inheritance. You have once again created a source of potentially hard-to-find bugs. Why? g.) Performance - initialising attributes within a constructor (especially within the member initialisation list) is more efficient than initialising them from a separate method. If performance is any concern, you will almost certainly want the constructor to do it all. h.) If you absolutely must perform initialisation in a separate method, make the method
-
Just curious. Had this disagreement with my team a while back and lost. I didn't want to bring it up then as it would be pointless. But I am curious on a design issue with classes. I was brought up (admittedly on books for C++, not college), that constructors not only construct, but initialize everything so that the class is "ready to be used" alternately the destructor removes everything and cleans up after itself. This way there is never an issue as far as timing when you use the contents of a class. Others have promoted an init()/configure() methodology and cleanup()/clear() so that constructors construct the class, not the content, init()/configure() sets up the content, cleanup()/clear() removes the content (which I have used for specific reusable storage type classes, but never as a hard rule of everything) and destructor pretty much does nothing unless memory allocation is involved. Of course this also means, if threads are involved there requires an additional start() after configure() to get the ball rolling which creats a set of "do this first directives" in the documentation. :~ I do freely admit no college training, not sure if this is a college thing or not. I am not a lover of absolutes, but I did always like my classes clean so that they start and stop by scope properly (everything handled by constructors and destructors), and now I find myself staring at my code as if it is growing green, black and white mold with the recent changes to it. X| Is this a college teaching method? Have I been putting too much effort into organizing my classes as independant entities?
_________________________ Asu no koto o ieba, tenjo de nezumi ga warau. Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
Generally I think it's best to have the constructor/destructor perform the intilisation/clean up. Breaking the code out into an init procedure which is called from the constructor(s) can be useful for sharing similar code between constructors but otherwise it's a personal preference thing... However, there are times when init/cleanup methods are required. One example is when you want to support some kind of object pooling mechanism, in which case you'll want to be able to clean up the class without it be 'finalized' and you'll want to be able to re-initialize it when it's retreive back out of the pool - at which point the constructor is useless since the object has already been created. If initialisation is expensive, then you may also want to be able to create X number of classes in the pool (on startup for example) and allow the initialization to take place 'lazily' after the fact. Also, the 'clean up' style methods may be needed depending on what language you're working in. In something like C# where there is no deterministic finalization you need an explicit clean up method on some objects to allow immediate release of unmanaged resources (hence the IDisposable interface, which is really just a system supported way of implementing a clean up method). In VB6, circular references between classes could be a problem, in which case a clean up method would sometimes be needed to release at least one set of references so memory wasn't leaked and the classes did actually get 'finalized' eventually. I can't think of another instance when 'init' style methods are useful, except for the object pooling, although that might just be lack of imagination on my part :) Basically, I think it depends on that particular patterns and architecture you're using, but for mmost classes the constructors/destrutors are the right place for init/cleanup code.
-
El Corazon wrote:
Others have promoted an init()/configure() methodology and cleanup()/clear() so that constructors construct the class, not the content, init()/configure() sets up the content, cleanup()/clear() removes the content (which I have used for specific reusable storage type classes, but never as a hard rule of everything) and destructor pretty much does nothing unless memory allocation is involved.
That idiom is called "two-phase construction" and was widely used before exceptions were available for C++. For instance, MFC uses it all the time simply because VC++ didn't support exceptions at the time that MFC was designed. In general, today most experts including Stroustrup recommend avoiding two-phase construction, but some people don't use exceptions for one reason or another and they really have little choice: constructors can't return values to tell us if something went wrong :)
I have two solutions for those classes (and of course, no other human being has ever thought about this extremely clever solution:->). - a status function that checks invariants (mostly as simple as a fail or success flag) -- is_ok() - a static factory, mostly returning a std::pair (or just object*). This requires that one hides the c'tors (except assignment and copy -- falling back to is_ok()) auto_ptr f(new Foo(bar)); if(f->is_ok())... or auto_ptr f(Foo::create(bar)); if(f/* && f->is_ok()*/)... or pair> r(Foo::create(bar)); if(r->first == BAD_DOG) //drooled on the ham...:omg:
-
Just curious. Had this disagreement with my team a while back and lost. I didn't want to bring it up then as it would be pointless. But I am curious on a design issue with classes. I was brought up (admittedly on books for C++, not college), that constructors not only construct, but initialize everything so that the class is "ready to be used" alternately the destructor removes everything and cleans up after itself. This way there is never an issue as far as timing when you use the contents of a class. Others have promoted an init()/configure() methodology and cleanup()/clear() so that constructors construct the class, not the content, init()/configure() sets up the content, cleanup()/clear() removes the content (which I have used for specific reusable storage type classes, but never as a hard rule of everything) and destructor pretty much does nothing unless memory allocation is involved. Of course this also means, if threads are involved there requires an additional start() after configure() to get the ball rolling which creats a set of "do this first directives" in the documentation. :~ I do freely admit no college training, not sure if this is a college thing or not. I am not a lover of absolutes, but I did always like my classes clean so that they start and stop by scope properly (everything handled by constructors and destructors), and now I find myself staring at my code as if it is growing green, black and white mold with the recent changes to it. X| Is this a college teaching method? Have I been putting too much effort into organizing my classes as independant entities?
_________________________ Asu no koto o ieba, tenjo de nezumi ga warau. Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
In my experience, I've always wanted my classes ready to use from the time they are created. If I want a null instance, I create a null constructor. If I want the fields populated, I create an overloaded constructor that does that. I used to build separate routines that performed similar functions as yours, but tracking what did what became too much of a maintenance overhead. Once I began overloading the constructors, management became a lot easier. I'm not saying that I didn't refactor, because I did and I do. If I had redundant initialization code, I pulled them out into their own methods. But, I do not automatically pull out initialization just for the sake of pulling it out, because it's "easier to test," which, by the way, really doesn't make it any easier. It just creates additional, unnecessary function points... My $0.02...