class Wood {
public:
Wood();
Wood(const Wood&); //copy constructor
~Wood();
private:
string price;
};
Wood::Wood(const Wood& orig) {
price(orig.price); **//error, why?**
}
Wood::Wood(const Wood& orig) : price(orig.price) { //rigth
}
If i using the construct initialize and it was correct. But if if using "price(orig.price)" and it will error, why?
The function body of a constructor (the part between the opening and closing braces) is no different the body of any other function. Would you expect this to compile:
std::string a, b;
a(b); // <--- this line?
No, of course not. In order for that to compile, std::string would need something like an operator() overload which takes another string. It doesn't have that.
The code in the initialization list is different. The expressions in an initialization list are not interpreted as normal statements, like those inside a function body. They are interpreted as initializations (e.g. constructor calls). So, in an initialization list, this:
: price(orig.price)
Is equivalent to a statement like this:
std::string price(orig.price);
Except that the type of price doesn't need to be specified, because that was already done in the class definition.
Note that you cannot do member initialization inside the constructor body, because by the time you get there, all members are already initialized. That's why you need the initialization list. You can, of course, do assignment in the constructor body:
price = orig.price;
But that is different than initialization. It won't work for some types (such as const members, reference members, or members without default constructors). And it can be less efficient for some types, since you are constructing first (with the default constructor), and then assigning. But for many types, it doesn't really matter, because a default construction costs practically nothing.
It's incorrect because price is already constructed. You can't call it's copy constructor if it's already constructed.
You have to do something like price = orig.price;
Examine the compile error and you will see why. In the member initializer list, price(orig.price) is direct initialization of the member variable price with the value orig.price. In the body of the copy constructor, price(orig.price) is a call to an overloaded operator() in std::string which accepts a std::string. Since there is no such overload, you get a compile error.
Related
Alright, so I was studying about nameless temporary objects in operator overloading where instead of writing:
Classname temp;
temp.variable=variable;
return temp;
You could do this:
return Classname(variable);
But to do this, you also need to create a one-argument constructor like this:
Classname(int c): variable(c)
{ }
My question is.. why do we need to create this one-argument constructor? I've been searching online a lot and can't find any reason. So, I really need your help guys. Thanks.
You don't have to. You can use aggregate initialization instead:
struct Classname {
int variable;
};
// ...
return Classname{42};
This creates an object of the class whose only non-static member is initialized with the value 42.
Aggregate initialization is subject to some restrictions. The logic is that only types that are in some sense analogous to C structs---being groupings of data with no invariants---should be eligible. If you make the type more complicated, by adding constructors, or hiding some members through access control, your intent is probably that the members shall not be initialized directly without going through some constructor or some setter methods. So in that case, it is not allowed; instead, an appropriate constructor must be defined.
I've been trying to learn C++ for about a month and it still puzzles me. For example this 'easy' code:
class A
{
int m;
public:
A() = default;
int getM() { return m; }
};
class B
{
int m;
public:
// empty body and empty initializer list
B() {} // isn't this the same as "B() = default;" ?
int getM() { return m; }
};
int main()
{
A a1;
A a2{};
std::cout << "a1.m=" << a1.getM() << "\n";
std::cout << "a2.m=" << a2.getM() << "\n";
B b1;
B b2{};
std::cout << "b1.m=" << b1.getM() << "\n";
std::cout << "b2.m=" << b2.getM() << "\n";
std::cin.ignore();
}
result:
a1.m=...garbage
a2.m=0
b1.m=...garbage
b2.m=...garbage
According to CPP REFERENCE default constructor defined by the compiler does have empty body and empty initializer list. So how the heck does it (in class A) initialize member 'm' to zero when explicitly defined default constructor with empty body and empty initializer list does not. According to cppreference excerpt:
If the implicitly-declared default constructor is not defined as deleted, it is defined (that is, a function body is generated and compiled)
by the compiler if odr-used, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list.
As far as I understand it both constructors should behave exactly the same way. Seems simple yet I do not get it.
Here's the basic idea. Both A a2{}; and B b2{}; will perform what is called "value initialization" on the two objects. However, the way value initialization behaves depends on how those types are defined.
B is an object which has a user-provided default constructor. "User-provided" being the term for when you provide a body for the default constructor. Because of that, value initialization will call the default constructor. That default constructor does not initialize its members, so the members remain uninitialized.
A is an object which does not have a user-provided default constructor. Nor does it have any other user-provided constructors. And the default constructor is not deleted either. And there are no default member initializers in A. Given all of that, value initialization will perform zero initialization on the object. Which means that it will write all zeros to the memory for that object before it comes into existence.
That's what the rules say; the two do not behave the same, nor are they meant to. Nor is there anything you can do to make a user-provided default constructor act like a defaulted default constructor in all cases. You can make the user-provided constructor value initialize its members, but it would do so all the time, even if you use default initialization (B b1;, for example).
Why do the rules say that? Because = default is not supposed to be equivalent to an empty constructor body. Indeed, being different is why = default exists as a feature.
When you = default your default constructor, you are saying "generate the default constructor as you normally would". This is important, because there are things you can do which actively prevent the compiler from generating a default constructor for you. If you specify other constructors (which are not copy/move constructors), the compiler will not automatically generate one. So by using = default syntax, you're telling the compiler that you want the generated default constructor.
By contrast, if you make an empty body in your default constructor, you are saying something totally different. You are explicitly saying, "If a user calls my default constructor, I want my members to be default-initialized." That's what it means when you have an empty member initializer list in a constructor, after all. So that's what it should do.
If = default and an empty body behaved the same, there would be no way for you to get that behavior, to say that you want default initialization of your members no matter what.
Basically, Cppreference's statement is completely wrong; it does not have "exactly the same effect as a user-defined constructor with empty body and empty initializer list". Nor is it supposed to.
If you want to understand the thinking of value initialization a bit further, consider this.
int i{};
That is guaranteed to produce a value of 0 for i. It is therefore reasonable that this:
struct S{int i;};
S s{};
Should also produce a value of 0 for s.i. How does that happen? Because value initialization will zero-initialize s.
So how does a user say that they don't want that, or want some special form of initialization? You communicate that the same way you communicate everything else: you add a constructor. Specifically, a default constructor that does the form of initialization you want.
If any constructor is provided zero-initialization does not take place as stated here
Zero initialization is performed in the following situations:
...
2) As part of value-initialization sequence for non-class types and for members of value-initialized class types that have no constructors, including value initialization of elements of aggregates for which no initializers are provided.
Also here
The effects of value initialization are:
1) if T is a class type with at least one user-provided constructor of any kind, the default constructor is called;
Which makes sense. One should be able to control whether any additional operations can be added. If you provide a constructor, you take responsibility for your object initializations.
I have searched previous questions, and have not found a satisfying answer to my question:
If I define an empty default constructor for a class, as for example
class my_class{
public:
myclass(){}
private:
int a;
int* b;
std::vector<int> c;
}
my understanding is that if I define an object using the default constructor, say
my_class my_object;
then my_object.a will be a random value, the pointer my_object.b will also be a random value, however the vector c will be a well-behaved, empty vector.
In other words, the default constructor of c is called while the default constructors of a and b is not. Am I understanding this correctly? What is the reason for this?
Thank you!
a and b have non-class types, meaning that they have no constructors at all. Otherwise, your description is correct: my_object.a and my_object.b will have indeterminate values, while my_object.c will be properly constructed.
As for why... by writing a user-defined constructor and not mentioning a and b in the initializer list (and not using C++11 in-class member initializers) you explicitly asked the compiler to leave these members uninitialized.
Note that if your class did not have a user-defined constructor, you'd be able to control the initial values of my_object.a and my_object.b from outside, by specifying initializers at the point of object declaration
my_class my_object1;
// Garbage in `my_object1.a` and `my_object1.b`
my_class my_object2{};
// Zero in `my_object2.a` and null pointer in `my_object2.b`
But when you wrote your own default constructor, you effectively told the compiler that you want to "override" this initialization behavior and do everything yourself.
Since a and b are not objects but primitive datatypes, there is no constructor to call. In contrast, c is an object, so its default constructor is called.
#include<iostream>
using namespace std;
class A {
public:
int i;
};
int main() {
const A aa; //This is wrong, I can't compile it! The implicitly-defined constructor does not initialize ‘int A::i’
}
when I use
class A {
public:
A() {}
int i;
};
this is ok! I can compile it! why I can't compile it when I use the implicitly-defined constructor?
why the implicit-defined constructor does not work?
It does work, but one of the language rules is that it can't be used to initialise a const object unless it initialises all the members; and it doesn't initialise members with trivial types like int. That usually makes sense, since being const there's no way to give them a value later.
(That's a slight simplification; see the comments for chapter and verse from the language standard.)
If you define your own constructor, then you're saying that you know what you're doing and don't want that member initialised. The compiler will let you use that even for a const object.
If you want to set it to zero, then you could value-initialise the object:
const A aa {}; // C++11 or later
const A aa = A(); // historic C++
If you want to set it to another value, or set it to zero without the user having to specify value-initialisation, then you'll need a constructor that initialises the member:
A() : i(whatever) {}
why the implicit-defined constructor does not work?
Because the C++ standard says so:
[dcl.init] paragraph 7:
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
This ensures that you don't create a const object containing uninitialized data that cannot be initialized later.
To initialize a const-qualified object you need to have a user-provided default constructor or use an initialiser:
const A aa = A();
Here the object aa is initialized with the expression A() which is a value-initialized object. You can value-initialize a class type without a default constructor, because value-initialization will set values to zero if there is no default constructor for the type.
However, the rule in the standard is too strict, as it forbids using implicitly-defined constructors even when there are no data members or all data members have sensible default constructors, so there is a defect report against the standard proposing to change it, see issue 253.
You don't state what compiler you're using. I've tried this with VS2012 and get a warning C4269.
The reason this is a problem is because aa is const. Because you haven't defined a constructor a default one is used and so i can be anything. It also cannot be changed (because aa is const).
If you define a constructor, it is assumed that you are happy with the initialization of i. Although, in this case you haven't actually changed the behaviour.
From this MSDN page
Since this instance of the class is generated on the stack, the initial value of m_data can be anything. Also, since it is a const instance, the value of m_data can never be changed.
Because i is not initialized.
class A
{
public:
A()
{
i =0;
}
int i;
};
"Implicit constructor" means a constructor generated for you automatically and generates an error because it realizes it is not able to initialize the value of i. This can be a no-args constructor, a copy constructor or (as of C++11) a move constructor.
why the implicit-defined constructor does not work?
It works just fine, but it does not decide what your default values are implicitly (and as such, it only calls default constructors for it's members, but not for POD types).
If you want the constructor to initialize your members with certain values you have to explicitly write that (i.e. add a default constructor explicitly).
Making a default (implicit) constructor initialize POD members with a chosen value (like zero for example) would add extra computing cycles (and slow your program down) when you don't need that. C++ is designed to behave as if you (the programmer) know what you are doing (i.e. if you do not initialize your members explicitly, the compiler assumes you don't care what default value you get).
This is very trivial question regarding the use of a constructor in C++. I will present in the form of an interview dialogue (it was difficult to present it in any other forms)
interviewer - what is a constructor?
me - constructor are special functions which makes sure that all objects are initialized before their use.
interviewer - what is an initializer list?
me - that is a list where all the initialization happens. A constructor's body is entered only after all the data members are initialized, or some constructor of all the member objects are called.
interviewer - that means initialization is carried out in initializer list, not inside constructor. But you said constructor initialize the object! Didn't you? Do you want to answer my first question.
me - I think constructor does assignment, it calls assignment operator on already initialized member objects.
So my question to you can be
how initializer list works?
what lies between function's starting address & starting braces [{]?
or just answer me how to convince my interviewer.
Technically speaking, your interpretation is accurate. No members may be initialised from inside the ctor body; only in the ctor-initializer. Any member access in the ctor body may only be assignment.
All members are "initialised" before the ctor body is entered.
However, speaking more broadly, since the body always follows the initializer, it's said that — as a unit — the object is initialised once the constructor has ended... including the body.
Partly this is because, again speaking broadly, you might consider initialisation to include some business logic that you must perform in your ctor body, even though this is not the same as actual initialisation of a data member.
You're overthinking it and allowing the interviewer to confuse you.
Initializing the members of an object is not the same thing as initializing the object itself. Just because the members have sane values doesn't mean the object has been constructed. Until the constructor has completed, the object itself has not been properly initialized.
The main things about an initialisation list are efficiency and code readability.
The readability part is self-explanatory, because you know exactly where to look to see where the values are initialised. The efficiency saving comes in the fact that if you assign them values within the code of the constructor instead, then they will be assigned twice: firstly when the object is created they will be assigned the values provided by that datatype's default constructor, then they will be assigned a new value within your object's constructor. The initialisation list just insures that they are initialised with the value you specified to begin with.
for example, here is an example of an initialisation list I used in a Doubly Linked List implementation:
template <typename T>
LinkedList<T>::LinkedList()
: size(0)
, pHead(NULL)
, pTail(NULL)
{
}
Whereas the less efficient version where size, pHead and pTail get assigned twice is shown below:
template <typename T>
LinkedList<T>::LinkedList()
{
size = 0;
pHead = NULL;
pTail = NULL;
}
Essentially, you are correct but the member initializer shouldn't be considered separate from the constructor. The initializer is a part of the constructor and is called before the main body of the constructor.
When you declare an automatic variable of a built-in type, it is both a definition and a declaration. It's a declaration because an identifier is bound to a type and it's a definition because the compiler allocates storage for it.
int var1; // declares/defines var of type int
int var2 = 0; // declares/defines a var of type int and initializes it to 0
An initializer sets the variable with an initial value when it is defined but it's considered defined before the initializer.
int x = 5;
int y = 5;
int main()
{
int x = x; // x is undefined here not 5 because x refers to itself
int y[y];
int size = sizeof(y)/sizeof(int); // size is 5 (y[5]) since y isn't defined until after the enclosing bracket so y referred to the global y in the declaration.
}
There are some variables that must be initialized however. Constants and references.
It is the same with constructors. The point at which the members have been defined is right before the ctor body. This is the order that members and bases are defined when the constructor is called.
virtual base classes
base class
members--in the order in which they were declared
ctor body is executed
After leaving the constructor body, everything has been initialized.
If you don't use the initializer, then you can assume that its already defined when entering the ctor body but you can't assume it has any particular value. Again, constants and references must be initialized in the member initializer.
The initializer list is part of the constructor. Each constructor has its own.