Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Constructors in C++ classes

  1. Mar 19, 2005 #1

    Math Is Hard

    User Avatar
    Staff Emeritus
    Science Advisor
    Gold Member

    I'm working on learning how to create classes in C++ and I'm still a little unclear on the syntax for constructor functions and access member functions.
    We've been using this class called "rational" for working with rational numbers. Header and implementation files are here.

    http://www.math.ucla.edu/~rclark/10a.1.05w/hw6/my/rational.h
    http://www.math.ucla.edu/~rclark/10a.1.05w/hw6/my/rational.cpp

    My main question is: in the header file, the constructor function looks like this:

    rational(int n=0,int d=1) : num(n), den(d) {reduce();}

    It creates a variable with two int parts: one for the numerator and one for the denominator.

    I don't understand what the colon means here. I know that the int n = 0 and d = 1 are default values, so does the colon mean use the default values unless the user has initialized a value for num or den or both?
    And I don't really understand why this assignment of n to num and d to den goes on outside of the curly braces. The only thing he does in the curly braces is call a function to reduce the fraction.
    Any comments are appreciated. I use MS VC++ version 6 if that matters.
    Thanks.
     
  2. jcsd
  3. Mar 19, 2005 #2

    Hurkyl

    User Avatar
    Staff Emeritus
    Science Advisor
    Gold Member

    Consider this conundrum:

    You have a class that doesn't have a default constructor. For example:

    Code (Text):

    class foo
    {
    public:
      foo(int number);
    };
     
    Now, normally when you'd use a `foo', you would call its constructor when declaring it:

    Code (Text):

    int main()
    {
      foo x(17);
    }
     
    In fact, you have to do so, because the `foo' class has no default constructor.

    But what if you wanted to use a foo as a private variable? e.g.

    Code (Text):

    class bar
    {
    private:
      foo x;
    public:
      bar(int num);
    };
     
    You can't create an `x' without initializing it, so there is a problem... and you can't know just how to initialize it until you're in one of your constructors. (You might want to initialize `x' with `num + 1', for example)

    That's what the colon syntax is for -- it is how you call the constructors for your member variables.

    Beyond this problematic case, it is generally considered good practice to initialize your member variables with this initialization syntax when possible.
     
  4. Mar 19, 2005 #3

    Math Is Hard

    User Avatar
    Staff Emeritus
    Science Advisor
    Gold Member

    Thanks, Hurkyl. I guess it's just confusing to me because it doesn't look like what I expect a "function" to look like.
     
  5. Mar 19, 2005 #4

    Hurkyl

    User Avatar
    Staff Emeritus
    Science Advisor
    Gold Member

    Oh, and a nitpick -- I can't see the code so I can't check, but shouldn't there be a test to ensure d != 0 in that constructor? Maybe reduce complains?
     
  6. Mar 19, 2005 #5
    It's also necessary to use the colon syntax if you're using inheritance and you want to specify which parent constructor the child should call.

    Basically, you use the colon syntax to specify any initialization that has to be done before the class is constructed. It's kind of messy, but it has some advantages.
     
  7. Mar 19, 2005 #6
    Reduce contains an assert(den != 0).
     
  8. Mar 19, 2005 #7
    Why is it num(n) and den(d)? num and den are variables, not functions!
     
  9. Mar 20, 2005 #8
    Because in an initializer list you specify which constructors to use for class members. Of course, primitive types like int don't really have constructors, but you're allowed to initialize them as if they had copy constructors.
     
  10. Mar 20, 2005 #9

    plover

    User Avatar
    Homework Helper

    It's also important to know the order in which the initializers will be evaluated. For example, the following definition will cause problems:
    Code (Text):
    class Thingy : public BaseThingy {
    public:
        Thingy(int endpoint_, int count_) : last(endpoint_), BaseThingy(), first(last - count_) { }
    private:
        int first;
        int last;
    };
     
    The reason this is a problem is that initializers in the constructor are not executed in the order they are listed, but rather the base class is always initialized first, followed by the other member variables in the order those variables are declared in the class definition. (Then the body of the constructor will be executed after all the members are initialized.) Thus the three initializers in the constructor for Thingy will be executed in this order:
    1. BaseThingy() // The base class constructor
    2. first(last - count_)
    3. last(endpoint_)
    The problem occurs when initializing the member first, as the expression calculating the value for first contains a reference to last—but last has not been initialized yet!

    The recommended practice is to always list the initializers in the order they will be executed.
     
  11. Mar 21, 2005 #10
    Better to avoid writing the derived class constructor that way! If there is a long list of attributes to initialize one should rather write the body of the constructor.
    Besides, you don't have to write the superclass constructor BaseThingy() when it is without arguments.

    Code (Text):
    class Thingy : public BaseThingy {
    public:
       Thingy(int endpoint_, int count_)
       {
           last = endpoint_;
           first = last - count_;
       }
    private:
        int first;
        int last;
    };
     
     
  12. Mar 21, 2005 #11
    Why is it better to initialize things inside the constructor? I agree that including calls to default constructors is rather pointless, but this rule about where you "should" put initialization sounds like your personal preference.
     
  13. Mar 21, 2005 #12

    plover

    User Avatar
    Homework Helper

    This was a just contrived example to show why knowing how initialization order works can be important. It is, of course, true that the BaseThingy constructor is not necessary, and I probably should have said that. The reason I included it was to show the different things that appear in an initializer list.

    However, I disagree that having a long list of initialized attributes is necessarily bad. For example, const and reference members must have initializers. Also, for many C++ experts (see for example Scott Meyers' Effective C++, Item #12), using initializers for member variables is the preferred practice for cases where an initializer is possible and the expressions necessary for producing the constructor arguments don't make the code unsafe or unreadable. The main reason for this is efficiency.

    Suppose you have the following:
    Code (Text):
    class StringyThingy {
    public:
        StringyThingy(std::string s_) { theString = s_; }
     
    private:
        std::string theString;
    };
     
    So what happens when a StringyThingy gets constructed? Well, all members will be initialized before the body of the constructor is executed. So first, the default string constructor will be called for theString. Then when the body of the constructor is executed, the desired value for theString will be assigned via the assignment operator. If an initializer were present setting theString to s_, then the copy constructor would be called directly.

    In many programs, this optimization will make no real difference, in others it will. To me, it's simpler to just use initializers consistently and not worry about it.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook

Have something to add?



Similar Discussions: Constructors in C++ classes
  1. C++ Matrix Class (Replies: 2)

  2. Java Class! (Replies: 129)

Loading...