I came across a piece of code that had structs and I am now thoroughly confused about how automatically the constructor of the struct is being called. I made a dummy code just to understand the concept. For example,
struct obj {
int a = 0, b = 2;
obj (int aa) {
a = aa;
}
obj (int aa, int bb) {
a = aa;
b = bb;
}
int getSum () {
return a+b;
}
};
void calcSum (obj o) {
cout << o.getSum() << endl;
}
int main()
{
calcSum(5);
return 0;
}
This does print 7. I believe that the single argument constructor of struct obj is automatically being called. If so, how can I make it automatically call the double argument constructor? I am unable to do so.
What is the name of this functionality?
Also please guide me to some good articles as I was unable to find any.
You can use a braced initialization list to invoke the constructor:
calcSum({5, 3});
A somewhat broad term for this feature of C++ is called "implicit conversion". If the type of the object being "assigned to" (or constructed, which in this case is a function parameter) does not match the type of the object being "assigned from", C++ has a long, long list of rules of implicit conversions that can be used to convert one type to another.
One of these rules involves a comma-separated list of values in braces. If it is specified for an object with an explicit constructor, the constructor get used to construct the object.
Note that this also invokes the same constructor as in the original code:
calcSum({5});
A constructor with one parameter does not need braces to be implicitly invoked.
Related
I want to initialize member object variables in the default constructor of the class.
Let's consider the following,
class ABC {
ABC(int A, int B) {
a = A;
b = B;
}
int a;
int b;
};
class Foo {
Foo();
ABC m_obj1;
};
From the above example, I would like to initialize "obj1" in "Foo::Foo()".
One of the restrictions I have is that I cannot do so in the initializer list, as I need to do some computation before I could initialize the member. So the option available (ASFAIK) is to do so in the body of the default constructor only.
Any inputs, how could I do this?
Edit: Restricting to C++11
Would this be a correct way,
Foo:Foo() {
int x = 10;
int y = 100;
m_Obj1(x, y); //Is this correct? <--------
}
Depending on your exact problem and requirements, multiple solutions might be available:
Option 1: Use a function to do the computations and call Foo constructor
Foo makeFoo()
{
// Computations here that initialize A and B for obj1 constructor
return Foo(A, B)
}
Option 2: Call a function that does the computations and initialize obj1 in Foo member initializer list
ABC initABC() {
// Some computations
return ABC(A, B)
}
Foo() : obj1(initABC()) {}
Option 3: Dynamically allocate obj1, for instance with a std::unique_ptr
Option 4: Use std::optional or an emulated c++11 version as shown by other answers
You simply call the base constructor inside the initializer list of the derived constructor. The initializer list starts with ":" after the parameters of the constructor. See example code!
There is no problem to call functions inside the initializer list itself.
int CallFunc1(int x) { return x*2; }
int CallFunc2(int y) { return y*4; }
class ABC {
public:
ABC(int A, int B):a{CallFunc1(A)},b{CallFunc2(B)} {
std::cout << "Constructor with " << a << " " << b << " called" << std::endl;
}
private:
int a;
int b;
};
class Foo {
public:
Foo(): obj1(1,2){}
Foo( int a, int b): obj1(a, b){}
private:
ABC obj1;
};
int main()
{
Foo foo;
Foo fooo( 9,10);
}
edit:
The best method I can think of for your case is a copy constructor, being more specific on what you need to store helps a lot since if it is just two ints inside a class dynamic allocation is not worth it, the size of the object being constructed makes a difference to what method is best, copy constructors can be slower with much larger data types as the object has to be created twice: once when it is automatically constructed in the parent objects constructor and again when a temporary object is created and all the values have to be copied, which can be slower then dynamically allocating if the size is larger.
As far as I'm aware all objects in a class are automatically initialized/allocated in the constructor so sadly dynamic memory use may be your best bet here.
If you are fine with having the object initialized but empty so you know it is not 'ready' yet you can later fill it with useful data when you would have wanted to initialize it. This can be done with default constructors that set the things inside the object to null values or something similar so you know the object hasn't been properly initialized yet. Then before using the object you can check whether it has been initialized by checking for the null values or by having put a bool in the object that tells you whether it is initialized. Dynamically allocated would still be better in my opinion and makes the code look cleaner overall as all you need to store is a null pointer until the object is needed and then allocated and set to the pointer. It is also very easy to check if the pointer is equal to nullptr to know the state of your object.
Dynamically allocating memory may be a hassle since you have to make sure to get rid of memory leaks and it is slightly slower than using the stack, but it is a necessary skill for c++ since the stack is not enough when making programs that use more than the few available megabytes of data on the stack so if you are doing this simply to avoid the hassle I recommend learning it properly. It would be nice if you could be more specific about what kind of object you want to do this with or if you just want an answer that works for most cases.
eg:
*ABC obj1 = nullptr;
...object is needed
obj1 = new(ABC(constructor stuff));
...obj1 isn't needed
delete obj1;
or c++ automatically deletes it when the program closes.
I overloaded the operator "=" to do something, but instead it goes and uses the constructor
class Date
{public:
int x;
public:
Date(int v1)
{
x = v1+1;
}
Date& operator=(Date& d)
{
x = x - 1;
}
public:
~Date() {};
};
int main()
{
Date d = 1;
cout << d.x;
//delete d;
return 0;
}
I was expecting to print 0 but instead it prints 2(uses the constructor). Why is that? Also why won't it let me delete d? it says it must be a pointer to a complete object type.
It should get priority, because this
Date d = 1;
Is not assignment, it's an object declaration with initialization. Initialization of class objects in C++ is the domain of constructors. Don't let the syntax (using = 1 as an initializer) confuse you.
To get the assignment operator called, the left hand side must be an existing object whose initialization already happened. Assignment only applies to preexisting objects. So if you add a statement like this:
d = 1;
It could call the assignment operator as you expect (after some other errors are fixed).
Also why won't it let me delete d? it says it must be a pointer to a complete object type.
The error seems pretty self explanatory to me. You can only call delete on a pointer operand. And the pointer must be pointing at an object previously create with new.
I found this example interview question and would like some help understanding it:
#include <iostream>
class A
{
public:
A(int n = 0)
: m_n(n)
{
++m_ctor1_calls;
}
A(const A& a)
: m_n(a.m_n)
{
++m_copy_ctor_calls;
}
public:
static int m_ctor1_calls;
static int m_copy_ctor_calls;
private:
int m_n;
};
int A::m_ctor1_calls = 0;
int A::m_copy_ctor_calls = 0;
void f(const A &a1, const A &a2 = A())
{
}
int main()
{
A a(2), b = 5;
const A c(a), &d = c, e = b;
std::cout << A::m_ctor1_calls << A::m_copy_ctor_calls;
b = d;
A *p = new A(c), *q = &a;
std::cout << A::m_copy_ctor_calls;
delete p;
f(3);
std::cout << A::m_ctor1_calls << A::m_copy_ctor_calls << std::endl;
return 0;
}
The way I understand it, the first line of main creates two new objects, resulting in 2 calls to the constructor. In the second line, I see that they use the copy constructor for c(a) and e = b. The copy constructor isn't used for &d = c because it is only referencing c is that right? Also one thing I don't understand is that if the copy constructor requires a reference, how come an object is being passed into it instead of a reference to the object? The parts after with pointers are really confusing to me. Can someone provide some insight?
Thanks!
The copy constructor isn't used for &d = c because it is only
referencing c is that right?
Yes. d becomes an alias for c.
Also one thing I don't understand is that if the copy constructor
requires a reference, how come an object is being passed into it
instead of a reference to the object?
An object passed into a function taking a reference is automatically "converted" into a reference. The parameter is now an alias for the passed-in object.
The parts after with pointers are really confusing to me. Can someone
provide some insight?
p points to a newly allocated A object, copied from c. q points to a. (1 copy constructor). Then p is deleted.
f(3) gets fun. It constructs a temporary A initialized with 3 to bind to a1. a2 gets a temporary default constructed A. After f(3) finishes, these two temporaries are destroyed.
End of the function, and the remaining instances of A are destroyed.
Outside of an interview, you can stick this code in an IDE and step through it with a debugger.
In case you were wondering, here is the output (with spaces added):
2 2 3 4 3
Hey this is a really basic question and but I got confused about it. Say I created an object
MyObject a.
It comes with a copy constructor, so I know I can do this:
MyObject b(a);
But can I do this?
MyObject& b(a);
And if I do this:
MyObject b = a; what is in b? Apology if this question is too fundamental to be bothered posting.
Doing MyObject& b(a) has nothing to do with the copy constructor. It just creates b which is a reference to the object a. Nothing is copied. Think of b as an alias for the object a. You can use b and a equivalently from then on to refer to the same object.
MyObject b = a; will use the copy constructor, just as MyObject b(a); would.
There are two forms of initialisation: T x = a; is known as copy-initialization; T x(a) and T x{a} are known as direct-initialization.
When T is a reference type, it doesn't matter which type of initialisation is used. Both have the same effect.
When T is a class type, we have two possibilities:
If the initialisation is direct-initialization (MyClass b(a);), or, if it is copy-initialization with a being derived from or the same type as T (MyClass b = a;): an applicable constructor of T is chosen to construct the object.
As you can see, both of your examples fall in this category of class type initialisers.
If the initialisation is any other form of copy-initialization, any user-defined conversion sequence will be considered followed by a direct-initialization. A user-defined conversion sequence is basically any sequence of standard conversions with a single conversion constructor thrown in there.
If c were of Foo class type and there was a conversion constructor from Foo to MyClass, then MyClass b = c; would be equivalent to MyClass b(MyClass(c));.
So basically, if the source and destination types are the same, both forms of initialisation are equivalent. If a conversion is required, they are not. A simple example to show this is:
#include <iostream>
struct Bar { };
struct Foo
{
Foo(const Foo& f) { std::cout << "Copy" << std::endl; }
Foo(const Bar& b) { std::cout << "Convert" << std::endl; }
};
int main(int argc, const char* argv[])
{
Bar b;
Foo f1(b);
std::cout << "----" << std::endl;
Foo f2 = b;
return 0;
}
The output for this program (with copy elision disabled) is:
Convert
----
Convert
Copy
Of course, there are lots of other types of initialisations too (list initialisation, character arrays, aggregates, etc.).
Here is my view:
References are tied to someones else's storage, whenever u access a reference, you’re accessing that storage. References cannot be assigned to null because of the fact that they are just an aliases. A reference must be initialized when it is created. (Pointers can be initialized at any time.)
so when you say
MyObject& b(a);
compiler allocates a piece of storage b, and ties the reference to a.
when you say
MyObject b = a;
you pass a reference of a to the copy constructor and creates new b from it. Note that its a deep copy only if you have a copy constructor written for it. Otherwise it calls the default copy constructor which results in a shallow copy.
and when you say
a = b; // after creating these objects
it translates as Object::operator=(const Object&), thus A.operator=(B) is called (invoke simple copy, not copy constructor!)
What EXACTLY is the difference between INITIALIZATION and ASSIGNMENT ?
PS : If possible please give examples in C and C++ , specifically .
Actually , I was confused by these statements ...
C++ provides another way of initializing member variables that allows us to initialize member variables when they are created rather than afterwards. This is done through use of an initialization list.
Using an initialization list is very similar to doing implicit assignments.
Oh my. Initialization and assignment. Well, that's confusion for sure!
To initialize is to make ready for use. And when we're talking about a variable, that means giving the variable a first, useful value. And one way to do that is by using an assignment.
So it's pretty subtle: assignment is one way to do initialization.
Assignment works well for initializing e.g. an int, but it doesn't work well for initializing e.g. a std::string. Why? Because the std::string object contains at least one pointer to dynamically allocated memory, and
if the object has not yet been initialized, that pointer needs to be set to point at a properly allocated buffer (block of memory to hold the string contents), but
if the object has already been initialized, then an assignment may have to deallocate the old buffer and allocate a new one.
So the std::string object's assignment operator evidently has to behave in two different ways, depending on whether the object has already been initialized or not!
Of course it doesn't behave in two different ways. Instead, for a std::string object the initialization is taken care of by a constructor. You can say that a constructor's job is to take the area of memory that will represent the object, and change the arbitrary bits there to something suitable for the object type, something that represents a valid object state.
That initialization from raw memory should ideally be done once for each object, before any other operations on the object.
And the C++ rules effectively guarantee that. At least as long as you don't use very low level facilities. One might call that the C++ construction guarantee.
So, this means that when you do
std::string s( "one" );
then you're doing simple construction from raw memory, but when you do
std::string s;
s = "two";
then you're first constructing s (with an object state representing an empty string), and then assigning to this already initialized s.
And that, finally, allows me to answer your question. From the point of view of language independent programming the first useful value is presumably the one that's assigned, and so in this view one thinks of the assignment as initialization. Yet, at the C++ technical level initialization has already been done, by a call of std::string's default constructor, so at this level one thinks of the declaration as initialization, and the assignment as just a later change of value.
So, especially the term "initialization" depends on the context!
Simply apply some common sense to sort out what Someone Else probably means.
Cheers & hth.,
In the simplest of terms:
int a = 0; // initialization of a to 0
a = 1; // assignment of a to 1
For built in types its relatively straight forward. For user defined types it can get more complex. Have a look at this article.
For instance:
class A
{
public:
A() : val_(0) // initializer list, initializes val_
{}
A(const int v) : val_(v) // initializes val_
{}
A(const A& rhs) : val_(rhs.val_) // still initialization of val_
{}
private:
int val_;
};
// all initialization:
A a;
A a2(4);
A a3(a2);
a = a3; // assignment
Initialization is creating an instance(of type) with certain value.
int i = 0;
Assignment is to give value to an already created instance(of type).
int i;
i = 0
To Answer your edited Question:
What is the difference between Initializing And Assignment inside constructor? &
What is the advantage?
There is a difference between Initializing a member using initializer list and assigning it an value inside the constructor body.
When you initialize fields via initializer list the constructors will be called once.
If you use the assignment then the fields will be first initialized with default constructors and then reassigned (via assignment operator) with actual values.
As you see there is an additional overhead of creation & assignment in the latter, which might be considerable for user defined classes.
For an integer data type or POD class members there is no practical overhead.
An Code Example:
class Myclass
{
public:
Myclass (unsigned int param) : param_ (param)
{
}
unsigned int param () const
{
return param_;
}
private:
unsigned int param_;
};
In the above example:
Myclass (unsigned int param) : param_ (param)
This construct is called a Member Initializer List in C++.
It initializes a member param_ to a value param.
When do you HAVE TO use member Initializer list?
You will have(rather forced) to use a Member Initializer list if:
Your class has a reference member
Your class has a const member or
Your class doesn't have a default constructor
Initialisation: giving an object an initial value:
int a(0);
int b = 2;
int c = a;
int d(c);
std::vector<int> e;
Assignment: assigning a new value to an object:
a = b;
b = 5;
c = a;
d = 2;
In C the general syntax for initialization is with {}:
struct toto { unsigned a; double c[2] };
struct toto T = { 3, { 4.5, 3.1 } };
struct toto S = { .c = { [1] = 7.0 }, .a = 32 };
The one for S is called "designated initializers" and is only available from C99 onward.
Fields that are omitted are automatically initialized with the
correct 0 for the corresponding type.
this syntax applies even to basic data types like double r = { 1.0
};
There is a catchall initializer that sets all fields to 0, namely { 0 }.
if the variable is of static linkage all expressions of the
initializer must be constant expressions
This {} syntax can not be used directly for assignment, but in C99 you can use compound literals instead like
S = (struct toto){ .c = { [1] = 5.0 } };
So by first creating a temporary object on the RHS and assigning this to your object.
One thing that nobody has yet mentioned is the difference between initialisation and assignment of class fields in the constructor.
Let us consider the class:
class Thing
{
int num;
char c;
public:
Thing();
};
Thing::Thing()
: num(5)
{
c = 'a';
}
What we have here is a constructor that initialises Thing::num to the value of 5, and assigns 'a' to Thing::c. In this case the difference is minor, but as was said before if you were to substitute int and char in this example for some arbitrary classes, we would be talking about the difference between calling a parameterised constructor versus a default constructor followed by operator= function.