Page 1 of 3

Constructor

Posted: May 12th, 2017, 1:53 am
by albinopapa
What does a constructor do?
The constructor of a class is responsible for initializing it's data members. It can do this either in the initializer list or the function body.


What's the initializer list?
The initializer list is the part of a constructor's definition that goes between the colon : and the curly-braces {} which is the function body. The purpose of the initializer list is to initialize data members of the class at the same time memory is allocated for the class object.


What is the difference betwen using the initializer list and the function body?
As stated, the initializer list initializes members when the memory is allocated for the class object, while using the function body is a second step. By the time the function body is reached, the memory is already allocated and if none of the members were manually initialized, then they were default initialized with garbage values. This means now you must assign the data members new data in the function body, which means using the initializer list is more efficient, but there are cases where you cannot use the initializer list.


What are those cases?
When you have an array or vector you want to initialize, then you are most likely going to want to use a for loop to initialize all the elements in the array. You can't do this in the initializer list and therefore need to use the function body.


How do you call constructors from a constructor?
Use the initializer list, the same as you would initialize a data member. There is a caveat to doing this though, if you call a constructor from the initializer list, you cannot use it for any other data members.

Example:

Code: Select all


class MyClass
{
public:
	// Compiler will create a default constructor using in-class initializers if provided and 
	// default constructors of objects.
	MyClass() = default;
	MyClass(float X, float Y, const std::string &Name)
		:
	x(X), y(Y), foo(0), bar(0), name(Name)
	{}
	MyClass(int Foo, int Bar, const std::string &Name)
		:
	x(0.f), y(0.f), foo(Foo), bar(Bar), name(Name)
	{}
	MyClass(float X, float Y, int Foo, int Bar, const std::string &Name)
		:
	MyClass(X, Y, Name)
	{
		// Now I have to assign Foo and Bar separately
		foo = Foo;
		bar = Bar;
	}
private:
	int foo = 0, bar = 0,
	float x = 0.f, y = 0.f;
	std::string name;
};
[/color]

Should you make a default constructor?
There are a couple of reasons to have one and maybe a couple to not have one.

Reasons for having a default constructor in your class would be:

  • You want to default initialize your object
  • You want an array or vector of those objects
  • You want to delay assigning values based on some conditions

Reasons for not having a default constructor in your class would be:
  • Your class should never be default initialized, all values must be initialized by outside sources through other constructors.
  • You have no constructors at all and your data is public, this allows for aggregate initialization
    • Ex: If Vec2 didn't have any constructors, you could do Vec2 pos = { 300,200 };
    • You can use the curly brace initialization with classes with private data as long as you have a matching constructor Vec2( float X, float Y ), otherwise aggregate initialization doesn't work.
  • Your class is a singleton and must be instantiated through an Instance() function for instance lol.

Re: Constructor

Posted: May 24th, 2017, 4:26 am
by cyboryxmen
I personally prefer putting the list next to the signature:

Code: Select all

constexpr Vector3 ( const float x, const float y, const float z ) : x_ { x }, y_ { y }, z_ { z }
{
}

Re: Constructor

Posted: May 24th, 2017, 6:07 am
by albinopapa
cyboryxmen wrote:I personally prefer putting the list next to the signature:

Code: Select all

constexpr Vector3 ( const float x, const float y, const float z ) : x_ { x }, y_ { y }, z_ { z }
{
}
The only problem with that is the length of the line. My environment for instance, being visual impaired, is maxed at 100 columns, so I try to stick with the golden rule of 80 characters per line if possible. That way I get to see everything without having to scroll horizontally.

I sometimes do this though.

Code: Select all

constexpr Vector3 ( const float x, const float y, const float z ) 
     : x_ { x }, y_ { y }, z_ { z }
{
}

Re: Constructor

Posted: May 24th, 2017, 8:50 am
by cyboryxmen
Also, you can totally initialize an array in the initialization list:

Code: Select all

constexpr Set ( ) : set_{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
{
}

Code: Select all

Set ( ) : set_{ RandNum ( ), RandNum ( ), RandNum ( ), RandNum ( ), RandNum ( ), RandNum ( ), RandNum ( ), RandNum ( ), RandNum ( ), RandNum ( ) }
{
}
This is from the C++11 standard and is tested with Visual Studio 2015.

Re: Constructor

Posted: May 24th, 2017, 9:05 am
by LuisR14
albinopapa wrote: I sometimes do this though.

Code: Select all

constexpr Vector3 ( const float x, const float y, const float z ) 
     : x_ { x }, y_ { y }, z_ { z }
{
}
this is my preferred style

(and i somehow thought that one could initialize arrays in the initializer list, as cyboryx just showed us)

Re: Constructor

Posted: May 24th, 2017, 9:16 am
by cyboryxmen
While we're at it, you can also set the default value of the member variable at the class declaration C# style.

Code: Select all

class Set
{
public:
	Set ( ) = default;
	Set ( int ( &set ) [ 10 ] ) : set_ { set [ 0 ], set [ 1 ], set [ 2 ], set [ 3 ], set [ 4 ], set [ 5 ], set [ 6 ], set [ 7 ], set [ 8 ], set [ 9 ] }
	{
	}

	int Get( const std::size_t index ) const
	{
		return set_ [ index ];
	}

private:
	int set_ [ 10 ] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	int useless_var_ { 12 };
};

int main ( )
{
	int arr [ 10 ] { 1, 1, 1, 1, 1, 6, 1, 1, 1, 1 };
	const Set set { arr };

	std::cout << set.Get ( 5 ) << std::endl;

	system ( "pause" );
	return 0;
}
As you can see, the initializer list overrides the default value you set in the member variable declaration.

Re: Constructor

Posted: May 24th, 2017, 9:34 am
by albinopapa
I've never seen this before

Code: Select all

void Func(int( &st )[ 10 ]){}
How is this even working?

Re: Constructor

Posted: May 24th, 2017, 9:36 am
by albinopapa
This is the code you used:

Code: Select all

	Set( int( &set )[ 10 ] ) : set_{ set[ 0 ], set[ 1 ], set[ 2 ], set[ 3 ], set[ 4 ], set[ 5 ], set[ 6 ], set[ 7 ], set[ 8 ], set[ 9 ] }
I understand the initialization of the set_ member using the init list, the part I don't understand is the parameter list, the int(&set)[10] part.

Re: Constructor

Posted: May 24th, 2017, 9:44 am
by cyboryxmen
I guess most people who don't do metaprogramming wouldn't have much knowledge nor use for array types. When you deal with arrays, they would usually decompose into pointers. However, you need to realise that arrays are not just pointers but are their very own type. Instead of decomposing them into pointers, you can get the array itself with the array type Type( &name )[ SIZE ]. Add an & and you'll get the reference type. I use this a lot for metaprogramming with overloading and type specifications.

Re: Constructor

Posted: May 24th, 2017, 9:54 am
by albinopapa
@Luis, yes, you can init arrays in the init list. The reason I made that statement is 1) You probably aren't going to have such small arrays. Initializing an array of 100+ items in the init list is a waste of time. 2) If it is an array of simple types, it may be reasonable, but if it's an array of user defined types with a few parameters in the constructors, still unreasonable. 3) I was heading in the right direction in the first sentence, but by the second sentence, I forgot about braced initialization.

Before C++11, it wasn't possible if the class didn't have a default constructor, but now you can use braced initialization on arrays of classes that don't have default constructors.

Code: Select all

class MyStuff
{
public:
	MyStuff( float X, int A )
		:
		x( X ), a( A )
	{
	}

	float X()const { return x; }

	int A()const { return a; }
private:
	float x = 0.f;
	int a = 0;
};

class MyStuffContainer
{
public:
	MyStuffContainer()
		:
		stuff{ { 1.f, 0 }, { 2.f, 1 }, { 3.f, 2 }, { 4.f, 3 }, { 5.f, 4 },
		{ 6.f, 5 }, { 7.f, 6 }, { 8.f, 7 }, { 9.f, 8 }, { 10.f, 9 } }
	{
	}
	const MyStuff &operator[]( int Idx )const { return *( stuff + Idx ); }
private:
	MyStuff stuff[ 10 ];
};
int main()
{
	MyStuffContainer stuff;
	for( int i = 0; i < 10; ++i )
	{
		std::cout << stuff[ i ].A() << "    " << stuff[ i ].X() << std::endl;
	}
	
	system( "pause" );
	return 0;
}