tags:

views:

264

answers:

7

All of the constructors methods here do the same thing. I mostly use method2 but saw method3 for the first time today. Have seen method1 in some places but dont know what are the exact differences between them ? Which one is the best way to define constructors and why ? Is there any performance issues involved ?

  1 class Test
  2 {
  3     private:
  4         int a;
  5         char *b;
  6     public:
  7         Test(){};
  8         
  9         // method 1
 10         Test(int &vara, char *& varb) : a(vara), b(varb){}
 11         
 12         // method 2
 13         Test(int &vara, char *& varb)
 14         {
 15             a = vara;
 16             b = varb;
 17         }   
 18         
 19         //method 3
 20         Test(int &vara, char *& varb)
 21         {
 22             this->a = vara;
 23             this->b = varb;
 24         }   
 25         
 26         ~Test(){}
 27 };

I have here used simple fields int and char*,what will happen if I have many fields or complex types like struct ??

Thanks

+12  A: 

For the types you use, there will probably be no difference in performance. However for non-POD data (classes with constructors) the form:

Test(int &vara, char *& varb) : a(vara), b(varb){}

will be the most efficient. This is because non-POD data will be initialised whther you provide an initialisation list or not. The other forms, which use assignment, will take the hit for initialisation, and then another hit for assignment.

anon
@Neil : thanks, what is non-POD ??
seg.server.fault
As i said, basically types with constructors, assignment operators or destructors - things like std::string, std::vector and your own class types.
anon
Plain Old Data?
Ahmad Farid
@seg.server.fault: POD is for "Plain Old Data". See http://en.wikipedia.org/wiki/Plain_old_data_structures
sbi
On top of that, some types don't _have_ a default constructor, so you need to use the initializer list!
xtofl
+1  A: 

method 3 is usually used if you have something like

Test (int &a, char &b) 
{
    this->a = a;
    this->b = b;
}

in order to fully qualify that you're setting the class field a and b, it's basically the same as method 2.

ferrari fan
+4  A: 

Probably not a HUGE difference in terms of performance, but in method 1, you're constructing a and b using a copy constructor. In method 2, you're constructing a and b automatically, THEN using the assignment operator. That could be slower for more complex types than int and char*.

Method 3 is exactly the same thing as method 2.

+2  A: 

The first way, called "initialization lists", is the best. It is required for reference attributes, and for standard attributes is more efficient.

The second way will invoke the standard constructors for all attributes, and then use the assignment operator on each assigned attribute. It is slower, and potentially will break entirely if an attribute type does not support assignment.

The third way is exactly the same as the second, but needlessly verbose. Prefixing attribute access with this-> is poor style unless there is a local variable shadowing an attribute.


Unrelated to the question, there's no need to use references for basic types. You're adding additional overhead for pointer dereferences, and since the values are being assigned to non-references anyway, there's no point to it.

John Millikin
seg.server.fault
Huh? No, what he is saying is that references are for two things: Non-const references are for when you want to be able to modify the value of an argument and have that modification persist back at the caller. Const references are for passing a small handle to a large object (rather than copying the object around). The handles are just about as big as integers, and there is a small performance hit for getting the value from the handle, so there is no point to using references on integers, chars, or other small fundamental types.
Tyler McHenry
@Tyler : Thanks
seg.server.fault
+3  A: 

There is absolutely no difference between Methods 2 and 3. Within a member function (including constructors and destructors) a member variable x is synonymous with this->x. The only time this makes a difference is if there is another variable named x in a nearer scope. For example:

int Foo::memberFunc() {
  return x; // Returns member variable x. Same as return this->x.
}

int Foo::memberFunc(int x) {
  return x; // Returns the argument x. Need to say return this->x to return member x
}

Method 1 is normally the preferred way of initializing member variables because it makes it explicit that that is what you are doing.

In your case, it is also identical to methods 2 and 3, but in some cases it is not. In particular, if any of your member variables are constants, references, or objects without default constructors, then Method1 is the only way you can initialize them.

Another small difference of Method 1 is that if any of your member variables are objects, using Methods 2 or 3 they will be constructed with their default constructors first and then modified or assigned to int he constructor code, while with Method 1 you can create them with a constructor other than the default in the first place.

Tyler McHenry
What makes you think usage of the initializer list is ever 'identical' to using default construction followed by assignment?
xtofl
In the simple examples in the question, it is. In more complicated situations, it isn't.
David Thornley
+1  A: 

The first method (using initializer list) is the preferred way to initialize member variables of a C++ class. Its advantage is that it lets you choose which constructor to use for each member. The problem with the second method is that the default constrictors of the member fields will already be called before the body of the constructor is entered. This is also the only method of invoking a base class constructor, when the base class does not has a default constructor. The other two constructors are same, method 3 being the least preferred from a stylistic perspective.

Vijay Mathew
+1  A: 

They don't do the same thing.

Method 1 initializes the class members with the specified values.

Methods 2 and 3 first default-initializes the members (which for non-POD types means calling the default constructor), and then calls the assignment operator to assign a new value to them.

In other words, 2 and 3 will fail to compile if the class contains references, or if one of the members do not have a default constructor. It will also be slower for most non-POD types.

In other words, use the initializer list (method 1). That's what it's for. It is better from a correctness as well as a performance point of view.

jalf