Thread: Declare object without initializing

  1. #46
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by C_ntua View Post
    An uninitialized object does exist in C++. It just has an empty constructor. If that is what you want, then use that (along with an initialization function).
    Where there's a will, there's a way.

    It is possible to pre-allocate a buffer, where an object can later be constructed.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  2. #47
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    It is possible to pre-allocate a buffer, where an object can later be constructed.
    If the constructor has not been called, the object does not exist. (It doesn't mean anything if you have a "do nothing" constructor and a secondary "init" method. Without the constructor, the "init" method doesn't exist as part of the object.) The existence of memory where an object may eventually reside doesn't advance the notion that you can have an "uninitialized" object. Using a punned pointer to an area in memory where an object, not a "built in", has not been constructed is undefined. (It has been a while. It may be truly illegal instead of just undefined.)

    My object that managed [...] thing to be cleaned up.
    This sounds like a major failure of the design.

    Let me see if I have this "correct".

    1) You have an object (from now called D3DDM) managing ("ACDD" and other facilities) a Direct3D device.

    2) You have an object (from now called D3DRS) managing ("ACDD" and other facilities) the rendering states of the device managed by D3DDM.

    3) D3DDM has no knowledge of D3DRS. D3DRS has only the knowledge of D3DDM necessary to for the facilities relating to D3DDM to function correctly.

    4) D3DDM is a singleton, or traditional global, implemented in such a way that the compiler or standard library destroys the object. (A global instance in a translation unit, a singleton using `atexit' to register a "free me" function, or some such facility.)

    5) D3DRS is a singleton, or traditional global, implemented in such a way that the compiler or standard library destroys the object and requires, as a client of D3DDM, that D3DDM is "ready to go" during the use but not necessarily during construction. (A global instance in a translation unit, a singleton using `atexit' to register a "free me" function, or some such facility.)

    Under the assumption that my understanding is correct, the only way I see this failing (at any point of execution) is that if both objects aren't treated as potentially uninitialized (not constructed) objects, a broken `atexit' (or similar) implementation, or badly designed objects. (If either objects isn't treated as a true singleton. If `atexit' doesn't apply the functions in reverse order they were registered. If any object managing, exporting, more than one "globally accessible facility with state" treats them as independent during creation but dependent during destruction.)

    D3DRS, as an eventual client of D3DDM, must assume that D3DDM is in a valid state as long as D3DRS exists. D3DDM, if treated as a true singleton, must necessarily exist at least as long as its oldest client. I want more information about this.

    Soma

    ACDD: Allocation Construction Destruction Deallocation

    My object that managed various D3D render states was a singleton which worked great....until shut down. At shut down the object managing the device would cleanup the device long before the singleton would get cleaned up. So even though the render state object would attempt to clean up correctly release had already been called on the device. At this point the device was in an invalid state and leaked every COM object that was allocated by the singleton object. Not good. Hard to avoid though since if the device is cleaned up before any other D3D COM allocations the allocations will be leaked. The device must be the last thing to be cleaned up.

  3. #48
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by phantomotap View Post
    If the constructor has not been called, the object does not exist. (It doesn't mean anything if you have a "do nothing" constructor and a secondary "init" method. Without the constructor, the "init" method doesn't exist as part of the object.) The existence of memory where an object may eventually reside doesn't advance the notion that you can have an "uninitialized" object. Using a punned pointer to an area in memory where an object, not a "built in", has not been constructed is undefined. (It has been a while. It may be truly illegal instead of just undefined.)
    You seem to be arguing semantics, when my point was about function. Whether you call a preallocated block an uninitialized object or not, it does conceptually provide such an interpretation. Which goes to my larger point that C++ has no lack of power.

    Undefined is illegal. It's the harshest criterion on runtime behavior in the standard.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  4. #49
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Function? An object doesn't exist until it is created. An object doesn't exist after it is destroyed. An object that has not been created can not be uninitialized because it doesn't ........ing exist. That is the functionality.

    End of story.

    Undefined is undefined. Illegal is illegal.

    Soma

  5. #50
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    Speak with some code cause I think you are just using different words for the same thing.
    The point is that an uninitialized object can exist.

    I have to say that someone can argue thought that the above is not true for an object statically allocated, but only for an object dynamically allocated. Because as initialization one could count allocation as well. Thus considering an object initialized when memory for all its member is allocated (w/o a specific value give).
    In this case, MK27 has a point, and you could only achieve that with a pointer and dynamical allocation. And the whole problem is indeed a scope problem, since you lack the ability just to declare an object for scoping reasons. That could be possible, but don't know how useful...

  6. #51
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by phantomotap View Post
    Function? An object doesn't exist until it is created. An object doesn't exist after it is destroyed. An object that has not been created can not be uninitialized because it doesn't ........ing exist. That is the functionality.

    End of story.
    If you define it that way, then it's semantics. And beside the point. Logically a buffer allocated for later construction is an uninitialized object. It's something someone can use to get the functionality they want when they ask questions like in the OP. (not that doing so is a good idea for the OP)

    Undefined is undefined. Illegal is illegal.

    Soma
    I suppose then by illegal you mean the compiler is required to give an error diagnostic. This won't happen from trying to access a pre-allocated buffer, because doing so requires a reinterpret_cast, which is allowed.

    Quote Originally Posted by C_ntua View Post
    I have to say that someone can argue thought that the above is not true for an object statically allocated, but only for an object dynamically allocated. Because as initialization one could count allocation as well. Thus considering an object initialized when memory for all its member is allocated (w/o a specific value give).
    In this case, MK27 has a point, and you could only achieve that with a pointer and dynamical allocation. And the whole problem is indeed a scope problem, since you lack the ability just to declare an object for scoping reasons. That could be possible, but don't know how useful...
    No, you can pre-allocate a buffer on the heap, on the stack, or in global memory. It's useful for memory pools and things like std::vector. The buffer will generally have to be a char buffer if on the stack, but the size_t version of new returns a void * that can be used. Then placement new is used to call the constructor.
    Last edited by King Mir; 03-23-2010 at 06:01 AM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  7. #52
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    The problem is that when using the singleton pattern you cannot rely on destructors for proper cleanup if those singletons make use of other singletons or pointers to objects managed by other singletons for their operations. You can end up with dangling pointers and references quite easily during cleanup. This is because the order of cleanup is not guaranteed. To get around this you would implement a cleanup function that would be called prior to actual object destruction which would then allow your system to order the cleanup correctly. Most game design books recommend not relying on constructors for any init besides operations that cannot fail and recommend not relying on destructors for object cleanup b/c the order of cleanup becomes undefined to the programmer since the compiler controls the order.

    Here is a good article on the issue at hand:
    http://www.research.ibm.com/designpa...s/ph-jun96.txt

    But back to constructors and initialization:
    Essentially relying on constructors for initialization is ok given that none of the init operations can fail. If they can fail your only option is exceptions which work fine when handled correctly and propogated correctly but trying to throw exceptions across a module boundary is fraught with problems. It can be done given certain restrictions are met. However given the nature and design of DLLs is to act as units of logic, IE: libraries, that can be replaced long after the release of a product there is no guarantee that the restrictions are going to be met. Assuming the user of the DLL and the DLL are both compiled on the same compiler version is a huge assumption in most complex systems.

    Just because an object has been created in memory does not guarantee it is ready for use unless you completely init the object in the constructor which means you may have to end up throwing a lot of exceptions from it and using std::auto_ptr or boost pointers to ensure memory allocated prior to the throw point is cleaned up. There are many times when objects may require a significant amount of setup prior to usage that may or may not be done in the constructor. Sometimes at the point of construction the object cannot be setup completely for usage or perhaps it is unwieldy to pass in a whole bunch of parameters to the constructor just to 100% init the object for use. Now in the constructor you can set pointers to 0, values to 0, etc, etc. which means the object has been properly constructed....but the pointers that are 0 cannot yet be used. They have just been init to 0 but they don't point at anything meaningful and b/c of this no function that relies on these pointers can be called. This is the 'invalid state' that has been talked about and it is definitely a problem with the two step approach. However complete init in the constructor is fraught with just as many problems so it's really a matter of picking your poison b/c neither approach is 100% satisfactory in all cases.
    Last edited by VirtualAce; 03-23-2010 at 11:36 PM.

  8. #53
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    If you define it that [...] questions like in the OP.
    Unfortunately I've had enough beating my head against the wall, I guess you just have to continue that nonsense without me.

    The problem is that [...] since the compiler controls the order.
    From my post:

    the only way I see this failing (at any point of execution) is [...] a broken `atexit' (or similar) implementation[...].
    It is perfectly safe to rely on destructors for proper cleanup. For a singleton you only need to control when the destructors are called. This could be said to be the same as a "destroy" method and a separate destructor. I'm not going to argue with that beyond saying that, in a singleton, neither is a necessary, or valid, part of the public interface.

    Here is a good article on the issue at hand:
    I have no interest of an article written about the implementation of singletons in C++ from before the standard was published. That said, he article has several errors and invalid techniques. (An example would be "double checked locking".)

    If they can fail your only [...] with problems.
    This is true, but has nothing to do with singletons. Or, rather, the problem isn't innate to singletons.

    Assuming the user of the DLL and the DLL are both compiled on the same compiler version is a huge assumption in most complex systems.
    And yet, since there is no standard ABI, a perfectly valid assumption. Even something as simple as a single option to the compiler during compilation of either the library or the client can break compatibility if not used during the compilation of both the library and the client. If you can't assume a compatible ABI you can't export any C++ object through the library. If you can assume a standard ABI, and the ABI allows "thread jumping", you can safely throw exceptions from library to client and back.

    Just because an object has [...] point is cleaned up.
    There is nothing wrong with relying on constructors to do what they were designed to do. This is much the same as destructors. If you need to create an object with a constructor that can raise exceptions while "hampering" the exceptions, you only need to create a new instance of the object within a `try' block.

    Using exception safe primitives like `std::auto_ptr<???>' is a good idea in general. I can't imagine why you are complaining about it.

    There are many times when [...] satisfactory in all cases.
    There is always at least one less problem with constructors that a separate "init" method has: I must remember to call this separate "init" method. You say "You only need to clearly document that the "init" method must be called."? I say that you only need to clearly document that construction can fail and therefore may raise an exception.

    I will never use an object directly that has a two stage "init" process. I always wrap it in a class that does this for me. I'm sure most C++ programmers would eventually.

    Also, if an object has to be "100%" initialized for use, and this "initialization" requires many parameters, you can't get around these necessary parameters by separating their assignment into multiple functions. An "init" method doesn't save you from complexity of implementation; it only moves it around.

    Soma

  9. #54
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    It is perfectly safe to rely on destructors for proper cleanup. For a singleton you only need to control when the destructors are called. This could be said to be the same as a "destroy" method and a separate destructor. I'm not going to argue with that beyond saying that, in a singleton, neither is a necessary, or valid, part of the public interface.
    We obviously do not see eye to eye here.

    I have no interest of an article written about the implementation of singletons in C++ from before the standard was published. That said, he article has several errors and invalid techniques. (An example would be "double checked locking".)
    A few invalid techniques does not invalidate the entire text of the article.

    There is always at least one less problem with constructors that a separate "init" method has: I must remember to call this separate "init" method. You say "You only need to clearly document that the "init" method must be called."? I say that you only need to clearly document that construction can fail and therefore may raise an exception.
    Which is not possible in all circumstances. Unfortunately when you develop on a platform developed by Microsoft they really do not care about the standard or what things should do. It is generally a bad idea to throw exception across a module boundary in Win32. Period. I do not know about other platforms or OS's or how they deal with the issue.

    And yet, since there is no standard ABI, a perfectly valid assumption.
    An assumption that could be extremely costly later down the line. You must work within the confines of your target platform. Working against it won't do much good for anyone involved.
    And what is an ABI?

    Also, if an object has to be "100%" initialized for use, and this "initialization" requires many parameters, you can't get around these necessary parameters by separating their assignment into multiple functions. An "init" method doesn't save you from complexity of implementation; it only moves it around.
    I did not say it was not without it's faults. You are claiming it is an invalid approach and I am saying it is not as straightforward and simple as you make it out to be.

    This is true, but has nothing to do with singletons. Or, rather, the problem isn't innate to singletons.
    I did not say it did. If you notice in my post I make this statement prior to what you have quoted.
    But back to constructors and initialization:

    Anyways I'm done arguing with you over this since there isn't a clear cut answer or a reason to continue. Do what you will but realize that not everything fits into nice little neat molds as you are claiming they do. I would invite you to think a bit before making all encompassing statements about a language that pretty much allows you to do anything you want even if it isn't the best idea in the world.

  10. #55
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    A few invalid techniques does not invalidate the entire text of the article.
    I offered you a mechanism that is guaranteed to work if you have a standard `atexit' and follow the trivial documentation of "Call the `get_singleton' method in the constructor of dependent objects.".

    An article posing "While this obviates the need for destroyers, the real problem remains. Garbage collection, anyone?" just isn't interesting.

    And what is an ABI?
    O_o

    Anyways I'm done arguing with you over this since there isn't a clear cut answer or a reason to continue.
    Considering the above, that's probably for the best.

    Do what you will but realize that not everything fits into nice little neat molds as you are claiming they do.
    ^_^

    You are the only person who said "nice" or "neat". I offered something that works. You are using something that obviously does not work. Hah! Congratulations on finding an implementation that isn't "nice", "neat", or functional.

    I would invite you to [...] best idea in the world.
    Here is an example of why that comment was stupid: "Memory leaks are bad!", a statement falling into the "all encompassing" category and yet absolutely true even if that's what some bozo want's to do.

    Soma

  11. #56
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    And what is an ABI?
    O_o
    At least I'm honest. I'm not going to act as if I know what you are talking about when I don't. Honestly I don't use that term or abbrev. often. I thought you might have meant API and made a typo.

    But you are obviously only open to your own thoughts so I will leave you with them.
    Last edited by VirtualAce; 03-25-2010 at 05:18 PM.

  12. #57
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by MK27 View Post
    I think it is an even worse idea to treat a general principle as a law. Yes, the reason there are special members called constructors is to construct the object, and obviously these work well in most cases, but there is also freedom to deal with things differently if appropriate.* And "ready to use" could mean a lot of things -- all of those meanings will be qualified "ready to use properly", as in, ready to use like this or like that, but "obviously I didn't mean used improperly". Eg, an empty vector<string> is not "ready to use" in sorting; you have to push some data into it first (after the constructor call).

    * If the authors had intended that to be impossible, it would be.
    I'd be careful with such sayings in C/C++. Obviously you can do really bad things™, and the language won't hinder you (eg throwing an exception in a destructor).
    With C/C++, you have to remember that the languages gives you the gun, with the safety off! If you don't know how to use, you will eventually shoot yourself in the foot
    The language won't stop you from doing that. It just provides you with the gun. The tools!

    Quote Originally Posted by C_ntua View Post
    An uninitialized object does exist in C++. It just has an empty constructor. If that is what you want, then use that (along with an initialization function).

    What is the difference of doing
    Code:
    try {
      Object o;
    ....
    }
    o.val = 1;
    rather than
    Code:
    Object o;
    try {
       o.init();
    ....
    }
    o.val = 1;
    just one line of code which is essential in the end to see in what scope the object o is. If the first example was valid then in order to find the type of o and where it is declared you would have to look in all try blocks. Which you can avoid the way the language is now. So you cannot have everything in the end.
    Except now you've brought up the two-stage construction again, which laserlight pointed out is a double-edged sword.

    Quote Originally Posted by Bubba View Post
    For certain programming tasks it is not recommended to rely on constructors and destructors for initialization and destruction or cleanup. We all know the inherent problems with constructors operations failing and if you are inside of a DLL the problem only gets worse. When it comes to destruction the problem can be magnified if you are using singletons and the like. Yes, the compiler is guaranteed to call the destructor for your class, however, it does not guarantee the order in which the destructors for objects will be called which can cause problems in more complex systems that may rely on a certain destruction order. It would be nice if these could be avoided but unfortunately this is not a perfect world. One example of where I got burned by this:

    My object that managed various D3D render states was a singleton which worked great....until shut down. At shut down the object managing the device would cleanup the device long before the singleton would get cleaned up. So even though the render state object would attempt to clean up correctly release had already been called on the device. At this point the device was in an invalid state and leaked every COM object that was allocated by the singleton object. Not good. Hard to avoid though since if the device is cleaned up before any other D3D COM allocations the allocations will be leaked. The device must be the last thing to be cleaned up.

    The two stage setup/shutdown process IMO are perfectly valid approaches so long as it is well documented what is going on and as long as all objects in the system conform to the same creation/destruction specification. It is not without its own perils and the invalid state during construction argument is a perfectly valid argument against it.
    I don't like relying on singletons at all (some do call it an anti-pattern, after all). After better yet, I like to avoid globals, because there is no way to tell when they're constructed and destructed. I like deterministic order, so I would define everything I could, in order, inside your main function and provide pointers/references to them.
    Just a thought.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Initializing constant object member of a class
    By Canadian0469 in forum C++ Programming
    Replies: 3
    Last Post: 12-03-2008, 08:05 PM
  2. Telling a shared_ptr not to delete object?
    By TriKri in forum C++ Programming
    Replies: 5
    Last Post: 08-16-2008, 04:26 AM
  3. ERRPR: Object reference not set to an instance of an object
    By blackhack in forum C++ Programming
    Replies: 1
    Last Post: 07-13-2005, 05:27 PM
  4. Set Classes
    By Nicknameguy in forum C++ Programming
    Replies: 13
    Last Post: 10-31-2002, 02:56 PM
  5. Set Classes
    By Nicknameguy in forum C++ Programming
    Replies: 3
    Last Post: 10-21-2002, 07:40 PM