Pitfall of OOP? Or me just having a hard time following good practice ...

Started by
46 comments, last by jag_oes 22 years, 3 months ago
Magmai: Also, I would be interested in an explanation of "side effects" and maybe an example or too. It seems as if people are not paying attention to it because they do not know what you mean by it...
Advertisement
Magmai,

I agree with Houdini on most of the stuff.

quote:
-------------------------------
Why can''t a public property be part of the interface? What''s the difference between a public property and a pass-through get/set tuple? I often here that you can''t change the implementation. The only implementation change I see affecting a few key public properties is if you wanted to the change thier names, but you can''t change the name of the accessor methods either. You can still change the internal implementation, but you have promised, using an accessor or not, that you will provide that property. If the name of the property is misleading, then the name of the accessor would also be misleading, and it both cases it should be refactored.
If you had a property at one point in time, and now need a calculation - refactor, remove the property, add a new method that performs the calculation. You now have a compile time error that tells you every bit of code affected by this behavioral change to the class. A new behavior, means a new interface; if you keep the interface definition the same but change the behavior, you have no guarantee that the old code will still work correctly. xps with something like lazy evaluation.
-------------------------------

Having public property be part of the interface as in the above example might be OK if you are the only programmer or if it a small application but I think it is a major flaw when it is a team of around 10 programmers in an environment of change control, version control and the *dreaded* third party tools for a few reasons below.

1. Consider your application has around 100,000 lines of code and suddenly your customer wants to have the data inside the class to be represent in a different fashion and so you decide to change the member variable in the class. In this case "The only implementation change I see affecting a few key public properties is if you wanted to the change thier names" logic will become monstrous because in the 100,000 lines of code, the "few key public properties" *might* be present in 1000 lines. Think of the labor and fix duration for this small change. If you had had a accessor function for the variable, the change is at only one place.

2. Consider a 10 member team in which you own a module having a few classes. Because of some requirement changes in the late part of developement (believe me, this happens always and customers, who might not be computer professionals at all, love to make changes during the final stages of developement), you decide to change a key member variable of the class. If you have a accessor function for this member variable, the change might be so small and for your code alone. It might involving replacing the variable and making some changes to the accessor variable (you still are going to have the same name for the accessor function becuase semantically it does the same. you have just change the way the class stores the same data). If you have this variable as a part of the interface, now you should all the other 9 members of the team to make the corresponding changes where ever they use this member variable. This going to lead to lot more bugs, frustration from team members, unexpected delays and finally a shock to the boss.

3. The classic case is when you use a third party API and you just have a the header and lib or dll file. Consider the case where the third party API has a key member variable as a part of the interface and you and your team members have used this variable through the code. During the next version, becuase of the new feature and some changes, the third part vendor decides to make changes to variable. Now, this is going to frustruate your team since your code is broken you have to make changes to numerous places in your code to accomodate the change. It is the same case, if you are the third party vendor and if some customer is using your API.

So I think, from my experience, hiding your implementation and having accessor functions for your variables if helpful in numerous places. Yes, I agree, that when you have a complete finalized design (if it is a product in itself and not a API to be used by any other customers), you will be tempted to removed the functions that just retrieves / sets a variable without doing any additional function. But removing them is going to make you design closed and rigid. Any future changes, product expansion and next versions becomes a difficult issue to implement.

Arun
In my above post, in Point 1 by,

quote:
------
Consider your application has around 100,000 lines of code and suddenly your customer wants to have the data inside the class to be represent in a different fashion and so you decide to change the member variable in the class.
------

I meant to say,

Consider your application has around 100,000 lines of code and suddenly your customer wants the behaviour of your class to be little different and so you decide to change a key member variable (which happens to be a part of the public interface of the class) in the class.

Thanks
Arun
quote:null_pointe
Magmai: Also, I would be interested in an explanation of "side effects" and maybe an example or too. It seems as if people are not paying attention to it because they do not know what you mean by it...

An annoying side-effect I’ve come across is the one in VB when you update the text of a control – it raises an event that the contents have changed. It does this whether the user enters the information or if the change is done in the code via the property set function. It seems they never considered the case where the code that changes the property wold be inside the event handler (it recursively calls itself until VB explodes.) With one control you can keep a flag variable around that indicates the control is updating itself, and so it shouldn’t do anything in the redundantly raised event.
Once I had a combo-box with a list of items (go figure) and some edit boxes that allowed the user to twiddle the properties of the corresponding item they selected in the combo-box; the name that was displayed in the combo-box was also editable. Update events cascade from the edit box to the combo-box endlessly. I had to maintain state information to determine which updates were real and which were bogus side-effects of changing the contents of the control and selecting the correct item from code.
After encountering this situation in VB, I really began to appreciate the way MFC works.

I guess the problem isn’t the get/set tuples directly, but what is done with them. The problem is, it’s far too common to make things automatically happen inside a property put that don’t belong there. By using a property get/set, you are saying it’s a property that can be set and retrieve and used, and you are also saying it’s really a method that does something. I think they should be two distinct operatations.

quote:
I want to add that "const_cast" is not guaranteed to work (did I say this before? (why do people ignore me?)). Like reinterpret_cast it should be used primarily to indicate platform-dependent code.

const_cast won’t work when the item in question is truly a compiled constant. In the case of a class property, this it isn’t so. You can set the value during construction to different values for each instance, or hack it up with a const_cast inside the class. There really is space allocated for it with each instance...
I thought reinterpret_cast was supposed to be used when you wanted a blind cast between incompatible types, something cryptic like a DWORD to a class pointer? I don’t see how it’s tied to the platform/compiler/os? What did you mean by platform-dependent?


quote:arunvb
1. Consider your application has around 100,000 lines of code and suddenly your customer wants to have the data inside the class to be represent in a different fashion and so you decide to change the member variable in the class. In this case "The only implementation change I see affecting a few key public properties is if you wanted to the change thier names" logic will become monstrous because in the 100,000 lines of code, the "few key public properties" *might* be present in 1000 lines. Think of the labor and fix duration for this small change. If you had had a accessor function for the variable, the change is at only one place.


Ok, let’s say you did make a public property, and now it needs to be replaced with a calculation, and even though you know you should refactor and make the calculation explicit, you don’t have time/resource to do so, and you really wish you would have restricted access using a Get functions. All is not lost, you still can use an accessor function and leave the client syntax alone (with MSVC and BCB at the least, probably others as well):
  class CFutzpah{	public:		//oops, the size needs to be determined dynamically//const int size;__declspec(property(get=GetSize)) int size;		int GetSize()			{			int size;			//…			return size;			}//…};  


I guess you''ve forsaken portability to platform that don''t have native support for get/set tuples... but using a public property doesn''t mean you can never use accessors without changing the interface.

Would any one class be so visible that the entire project uses it? Wouldn’t any change in an OOP limit the change’s scope to a number of objects that use it?
So it’s in a thousand lines, in 50 classes. One or two hours worth of Search&Destroy, aided by the inability to compile until you checked and fixed every instance it’s used in to ensure the behavior is truly unaffected.


quote:
2. Consider a 10 member team in which you own a module having a few classes. Because of some requirement changes in the late part of developement (believe me, this happens always and customers, who might not be computer professionals at all, love to make changes during the final stages of developement), you decide to change a key member variable of the class. If you have a accessor function for this member variable, the change might be so small and for your code alone. It might involving replacing the variable and making some changes to the accessor variable (you still are going to have the same name for the accessor function becuase semantically it does the same. you have just change the way the class stores the same data). If you have this variable as a part of the interface, now you should all the other 9 members of the team to make the corresponding changes where ever they use this member variable. This going to lead to lot more bugs, frustration from team members, unexpected delays and finally a shock to the boss.

I can see both sides of the coin here, on one hand it’s convenient to be able to change the behavior of the class and still have everything compile. On the other hand, whenever we get an update to the sound analysis module we use, our code will still compile (it _always_ compiles because it uses the ultra-@$#@$ IDispatch interface), but it often fails to function correctly because they changed some propset side-effect or change some other behavior we depended on (like the durability of the result from a GetMax() method). Since it’s a IDispatch interface to an exe component, we try to limit how many times we have to jump processes boundaries and marshal information. Sometimes they make changes that let us make calls less frequently, sometimes we need more…

quote:
3. The classic case is when you use a third party API and you just have a the header and lib or dll file. Consider the case where the third party API has a key member variable as a part of the interface and you and your team members have used this variable through the code. During the next version, becuase of the new feature and some changes, the third part vendor decides to make changes to variable. Now, this is going to frustruate your team since your code is broken you have to make changes to numerous places in your code to accomodate the change. It is the same case, if you are the third party vendor and if some customer is using your API.

You have to break some eggs to make an omelet. The worst thing that can happen to a project, is the coders become so afraid of breaking it that they are afraid to fix it. If you get a new version, you ought to expect new interfaces with a more effective design and better functionality. Otherwise, why bother updating? The code as is works, don’t change anything!


quote: Arild Fines
Thats merely a syntax issue - there''s nothing there that cannot be done equally as effective in any other language. Some people might even consider this a misuse of operator overloading because it''s not immediately obvious from the code what happens.

? What would you expect the dereferencing operators to do? Shouldn’t they give you what the iterator refers to? It’s slightly more than a syntax issues, because those are all normal methods, not virtual ones. The real difference is in the algorithms that don’t use any function pointers never-mind virtual ones.

quote:
What overhead? The only overhead I can see is if the runtime performs checking on the cast.

The template would not only avoid the cast, it can avoid pointers and virtual functions as well. The STL sort is a lot faster than many others because you can avoid the function call inside the sort to access the user data, it can also be inlined into the sort, this yields anywhere from a 25% to a 600% increase in performance depending on implementations and the data.
A run-time cast check on an integer vector iterator would be about a 1600% performance hit to a for loop (sixteen times slower). For more complicated containers, the hit would probably drop to about 30%.
Can a JIT eliminate the virtual part of the function? And inline the code? If/when the JIT can do that the speed should be comparable, until then it just can’t compete – it’s not even a fair comparison to make, because total different things will be happening in the loops (a lot of _unnecessary things).

And what claims about Java have I made that were blatantly false? I’d like to know so I can stop believing/repeating them… No need for a witch-hunt, I’m just curious since something I’ve claimed seems to have stuck.


quote:Houdini
Not all automatic function calling can cause performance degradation. Remember my Date class in my previous example? Calling Get performs an action only when the date changes and the user wants to retrieve this new date, and this must be performed no matter what. There is no performance degradation. Had I put the calculation in the Set functions, then there could be performance degradate.

Well, doesn’t it need to be converted every time the user sets the date, as the user’s date is in Gregorian, and the classes date is in Julian? This is a bit of a bad example too, since you really need a Gregorian date utility class. Anything else is going to be problematic.
If I saw that your class used Julian dates, perhaps I could also use Julian dates until it came time to display them on the screen, delaying and avoiding more conversions.

Meyer’s is careful to avoid absolute statements there’s a blurb about it in the preface; in item 20 he says _avoid public properties, not Thou Shalt Make Thee’s Properties Private. I do agree that properties should be almost always be protected, private whenever there’s a reason for it, and public when there’s a reason for it. If you wanted a class to deal with WAVEFORMATEX structures, would you really want to have to accessor methods to twiddle all those properties? Now, you definitely would want something like .CDQuality .RadioQuality, etc… but you kinda want to be able to set those properties directly as well.
In Effective STL in item 13 he talks about std::string implementations Some use reference count to improve performance, and he refers to an article “Optimizations That Aren’t (In a Multithreaded World)” that talks about optimization techniques that wreck performance in multithreaded applications because they require additional synchronization that was atomic prior to the optimization (like string reference counting).

quote:
It''s just like polymorphism, there''s a time and place to use it. Sometimes it''s helpful, and sometimes it''s hurtful.

touché (I avoid that hurt like the plague)

quote:
But let''s take a look at STL, since you like to use them as an example. Try calling push_back to add something to an array which is already maxed out on size. What happens?

*GASP*, the vector array automatically calls another function which resizes the array. Did it tell me it was going to do this? Nope. So should I be forced to do this manually do this? Heck no.

Well it is called _push_ back, as to push the back end of the container out, good analog with a stack. It only resizes when it needs to, and this is the behavior that made you decide to use a vector instead of an array! The method to resize is called “resize” and the return type of “size” is an int and takes no parameters, so it couldn’t possibly resize the array. empty() returns a bool, so it’s whether the container is empty or not. There is no function to empty the entire container, since that’s a special case of removing a range, provided by the remove algorithm and the erase method. Erase and remove are both present because the remove algorithm is the same for all containers, whereas erase is implementation specific.
You have Effective C++, go get Effective STL!
You may not want to be forced to reallocate the vector, but it’s a good idea to resize it adequately aforehand or at construction.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
quote:Original post by Magmai Kai Holmlor

After encountering this situation in VB, I really began to appreciate the way MFC works.


That''s quite a feat! Perhaps I should try VB.

I will try to make a list of things I would change in MFC:

1. Remove the create/destroy tuples. These were created because constructors had no return value and so they had no means of reporting failure. Now that we have exceptions, they are useless and overcomplicate the class.

2. Windows header files introduce a large number of #define''s, most of which could be easily replaced with inline functions, constants, and typedefs. The function calling convention macros cannot be "redefined" so they will have to stay, although I do not see the calling convention macros as a major problem.

3. I would move everything from the global namespace into a subnamespace such as "win."

4. Redefine the typenames so that they are more accurate (DWORD is a word on IA-32?), cause less confusion (what is the difference between BOOL and BOOLEAN, anyway?). Also, why bother defining INT when we have a built-in type "int"? They did this for portability, but it would have been better to name them according to different things, such as "size_type" instead of DWORD, which is used as a general variable anyway.

5. Eliminate the redundance in the function declarations (how many instances of the word "window" can you find in the function declaration of CreateWindow?

6. Eliminate the (now useless) Hungarian Notation, and all the rest of the capitalization, which will be unnecessary after both 5 and 6 have been accomplished.

7. Make the exception classes derive from the C++ standard exception types to make them more flexible.

8. Eliminate the (now useless) container classes and replace them with either the standard container classes or adaptor classes.

9. Remove the 101 functions that do nearly the same thing as other functions, and possibly replace them with overloaded functions.

etc.


quote:Original post by Magmai Kai Holmlor

const_cast won’t work when the item in question is truly a compiled constant. In the case of a class property, this it isn’t so. You can set the value during construction to different values for each instance, or hack it up with a const_cast inside the class. There really is space allocated for it with each instance...

I thought reinterpret_cast was supposed to be used when you wanted a blind cast between incompatible types, something cryptic like a DWORD to a class pointer? I don’t see how it’s tied to the platform/compiler/os? What did you mean by platform-dependent?


reinterpret_cast copies bit patterns, of which there is very little guarantee in the standard. Code that is dependent upon reinterpret_cast is dependent in some way upon the bit patterns that represent the types being used. This introduces issues such as sizes of types, pointer alignment, byte order, and so on depending on how you use reinterpret_cast.

Try making a portable bitmap loader. It cannot be done. However, you can make it highly portable using typedefs, but that means conditional compilation. To write generally portable code - that is, guaranteed to work on all platforms without modification - you must use some interesting tricks.

First you must use malloc to allocate memory, since it guarantees that its memory will be suitably aligned for any type. C++''s new operator is more type-safe but it only guarantees alignment for the type you specify, which is not good since we''ll be casting. There is a way to use new to allocate memory like malloc, but why bother when malloc works and we have no use for constructor calls, etc.?

Second, you must look at numeric_limits::digits() to see how many bits are in a char. A byte is not guaranteed to be 8 bits, contrary to what many people think, nor can char even assumed to be a byte. So we must calculate the size of the code and take into consideration the number of bits used to represent char to get the proper amount of storage for the bitmap''s bits.

Third, since you can make no assumptions regarding the sizes of types used, and conditional compilation is out, you must use C++ bitfields to replace most bit operations. It is the only legal way to specify how many bits a given variable will use.

Most people do not want to deal with writing portable code, so they make a few of the more valid assumptions and write highly-portable code, which is usually just fine for what they want to do.


quote:Original post by Magmai Kai Holmlor

I guess you''ve forsaken portability to platform that don''t have native support for get/set tuples... but using a public property doesn''t mean you can never use accessors without changing the interface.


That is a poor excuse for convoluted code. First, you are telling the compiler that you will not modify the variable after its initial assignment, but then you go and use const_cast all over your class member function definitions. What if we have a data member that we want to give the following access:

public: read-only
protected: read-write
private: read-write

How do we specify that in the class definition with the const_cast syntax? The writer of the class ought to be able to specify access for its members, rather than having clients make assumptions based on what they think they ought to be able to access. And why is there ever a reason to write workarounds for non-portable code that could just as easily have been portable in the first place by using features built-in to the language and designed specifically for this purpose (const member functions)?

Does this all come down to "I do not like the parentheses used with accessors"? It cannot be a matter of additional typing, because accessors reduce typing.


quote:Original post by Magmai Kai Holmlor

You have to break some eggs to make an omelet. The worst thing that can happen to a project, is the coders become so afraid of breaking it that they are afraid to fix it. If you get a new version, you ought to expect new interfaces with a more effective design and better functionality. Otherwise, why bother updating? The code as is works, don’t change anything!


Why bother breaking the eggs if you can just as easily complete the project without breaking them? And it is really hard to fix a problem you never introduced by never having programmers become dependent on the internals of the library. If anything, hiding the representation of a class makes it easier to make changes, not harder. I do not understand the "problem" that developers who hide data must "fix".


quote:Original post by Magmai Kai Holmlor

There is no function to empty the entire container, since that’s a special case of removing a range, provided by the remove algorithm and the erase method.


I think that vector::clear() calls vector::erase(begin(), end()).


quote:Original post by Magmai Kai Holmlor

You may not want to be forced to reallocate the vector, but it’s a good idea to resize it adequately aforehand or at construction.


I think that you meant vector::reserve(), which allocates storage capacity but does not actually create new elements.
quote:Original post by null_pointer
That''s quite a feat! Perhaps I should try VB.

I will try to make a list of things I would change in MFC:

I think MFC is beyond saving, it fell into the "too afraid to break to fix" mentality a long time ago. I agree that the things you listed about MFC are less than ideal, but changing them would require dramatic design changes. A _new framework is needed (perhaps that was some of the motivation behind .NET?).

quote:
1. Remove the create/destroy tuples. These were created because constructors had no return value and so they had no means of reporting failure. Now that we have exceptions, they are useless and overcomplicate the class.

ehhh, I''d want more constructors that would let perform initialization during construction if you wanted to, but I think it''s important to keep those steps seperate; otherwise you either need to resort to placement new or keep everything on the heep (neither of which I''d want to do on a regular basis).

quote:
8. Eliminate the (now useless) container classes and replace them with either the standard container classes or adaptor classes.

Definetly, those things are useless.


quote:
reinterpret_cast copies bit patterns, of which there is very little guarantee in the standard. Code that is dependent upon reinterpret_cast is dependent in some way upon the bit patterns that represent the types being used. This introduces issues such as sizes of types, pointer alignment, byte order, and so on depending on how you use reinterpret_cast.
<snip>

Ah I see, reinterpret_cast _is platform dependant (but that''s not what it''s for).

quote:
Second, you must look at numeric_limits::digits() to see how many bits are in a char. A byte is not guaranteed to be 8 bits, contrary to what many people think, nor can char even assumed to be a byte. So we must calculate the size of the code and take into consideration the number of bits used to represent char to get the proper amount of storage for the bitmap''s bits.

_Really?_ Do any platforms currently exist where char isn''t one byte? Surely all bytes consist of 8 bits?

quote:
That is a poor excuse for convoluted code. First, you are telling the compiler that you will not modify the variable after its initial assignment, but then you go and use const_cast all over your class member function definitions. What if we have a data member that we want to give the following access:

Yes, the const thing is quiet ugly, I think I''ve only ever done it once. The example I gave above, I changed the public const into an accessor method, but the client still access the the variable as though it''s public. MSVC and BCB have extentions to do this (the way VB''s prop let''s work); so the client is really using the accessor when the write
int n = m_futzpah.size; 

That invokes m_futzpah.GetSize(); so even though you used a public property initially, you can still change it to an accessor method, without requiring clients to change anycode. So the arguement that public properties are bad, because you can''t change the behavior/implementation later as you could with accessors isn''t valid on the Windows platform (because you can change it).

quote:
Does this all come down to "I do not like the parentheses used with accessors"? It cannot be a matter of additional typing, because accessors reduce typing.

No, it comes down to propget/set leads to side-effects which break the logical fucntionality of the code.

PropSet not only hides frivilous implementation details, it also hides implementation behavior (the PropSet side-effect); the former can change with no problems, the later cannot.


quote:
I think that vector::clear() calls vector::erase(begin(), end()).

I think that you meant vector::reserve(), which allocates storage capacity but does not actually create new elements.

oops, maybe the names aren''t so great...
What happens if you call resize to a size larger than what''s reserved? I''d expect it to grow the vector for you. (I''ve used both resize and reserve too...)
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
quote:Original post by Magmai Kai Holmlor

...changing them would require dramatic design changes. A _new framework is needed...


My thoughts exactly!

I began work on a project to do just that a while ago, but stopped working on it because I was bored with it. Maybe I should restart it? Hmm...


quote:Original post by Magmai Kai Holmlor

ehhh, I''d want more constructors that would let perform initialization during construction if you wanted to, but I think it''s important to keep those steps seperate; otherwise you either need to resort to placement new or keep everything on the heep (neither of which I''d want to do on a regular basis).


Are you assuming that create/destroy tuples involve little or no overhead vs. construction? However, if you can construct invalid objects, then every member function must add a check to see whether it is valid before performing an operation - otherwise you cannot make the same guarantees as the normal C++ construction method.

If performance problems caused by creating objects on the free store is a concern, then just implement a new allocator or two. Some could even be included with the framework; it is really rather trivial if you know what to do.

BTW, how often do you actually use the "two-step" creation process, and when is it necessary or very convenient? IMO, reliance on it usually indicates other design flaws.


quote:Original post by Magmai Kai Holmlor

Ah I see, reinterpret_cast _is platform dependant (but that''s not what it''s for).


I do not understand why you make the statement in the parentheses. reinterpret_cast was designed to copy bit patterns and so since the bit patterns are platform dependent, so is all code that uses reinterpret_cast. That much we agree upon, right?

Now if you study code that has been heavily optimized in the sense that it is definitely not portable, you will find that casts are usually involved. Consider the bitmap-loader or binary stream I/O or anything else that depends upon these patterns.

reinterpret_cast was intended to be big and ugly for the express purpose of flagging the beginning of non-portable code. Whenever you see reinterpret_cast, warning bells should go off in your mind. Ingenious, really.

(Of course, it is easy to do something non-portable without casting by using the bit manipulation operators, but you cannot have everything...)


quote:Original post by Magmai Kai Holmlor

_Really?_ Do any platforms currently exist where char isn''t one byte? Surely all bytes consist of 8 bits?


Google, my friend. Google.

Do a search on:

"4-bit byte"
"8-bit byte"
"12-bit byte"
"16-bit byte"
"32-bit byte"

(quotes included)

Double-check my previous post - "portable" was defined as what the standard guarantees, not what we think that we can safely assume. The standard''s definition of type sizes is rather interesting; they say that "sizeof(char) == 1" and all other types are multiples of char; therefore, sizeof(sometype) returns its size in multiples of char, not bytes.


quote:Original post by Magmai Kai Holmlor

That invokes m_futzpah.GetSize(); so even though you used a public property initially, you can still change it to an accessor method, without requiring clients to change anycode. So the arguement that public properties are bad, because you can''t change the behavior/implementation later as you could with accessors isn''t valid on the Windows platform (because you can change it).


It:

1. is convoluted
2. is less portable
3. is less flexible
4. leads to more typing
5. is unnecessary
6. is misleading

When I see an accessor I hesitate to make assumptions about what it does, but when I write a simple assignment statement I certainly can and will make assumptions about its performance implications.


quote:Original post by Magmai Kai Holmlor

PropSet not only hides frivilous implementation details, it also hides implementation behavior (the PropSet side-effect); the former can change with no problems, the later cannot.


Given your use of the compiler "property" extensions, what does this actually add to your argument? If you argue that there is no real difference between accessors and "properties," then must they not have the same faults? Or will you shift your argument so that you do not advise using "properties," and then fail to argue that your const_cast method is generally useful?
quote:Original post by Magmai Kai Holmlor
ehhh, I''d want more constructors that would let perform initialization during construction if you wanted to, but I think it''s important to keep those steps seperate; otherwise you either need to resort to placement new or keep everything on the heep (neither of which I''d want to do on a regular basis).


I agree with keeping the two seperate, although I''m not so certain about having the constructors automatically call the initialization function. I guess you could write two constructors, one which calls the initialization function and one which doesn''t.

quote:Original post by null_pointer
Are you assuming that create/destroy tuples involve little or no overhead vs. construction?


I''d think it''s save to assume that create/destroy tuples offer no noticable overhead (two extra function calls). And when you must reuse that class it''s faster than a delete/new call.

quote:Original post by null_pointer
However, if you can construct invalid objects, then every member function must add a check to see whether it is valid before performing an operation - otherwise you cannot make the same guarantees as the normal C++ construction method.


I don''t get this one. What difference does it make if the initialization fails in the constructor or another function? You can throw an exception from any function, if that''s what you''d like to do.

The important thing to note is: If you initialize in your constructor (or your constructor calls in initialize function) then you are forced to use exception handling, or the above method must be used. However, it you force the user to call initialize manually then you have the option of throwing an exception or returning an error code.

Personally, I don''t like being forced, or forcing users of my library, to use certain language features.

quote:Original post by null_pointer
If performance problems caused by creating objects on the free store is a concern, then just implement a new allocator or two. Some could even be included with the framework; it is really rather trivial if you know what to do.


I think that needing to create new allocators because of the way you design your class should seriously make you rethink your class design. There are much simpler ways to solve this than requiring new allocators for classes.

Doing this also requires you to keep it on the heap, which is exactly why Magmai stated he didn''t want to do. This forces more memory allocation, allocators or not, and can increase chances of bugs.

The way I see it, there are two options:
1) Force initialization in the constructor, write custom memory allocators for the classes, force exception handling, jump through hoops to stop partially constructed objects when throwing an exception in a constructor, and force allocation on the heap just to reuse class.

2) Write two constructors, one doesn''t initialize, and one which does by calling the initialize function.

IMO, I think number two is better.


- Houdini
- Houdini

This topic is closed to new replies.

Advertisement