Why use initializers for constructor functions in C++ classes?

  • C/C++
  • Thread starter Math Is Hard
  • Start date
  • Tags
    C++ Classes
In summary, the constructor for class Thingy looks like this:rational(int n=0,int d=1) : num(n), den(d) {reduce();}
  • #1
Math Is Hard
Staff Emeritus
Science Advisor
Gold Member
4,652
37
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.
 
Last edited by a moderator:
Technology news on Phys.org
  • #2
Consider this conundrum:

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

Code:
class foo
{
public:
  foo(int number);
};

Now, normally when you'd use a `foo', you would call its constructor when declaring it:

Code:
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:
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.
 
  • #3
Thanks, Hurkyl. I guess it's just confusing to me because it doesn't look like what I expect a "function" to look like.
 
  • #4
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?
 
  • #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.
 
  • #6
Hurkyl said:
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?

Reduce contains an assert(den != 0).
 
  • #7
Why is it num(n) and den(d)? num and den are variables, not functions!
 
  • #8
so-crates said:
Why is it num(n) and den(d)? num and den are variables, not functions!

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.
 
  • #9
It's also important to know the order in which the initializers will be evaluated. For example, the following definition will cause problems:
Code:
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.
 
  • #10
plover said:
Code:
class Thingy : public BaseThingy {
public:
    Thingy(int endpoint_, int count_) : last(endpoint_), BaseThingy(), first(last - count_) { }
private:
    int first;
    int last;
};

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:
class Thingy : public BaseThingy {
public:
   Thingy(int endpoint_, int count_) 
   {
       last = endpoint_;
       first = last - count_;
   }
private:
    int first;
    int last;
};
 
  • #11
ramollari said:
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.

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.
 
  • #12
ramollari said:
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.
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:
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.
 

1. What is a constructor in C++ classes?

A constructor in C++ classes is a special member function that is used to initialize the data members of a class when an object is created. It has the same name as the class and is automatically called when an object is instantiated.

2. How many types of constructors are there in C++ classes?

There are two types of constructors in C++ classes: default constructors and parameterized constructors. A default constructor does not take any arguments and initializes the data members to their default values. A parameterized constructor takes in one or more arguments and uses them to initialize the data members.

3. Can a constructor have a return type?

No, a constructor cannot have a return type, not even void. The purpose of a constructor is to initialize the data members of a class and it is automatically called when an object is created, so it cannot return any value.

4. What is the difference between a constructor and a regular member function?

A constructor is a special member function that is automatically called when an object is created and is used to initialize the data members of a class. A regular member function is called explicitly by the programmer and can perform any task or operation on the data members of a class.

5. Can a class have multiple constructors?

Yes, a class can have multiple constructors. This is known as constructor overloading. It allows a class to have different ways of initializing its objects depending on the arguments passed to the constructor.

Similar threads

  • Programming and Computer Science
2
Replies
36
Views
3K
  • Programming and Computer Science
Replies
25
Views
2K
  • Programming and Computer Science
Replies
23
Views
2K
  • Programming and Computer Science
Replies
2
Views
374
  • Programming and Computer Science
2
Replies
36
Views
2K
  • Programming and Computer Science
Replies
9
Views
1K
  • Programming and Computer Science
3
Replies
75
Views
4K
  • Programming and Computer Science
Replies
17
Views
1K
  • Programming and Computer Science
Replies
6
Views
8K
  • Programming and Computer Science
Replies
5
Views
813
Back
Top