Thread: How much to do in a contructor

  1. #1
    Registered User carrotcake1029's Avatar
    Join Date
    Apr 2008
    Posts
    404

    How much to do in a contructor

    I am very new to C++ and am trying to learn how most people do things.

    I have made a class and am wondering how much is too much in a constructor and what is considered bad practice. What I want to do with this class is (just for now) create a socket ready to listen on a server.

    Here is how I am currently doing it.
    Code:
    class server_socket
    {
        public:
            server_socket(short int port)
            {
                fport = port;
                fsock = 0;
                memset(&faddr, 0, sizeof(struct sockaddr_in));
    
                /* Create the socket */
                create();
                if (!isvalid()){return;}
                bind(fport);
                if (!isvalid()){return;}
                listen();
                if (!isvalid()){return;}
               //After I create the class I also call isvalid() in the function as well
            }
            ~server_socket()
            {
                close(fsock);
            }
    
            bool isvalid(void)
            {
                return (fsock != -1);
            }
    
            bool send(const char *);
            int recv(char *);
    
        private:
            int fsock;
            short int fport;
            struct sockaddr_in faddr;
    
            bool create(void);
            bool bind(const int port);
            bool listen(void);
            bool accept(int *fsock);
    };
    I also don't know if it is typically a good idea to "return" out of a constructor.

    I'm all ears right now because I am trying to write code a standard C++ programmer typically would. Moving from C to C++ has quite a few differences to say the least. Still haven't moved into <string> yet.

  2. #2
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    If construction fails, it is better to throw an exception, rather than to create an error when the user tries to use the socket. The programmer should not need to call isValid() after creation. Nor should he need to do so before send() and recv().

    Otherwise, looks good.
    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.

  3. #3
    Registered User carrotcake1029's Avatar
    Join Date
    Apr 2008
    Posts
    404
    Oh, I forgot how useful that was. I forgot some of my old OO practices from PHP.

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    You should NOT ever return early from a constructor [1]. If you can not obey that principle, then the code should not be part of the constructor [I'm sure someone will come up with a useful exception to this rule - but the general rule certainly is "do not return before end of construction" (aside from footnote 1)]. Throwing an exception, as King Mir says, would be a plausible alternative to splitting the construction.

    In this particular case, I'd say your constructor should set member variables, and a "connect" or "initialize_connection" or whatever you want to call it, should deal with the socket create, bind, listen, etc. You can then also return an error message to say "didn't work", and not have to call "is_valid" from the code using the class.

    Also, if bind, listen, create are all functions returning bool, why are you not checking the return value?

    [1] Obviously, if there are situations where some argument is optional (e.g. you may pass a NULL or a char */std::string pointer, and if the value is NULL, then there is less work to do, and this work is done last, then you would be allowed to return early after sufficient work besides the copying of the string or whatever the operation would be to deal with the optional parameter).

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Registered User carrotcake1029's Avatar
    Join Date
    Apr 2008
    Posts
    404
    I'm having a little trouble understanding how I would try/catch when creating a class variable, because the second I do it, the constructor is called.

    This doesn't work. I get some errors about braces and this and that.
    Code:
    int main (void)
    {
        server_socket ssck;
    
        try (ssck)
        {
            std::cout << "Socket Creation Successful!" << std::endl;
        }
        catch (err)
        {
            if (err == errcreate)
            {
                cout << "Error at create()" << endl;
            }
        }
    
        return 0;
    }
    I know I've deviated off of the original topic but we've kind of gone a new direction.

    --Edit. Nevermind, I'm a bit rusty on the syntax. I got it though. sorry.

    Thnaks again for your suggestions!
    Last edited by carrotcake1029; 04-11-2009 at 07:32 PM.

  6. #6
    Registered User carrotcake1029's Avatar
    Join Date
    Apr 2008
    Posts
    404
    Sorry for the double post but I have hit a block. I think I am doing what you guys suggested here but can't quite get it. Obviously the way I have my code currently won't work, but I hope you guys see what I am trying to achieve.

    In the contructor I call a private function, which will return a boolean. Inside the constructor, I check that boolean value, and if false, I throw an exception, which is from inside the constructor.
    I just am unsure what to put in the try block.

    Code:
    int main (void)
    {
        try
        {
            server_socket ssck (80);
        }
        catch (int err)
        {
            if (err == ssck.errcreate)
            {
                std::cout << "Error at create()" << std::endl;
                return -1;
            }
        }
    
        std::cout << "Socket Creation Successful!" << std::endl;
    
        return 0;
    }
    Thanks for any help!

  7. #7
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    That looks almost right. Just move the "Socket Creation Successful!" message inside the try block as well since you don't want that to print when it failed.
    I assume you are simply throwing an int?
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Code:
    int main (void)
    {
        try
        {
            server_socket ssck (80);
        }
        catch (int err)
        {
            if (err == ssck.errcreate)
            {
                std::cout << "Error at create()" << std::endl;
                return -1;
            }
        }
    
        std::cout << "Socket Creation Successful!" << std::endl;
    
        return 0;
    }
    YUCK! GROSS! NO! WRONG! BAD! EVIL!

    The purpose of an exception: TO BE INFORMATIVE!

    Now, let's step back a bit from this design and consider a good approach by following the advice you've been given:

    Code:
    class socket_error:
       public std::runtime_error
    {
       // ???
    };
    Code:
    class socket_invalid_port:
       public socket_error
    {
       // ???
    };
    Code:
    class server_socket
    {
    // ???
       server_socket(short int port):
          fport(port)
       {
          // ???
          if(!validate_port())
          {
             throw(socket_invalid_port(port));
          }
       }
    // ???
    }
    Code:
    int main()
    {
       try
       {
          server_socket ssck (80);
          // if execution gets here
          // we have a valid socket
          // full stop
          // don't check anything
          // just use the socket
          std::cout << "Socket Creation Successful!" << std::endl;
          return(0);
       }
       catch(const socket_error & error)
       {
          std::cout << error.what() << std::endl;
          return(-1);
       }
    }
    Soma

  9. #9
    Registered User carrotcake1029's Avatar
    Join Date
    Apr 2008
    Posts
    404
    Thank you phantomotap. I think I will get more descriptive later, but right now, I need to get the simplest version working so I can build off of it. I will most assuredly reference your post when I am ready to expand upon it.

    Back to what I had originally, I apologize that I failed to mention the error I was receiving:
    error: ‘ssck’ was not declared in this scope|
    And yes iMalc, I am just throwing an int.
    Code:
    //Constructor
    server_socket::server_socket(short int port)
    {
        errcreate = 1;
    
        fport = port;
        fsock = 0;
        memset(&faddr, 0, sizeof(struct sockaddr_in));
    
        /* Create the socket */
        if (screate() == true){throw errcreate;}
    }
    I set it to true for testing.

    errcreate is defined under public:

    Edit: The main thing I am worried about is the scope of the class variable, which is only in the try block.
    Last edited by carrotcake1029; 04-11-2009 at 09:20 PM.

  10. #10
    Registered User
    Join Date
    Apr 2008
    Posts
    890
    Quote Originally Posted by carrotcake1029 View Post
    Edit: The main thing I am worried about is the scope of the class variable, which is only in the try block.
    Use a pointer if you want the socket to exist in the scope outside of the try-catch block.

    Code:
    socket * sck;
    
    try {
        sck = new socket(80);
    } catch (socket_err & se) {
        // log error
        throw AnotherException;
    }
    
    // sck visible here

  11. #11
    Registered User carrotcake1029's Avatar
    Join Date
    Apr 2008
    Posts
    404
    Thank you. Why didn't I think of that? :P

  12. #12
    The larch
    Join Date
    May 2006
    Posts
    3,573
    The try block doesn't need to be around object creation only but may be around a larger scope, perhaps even in a function higher up the call stack.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  13. #13
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    What you do in a constructor depends on your object oriented approach. If you want true RAII then you can init everything in the constructor and throw when/if they fail. One problem here is the example of if you allocate 2 objects on the heap successfully but the third one fails and you throw, you just leaked 2 objects on the heap. std::auto_ptr can help out with this as well as the boost smart pointers so it's not a huge issue if you handle it right.

    The other approach is to initialize a class in a two-step process. The general rule here is don't perform any action in a constructor that could possibly fail. Another way of saying this is only perform operations that are guaranteed to succeed. Then you create an Init() function or Create() and it performs all the setup required for the object. Benefits to this approach is you know that an object will be created every time you call the constructor. Downside is it may not be very clear without some code spelunking on how to setup and create the object correctly. I would still call this a form of RAII because you are initializing prior to usage but via a two-step process.

    Normally I use the latter approach because it is much simpler and does not require all the try catch blocks every time you create an object and there is no room for error when it comes to leaking memory since the constructor does not allocate any memory. Sometimes this approach has to be used in Win32 code because even though your C++ object exists your actual Win32 object may not which can cause problems.

    Inevitably which approach you choose to use is up to you and I cannot say that one is more right or more wrong than the other. In an existing code line I would follow along the lines of what has already been done to maintain consistency.
    Last edited by VirtualAce; 04-12-2009 at 11:51 AM.

  14. #14
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Normally I use the latter approach because it is much simpler and does not require all the try catch blocks every time you create an object
    I adhere very strictly to the former approach, and the only try-catch blocks I have in my project are at COM boundaries. I consider two-stage construction something to be avoided if possible. (And it usually can be avoided.)
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  15. #15
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    If you use two-step process or more, then the object is is a non-usable state after the constructor, so it usually means extra work in making sure you can't use the object before the initialization process is complete or you might invoke undefined behavior.
    An ideal constructor will make the object ready for use after it's finished.
    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. calling the contructor again
    By symbiote in forum C++ Programming
    Replies: 10
    Last Post: 05-09-2009, 08:54 AM
  2. a data member that has no default contructor?
    By symbiote in forum C++ Programming
    Replies: 5
    Last Post: 03-29-2009, 03:06 AM
  3. easy Vector contructor question
    By noodle24 in forum C++ Programming
    Replies: 5
    Last Post: 04-21-2006, 07:43 PM
  4. Quick Question regarding Contructor Variables
    By cram in forum C++ Programming
    Replies: 3
    Last Post: 12-05-2004, 08:29 PM
  5. contructor not allocating array properly?
    By BrianK in forum Windows Programming
    Replies: 4
    Last Post: 07-13-2004, 05:02 PM