Thread: Declare object without initializing

  1. #31
    Registered User
    Join Date
    Mar 2010
    Posts
    15
    Quote Originally Posted by laserlight View Post
    I am right, because I talked about avoiding initialisation, not construction. If you can arrange to use members whose constructors do not initialise them (or initialise them to a known invalid state), then you can do two stage construction.
    You're always free to do nothing in your constructors, in particular in your default ones, but that's bad programming practice and contrary to the spirit of the language. The reason why C++ has special member functions called constructors is - surprise, surprise! - to construct the object. Which means initializing it, to use oldfashioned terminology. When an object has been constructed it should be ready to use. Don't break the unwritten laws of the language.

  2. #32
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by MK27
    Well, it's a server, one of a few variations which all use the same base class. So each such server program contains one server object providing the base functionality.
    Right. Actually, now I see what you are getting at. The thing is, if an exception is thrown, it must be handled somewhere. This means that if an exception might be thrown you should have at least one set of try/catch in your program, e.g., in the main function.

    Quote Originally Posted by mustermeister
    You're always free to do nothing in your constructors, in particular in your default ones, but that's bad programming practice and contrary to the spirit of the language. The reason why C++ has special member functions called constructors is - surprise, surprise! - to construct the object. Which means initializing it, to use oldfashioned terminology. When an object has been constructed it should be ready to use. Don't break the unwritten laws of the language.
    I agree. As I noted: "this means that it is possible to have objects with invalid state, and this can be Bad Thing."
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #33
    Registered User
    Join Date
    Mar 2010
    Posts
    15
    Quote Originally Posted by MK27 View Post
    Not if the member object is unessential. As I said, in this case it's optional based on a parameter. If the construction fails, I can just leave the pointer null and the class method will function as if that option had not been selected in the first place.
    Strange thinking, MK. If you don't need it, why not leave it out in the first place?

  4. #34
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by mustermeister View Post
    You're always free to do nothing in your constructors, in particular in your default ones, but that's bad programming practice and contrary to the spirit of the language. The reason why C++ has special member functions called constructors is - surprise, surprise! - to construct the object. Which means initializing it, to use oldfashioned terminology. When an object has been constructed it should be ready to use. Don't break the unwritten laws of the language.
    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.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #35
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by mustermeister View Post
    Strange thinking, MK. If you don't need it, why not leave it out in the first place?
    Specifically, in this case it's a log. You can start the server object with some logging enabled (of events which take place within the server class methods). But if you don't want those things logged, you don't enable logging. The logging is handled by a separate logger class, so the server class has a member object of it.

    I suppose I could have written the logger class to do nothing when initialized, but then you fall into that other caveat (an object should be ready to use). So in fact it is initialized with a file name which it attempts to open append in the constructor (or it throws). If it is not going to be used, no log file is submitted to the server and the logger is never initialized.
    Last edited by MK27; 03-21-2010 at 04:02 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  6. #36
    Registered User
    Join Date
    Mar 2010
    Posts
    15
    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.
    No, an empty vector<string> is as good as any other vector<string>. Any function taking such an argument should work equally well whether empty or not.

    It's not possible to make all abuse impossible/illegal. The compiler cannot stop you from writing programs that crash at run time.

  7. #37
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by MK27
    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).
    Note that there are other constructors for a vector that may make sense if it is immediately going to be sorted after construction. Furthermore, an empty vector is not in an invalid state, e.g., you can safely do:
    Code:
    std::vector<std::string> strings;
    std::sort(strings.begin(), strings.end());
    Quote Originally Posted by MK27
    So in fact it is initialized with a file name which it attempts to open append in the constructor (or it throws). If it is not going to be used, no log file is submitted to the server and the logger is never initialized.
    In my opinion, it makes sense to have a member (smart) pointer which you initialise to be a null pointer. In the constructor body, you check that say, the string provided is not an empty string, and if so you create the object and make the member pointer point to it. If an exception is thrown, you allow it to propagate from the constructor.

    EDIT:
    I have also thought of suggesting the option of a pointer parameter as it would facilitate polymorphism, e.g., the caller can provide a logger object of his/her choice that inherits from some abstract base logging class, or a null pointer, but this may be rather unnecessary.
    Last edited by laserlight; 03-21-2010 at 04:17 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #38
    Registered User
    Join Date
    Mar 2010
    Posts
    15
    Quote Originally Posted by MK27 View Post
    Specifically, in this case it's a log. You can start the server object with some logging enabled (of events which take place within the server class methods). But if you don't want those things logged, you don't enable logging. The logging is handled by a separate logger class, so the server class has a member object of it.

    I suppose I could have written the logger class to do nothing when initialized, but then you fall into that other caveat (an object should be ready to use). So in fact it is initialized with a file name which it attempts to open append in the constructor (or it throws). If it is not going to be used, no log file is submitted to the server and the logger is never initialized.
    The contradiction here is that you say the logging is optional, but the log object is constructed unconditionally. You should only construct it when the option of logging has been chosen. A solution is to have a child server class WITH log object, while the mother has none. If logging is chosen, you create the child, other wise the mother. Another solution is to use a pointer to a log object instead of a log object.

  9. #39
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    I haven't read everything, but most of it, but I don't see one very obvious suggestion. It might not work in all cases, but generally:
    Code:
    ObjectType obj;
    try {
      obj = ObjectType(param1, param2, ..., paramn);
    }
    ...
    Of course, that needs a copy constructor for the object, but it allows you to use the design you wish. Maybe it won't optimize the extra copy away though, but I think it might.
    Of course, this might be problematic in classes that can't be copied. But most classes should be copyable. Otherwise you should just make a function and not use the constructor.

  10. #40
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by EVOEx
    that needs a copy constructor for the object
    That would use the copy assignment operator, not copy constructor. I guess that it is a variant of two stage construction.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #41
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by mustermeister View Post
    The contradiction here is that you say the logging is optional, but the log object is constructed unconditionally.
    No, it is not constructed at all if it is not required, which was why I wanted to know if you could have uninitialized objects in C++. There is nothing obviously wrong with the idea, but when in Rome...so I'm using a member pointer.

    C++ is actually the 4th language I've done OOP in, and most of the others had dynamic typing, so eg in perl:
    Code:
    package WhatEver;   // a package is a class
    our $logobj;  
    sub new {  // the constructor
        $logobj = Logger->new();
    So this allows $obj to be a public member of a WhatEver, but it is initialized inside the WhatEver constructor.*

    It's a scoping problem, is all.

    Quote Originally Posted by laserlight View Post
    In my opinion, it makes sense to have a member (smart) pointer
    Okay, what's a "smart" pointer? I guess I can look this one up myself, but comments are always appreciated.

    *Actually, $logobj could be initially declared as 0:
    Code:
    our $logobj = 0;
    because of dynamic typing. Then later it is easy to check if logging is enabled or not:
    Code:
    if ($logobj)....
    but you don't need dynamic typing in order to have an uninitialized object, I would think (altho of course in the perl example $logobj is not such a thing, that's what it would be if the typing were static).
    Last edited by MK27; 03-21-2010 at 04:48 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  12. #42
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by MK27 View Post
    Yes, that was the complete program in finished form right there

    The intent was primarily to identify the exact error. The runtime reporting (on my system) for an uncaught exception is totally uninformative. It just says "int thrown". Which int? As you can see, there are three possibilities. To identify the error, I need to catch it.

    The reason I highlighted "You can only use valis if construction succeeds" is because that's not true. You can't use it even if it succeeds, because it goes out of scope with the try block.
    Right, the language makes it almost impossible to use an object that may not have been constructed. This is good. Your program as you have drafted has a big bug: you are trying to use valis (call acptlist and waitOnCall) when you might not have a valid valis object. The language stops you.

    You can use valis in a context where is has surely been constructed, namely inside the try block.

    Quote Originally Posted by MK27 View Post
    Nah -- the whole program is about the valis object, I'm not putting it all into a try block. I suppose a try is not really needed, the program has to fail anyway. Except that it does allow the exception to be identified -- without a try/catch it says nothing beyond the fact that "an exception occurred".
    What's wrong with the whole program being in a try block? It's a good practice. If an error occurs, you can log the error and return from main. You don't go on with the program trying to use an invalid object.
    Last edited by King Mir; 03-21-2010 at 05:26 PM.
    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.

  13. #43
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by MK27 View Post
    Okay, what's a "smart" pointer? I guess I can look this one up myself, but comments are always appreciated.

    *Actually, $logobj could be initially declared as 0:
    Code:
    our $logobj = 0;
    because of dynamic typing. Then later it is easy to check if logging is enabled or not:
    Code:
    if ($logobj)....
    but you don't need dynamic typing in order to have an uninitialized object, I would think (altho of course in the perl example $logobj is not such a thing, that's what it would be if the typing were static).
    This is a good use case for a (smart) pointer:

    Code:
    boost::scoped_ptr<Log> logobj = 0;
    if(use_log)
      logobj = new Log();
    
    //later
    if(logobj)
      logobj->logthis("error");
    A smart pointer is like a regular pointer, except you don't have to call delete. There's more to it, but you can look it up.
    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.

  14. #44
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    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.

  15. #45
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    But this means that it is possible to have objects with invalid state, and this can be Bad Thing.
    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.
    Last edited by VirtualAce; 03-21-2010 at 06:46 PM.

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