What is the difference between this two constructor?
int x, y; //position
BasePoint(int px, int py) : x(px), y(py) {}
and
int x, y; //position
BasePoint(int px, int py)
{
x = px;
y = py;
}
What is x(px), y(py) called? And When do I use this type of variable initialization?
Thanks.
First one is doing initialization using initialization-list, and second one is doing assignment using assignment operator.
First one is recommended!
BasePoint(int px, int py) : x(px), y(py) {}
^^^^^^^^^^^^^ this is called initialization-list!
Read this FAQ : Should my constructors use "initialization lists" or "assignment"?
The FAQ answer starts with :
Initialization lists. In fact,
constructors should initialize as a
rule all member objects in the
initialization list. One exception is
discussed further down [...]
Read the complete answer.
What is x(px), y(py) called?
These are called initializer lists. What you are actually doing is copying the value of px to x and py to y.
Uses:
class foo
{
private:
int numOne ;
public:
foo(int x):numOne(x){}
};
class bar : public foo
{
private:
int numTwo;
public:
bar(int numTwo): foo( numTwo ), // 1
numTwo(numTwo) // 2
{}
};
bar obj(10);
1. Notice that derived constructor's argument can be passed to base class constructor.
2. Compiler can resolve, in this case, which one is argument and which one is member variable. Had if, this needs to be done in the constructor,then -
bar::bar(int numTwo) : foo( numTwo)
{
this->numTwo = numTwo; // `this` needs to be used. And the operation is called assignment. There is difference between initialization and assignment.
}
BasePoint(int px, int py) : x(px), y(py) {}
here u are using the initialization list
so the object when constructed will not go inside the body and initiate those values.it saves time by not entering in the body of the constructor
Another use of this is when calling the derived class constructor.
where if you use the statement like
new derivedclass(a,b,c)
and you can write this
derivedclass(int x,int y,int z):baseclass(a,b),h(z){}
Related
I am currently trying to create a constructor that takes in two variables, yet the program has to be able to create an object with only one variable. If the object being constructed is missing the second variable it must initialize it to zero.
constructor(int x, int y)
Example:
constructor a (5, 5)
constructor b(0)
You can have multiple constructors for one class that have different signatures:
class Foo {
public:
int x, y;
Foo() : x(0), y(0) {}
Foo(int _x) : x(_x), y(0) {}
Foo(int _x, int _y) : x(_x), y(_y) {}
}
Foo a(5); // a.x == 5, a.y == 0
Foo b(5, 5); // b.x == 5, b.y == 5
Just like regular functions, the parameters of a constructor can have a default value. Using
constructor(int x, int y = 0) : x_var(x), y_var(y) {}
Allows you to call the constrcutor with either one or two parameters. You can even use
constructor(int x = 0, int y = 0) : x_var(x), y_var(y) {}
which now lets you call the constructor with zero, one, or two parameters.
C++ has a nice new feature:
struct Point{
int x;
int y;
int z;
};
Point p{.x=47, .y=1701, .z=0};
But if I add a constructor then I am forbidden from using the nice designated initalizers syntax:
struct Point{
Point(int x, int y, int z = 0): x(x), y(y), z(z){}
int x;
int y;
int z;
};
static Point p{.x=47, .y=1701, .z = 0};
error: designated initializers cannot be used with a non-aggregate
type 'Point'
Am I missing something obvious(why it would be terrible if designated initalizers worked with structs/classes that have public members, but are not aggregates) or this is just a missing feature that is just not added to the standard?
Aggregate initialization (including initialization with designed initializers) circumvents the constructor of the class.
This is not a problem for aggregates, since they aren't allowed to have user-defined constructors. But if you allow this kind of initialization for classes with user-provided constructors (that do something useful), it can be harmful.
Consider this example:
class A
{
static std::map<A *, int> &Indices()
{
static std::map<A *, int> ret;
return ret;
}
public:
int dummy = 0;
A(int index)
{
Indices().emplace(this, index);
}
A(const A &) = delete;
A &operator=(const A &) = delete;
~A()
{
auto it = Indices().find(this);
std::cout << "Deleting #" << it->second << '\n';
Indices().erase(it);
}
};
If you were able to do A{.dummy = 42};, you'd get UB in the destructor, and would have no way to protect against this kind of usage.
Designated initalizers where a feature lifted from C. Most C++ compilers are also C compilers, and it was a C feature first.
They added a restriction (that the initializers be in order) and applied it to C++ types that matched C types, and got it into C++. Most major C++ compilers already had it as a C++ extension (without the restriction); the restriction was checked with the compiler implementors as being reasonable, and then the "cost" of adding the feature was really low.
Once you have a constructor, it becomes a larger language issue. Does the initializer refer to the constructor arguments? If yes, we run into the problem that argument names are not unique. If no, then how do we handle it when the constructor sets a value and the initializer set a different value?
Basically we need function-arguments-by-name to get sensible designated initializers with constructors. And that is a new feature, not one simply lifted from C.
The workaround (for named arguments) is:
struct RawPoint{
int x = 0;
int y = 0;
int z = 0;
};
struct Point {
Point( int x_, int y_, int z_ = 0 ):
x(x_), y(y_), z(z_)
{}
explicit Point( RawPoint pt ):
Point( pt.x, pt.y, pt.z )
{}
int x, y, z;
};
then you can do:
Point pt( {.x=3} );
by accessing the RawPoint's designated initializer feature.
This is the same way you can have designated initializers in function calls.
This also works:
struct Point:RawPoint {
Point( int x, int y, int z = 0 ):
RawPoint{x,y,z}
{}
explicit Point( RawPoint pt ):
RawPoint( pt )
{}
};
This question already has answers here:
What is this weird colon-member (" : ") syntax in the constructor?
(14 answers)
Closed 4 years ago.
class Sales_data {
public:
Sales_data(int i, int j, int k) : x(i), y(j), z(k) {
}
private:
int x,y,z;
};
In the above code(more specifically in Sales_data constructor(recited below)), I don't understand the use of colon and comma separated list.
Sales_data(int i, int j, int k) : x(i), y(j), z(k) {
}
I have never seen a colon(":") following any function/constructor parameter list. What does this colon mean/signify here ?
Moreover, what is this comma separated list after colon ?
You may be confused, because the member variable name (x) is same as the function parameter (also x), which you can always avoid for clarity. Simplified code can look like so.
add_x(int x1) : x(x1) // a contructor that initializes the member vaiable x to x1
{
}
Still confused? then you can go for this ( not so optimize though)
add_x(int x1)
{
x = x1;
}
This is a constructor
This is not a standard function/method. Each class (struct) can have constructor(s). The constructor has the same name as the class and can optionally take parameters.
struct add_x {
int x;
add_x(int x) : x(x) {} // This is a constructor with one paramter
};
To make it easier to read let us format it better.
struct add_x {
int x;
add_x(int x) // Constructor that takes one argument.
: x(x) // This is called the initializer list.
{} // This is the body of the constructor.
};
The initializer list allows you to list out the member variables (comma separated) and initialize them before the body of the constructor is executed.
In this case the member x is initialized with the parameter x.
#include <iostream>
int main()
{
add_x test(5);
std::cout << test.x << "\n"; // Should print 5
// Note in my example I have removed the private
// section so I can read the value x.
}
This question already has answers here:
What is this weird colon-member (" : ") syntax in the constructor?
(14 answers)
Closed 5 years ago.
I saw this piece of code
class Foo {
public:
int x;
double y;
float z;
Foo(int x, double y, float z)
// XXX: What are the following doing?
: x(x),
y(y),
z(z) {}
}
I'm guessing what was written after the : is assigning the values to the members (x, y, z ) from the arguments.
Question 1: Why that way and not the following?
Foo(int x, double y, float z) {
this->x = x;
this->y = y;
this->z = z;
}
Question 2: in the original style, how does the compiler distinguish the member x from the parameter x? (In my modified pice of code, the qualifier this was used for that purpose)
For example, if I were to write this:
Foo(int x, double y, float z)
// XXX: What are the following doing?
: x(x * 2),
y(x * 3 + y ), // which 'x' is the 'x' in 'x * 2'?
z(z) {}
}
You guess correctly. Those are member initializer lists. The reason this is preferred over directly assigning in the constructor is because it is necessary for const class members, classes with no default constructors, and reference variables. Example:
class A
{
int b;
public:
A(int c) : b(c) {}; // No default constructor
};
class Test
{
A a; // A class with no default constructor
const int x; // A constant
int& r; // A reference
public:
Test(int d) : a(5), x(2), r(d) {};
};
int main()
{
Test test;
}
In this example, both members a and x of class Test cannot be assigned to because the first has no default constructor, while the latter is constant, meaning assignment to it would create an error. Finally, since reference variables cannot be assigned to, an initializer list is needed.
In the end, the primary difference comes down to the fact that initializer lists initialize the variable (who knew?), while assignment in the constructor is done after those member variables' constructors have been called.
As for your second question, you can see for yourself that the parameters are picked before the member variables during the initialization.
After studying the member copying and the assignment operator in C++,
and looking at " member copying of class " which explains the conditions where default assignment operator cannot be generated. I am not very clear about the concepts as the following example I tried actually works on g++4.5
#include<iostream>
using namespace std;
class Y{
int& x;
const int cx;
public:
Y(int v1,int v2)
:x(v1),cx(v2)
{}
int getx(){return x;}
int getcx(){return cx;}
};
int main()
{
int a = 10;
Y y1(a,a);
Y y2 = y1;//assignment
cout<<y1.getx()<<" "<<y1.getcx();
return 0;
}
So where am I not getting the concepts. Please suggest other examples (if possible) so that I can understand better.
Y y2 = y1; is not an assignment. It's a copy constructor call. If you declare and initialize a variable on the same line, a one parameter constructor is called with the right hand side of the equals sign as the parameter. There's nothing about Y that prevents the default copy constructor from being instantiated (and called).
Try the following:
Y y1(10, 10);
Y y2(11, 11);
y2 = y1;
This should fail, although I can't test it right now.
class Y{
int& x;
public:
Y(int v1,int v2)
:x(v1),cx(v2)
{} // v1 ceases to exist from this point
};
x is a reference variable to an int. Now you are initializing it to v1, which means x is an alias to v1 itself. The scope of v1 is in the constructor alone. With that said -
Y y2 = y1;//assignment => Not assignment. It is initialization.
is equivalent to
Y y2(y1); // compiler is looking for the overloaded constructor ( ie. copy constructor in this case ).
class Y{
public:
Y ( const Y & other ); // copy constructor
// ...
};
#include<iostream>
using namespace std;
class Y{
int& x;
const int cx;
public:
Y(int v1,int v2)
:x(v1),cx(v2)
{}
int getx(){return x;}
int getcx(){return cx;}
};
int main()
{
int a = 10;
Y y1(a,a);
Y y2 = y1;//NOT assignment. Object is yet to be constructed, so calls copy c'tor
y2 = y1; // assignment operator is called
cout<<y1.getx()<<" "<<y1.getcx();
return 0;
}
/*
D:\Workspaces\CodeBlocks\Test\main.cpp||In member function 'Y& Y::operator=(const Y&)':|
D:\Workspaces\CodeBlocks\Test\main.cpp|4|error: non-static reference member 'int& Y::x', can't use default assignment operator|
D:\Workspaces\CodeBlocks\Test\main.cpp|4|error: non-static const member 'const int Y::cx', can't use default assignment operator|
D:\Workspaces\CodeBlocks\Test\main.cpp||In function 'int main()':|
D:\Workspaces\CodeBlocks\Test\main.cpp|20|note: synthesized method 'Y& Y::operator=(const Y&)' first required here |
||=== Build finished: 3 errors, 0 warnings ===|
*/
Your class contains members which cannot be default-constructed or assigned, namely:
References
Constants
Therefore, no default constructor or assignment operator can be implied for your class. For example, you have to write your own constructor:
class Foo
{
const int a;
int & b;
public:
Foo(int val, int & modify_me) :
a(val) , // initialize the constant
b(modify_me) // bind the reference
{ }
};
It is clear that you cannot default-construct Foo (i.e. Foo x;). It is also clear that you cannot reassign objects of class Foo (i.e. x = y;), because you cannot reassign references or constants.
By giving your class a reference or a constant member, you actually confer reference or constant semantics on the class itself, if you will, so this should be a fairly immediate logical consequence. For example, reassignment probably doesn't even make sense semantically, because your class is supposed to embody a constant concept.
However, note that you can make copies of your class: That's because you can make "copies" of references (i.e. furhter aliases), and copies of constants. Therefore, a copy constructor is available implicitly, by simply applying copy-construction member-by member. So you can say:
int n;
Foo x(15, n);
Foo y(x);
Foo z = x; // these two are identical!
This results in two further objects y and z which have y.a == 15 and z.a == 15, and y.b and z.b are all references to n. (Don't be confused by the two alternative syntaxes at the end; both invoke the copy constructor.)