I'm reading 'C++ All-in-One for Dummies' by J. P. Mueller and J. Cogswell and stumbled onto this:
#include <iostream>
using namespace std;
int main()
{
int ExpensiveComputer;
int CheapComputer;
int *ptrToComp;
...
This code starts out by initializing all the goodies involved — two integers
and a pointer to an integer.
Just to confirm, this is a mistake and should read '... by declaring', right? It's just strange to me that such basic mistakes still make their way to books.
From the point of view of the language, this is default initialization. The problem is, they are initialized to indeterminate values.
otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.
Default initialization of non-class variables with automatic and dynamic storage duration produces objects with indeterminate values (static and thread-local objects get zero initialized)
Note that any attempt to read these indeterminate values leads to UB.
From the standard, [dcl.init]/7
To default-initialize an object of type T means:
If T is a (possibly cv-qualified) class type ([class]), constructors are considered. The applicable constructors are enumerated
([over.match.ctor]), and the best one for the initializer () is chosen
through overload resolution ([over.match]). The constructor thus
selected is called, with an empty argument list, to initialize the
object.
If T is an array type, each element is default-initialized.
Otherwise, no initialization is performed.
Yes you are correct.
You declared and defined these variables, you did not initialize them!
PS: What is the difference between a definition and a declaration?
This code both declares and defines three variables but does not initialize them (their values are said to be indeterminate).
A variable declaration only must include keyword extern.
Right. Hence, "dummies". :)
We can't even blame this on legacy; historically C programmers would declare* a variable and then "initialize" it later with its first assignment.
But it was never the case that simply declaring a variable, without an initializer, were deemed to be "initializing" it.**
So the wording is just wrong.
* Technically we're talking about definitions, but when we say "declare a variable" we almost always mean defining declarations.
** Though objects with static storage duration do undergo their own zero-initialisation phase before anything else happens, so forgoing initialisation yourself is not a catastrophe in that case. Still, we cannot claim that we have initialised that object.
Related
According to https://en.cppreference.com/w/cpp/language/zero_initialization
In the example provided by the documentation:
std::string s; // is first zero-initialized to indeterminate value
// then default-initialized to ""
Why does zero initialization occur to string s; if the syntax is for static T object;?
Why does zero initialization happen before default initialization and why are both allowed to happen?
The effects of zero initialization are:
If T is a scalar type, the object's initial value is the integral
constant zero explicitly converted to T.
If T is an non-union class
type, all base classes and non-static data members are
zero-initialized, and all padding is initialized to zero bits. The
constructors, if any, are ignored.
If T is a union type, the first
non-static named data member is zero-initialized and all padding is
initialized to zero bits.
If T is array type, each element is
zero-initialized
If T is reference type, nothing is done.
What if I initialize string array[2] = {"Test1"};? I know that the array will contain "Test1" and empty string "".
But according to the above documentation,
If T is array type, each element is
zero-initialized
The data type is string which is an object / reference type?
If T is reference type, nothing is done.
Nothing is done? I thought maybe a constructor would have been called. Surely an empty string is something?
(Unless otherwise specified, all declarations in this answer are assumed to be in namespace scope.)
Why does zero initialization occur to string s; if the syntax is for
static T object;?
Why does zero initialization happen before
default initialization and why are both allowed to happen?
Variables with static storage duration are first zero-initialized at compile time, and then optionally dynamically initialized at runtime. static T object; declares an object of static storage duration. For a simple declaration like
int x;
The dynamic initialization is not performed. For a more sophisticated declaration like
std::string s;
Zero-initializing a string may result in an invalid string with a broken class invariant. Therefore, the dynamic initialization calls the default constructor to ensure that the object is valid.
What if I initialize string array[2] = {"Test1"};? I know that the
array will contain "Test1" and empty string "".
First, at compile time, the two objects are zero-initialized, resulting in possible invalid state. Then, at runtime, the constructors are called (const char* constructor for the first object and default constructor for the second object), and the valid objects are constructed.
The data type is string which is an object / reference type?
std::string is an object type instead of a reference type.
[For a reference type] Nothing is done? I thought maybe a constructor
would have been called. Surely an empty string is something?
A reference type is not considered an actual "object", so there is no point in specifying its zero-initialization semantics.
Why does zero initialization occur to string s; if the syntax is for static T object;?
Why does zero initialization happen before default initialization and why are both allowed to happen?
In page you linked to, that defines a non-local variable.
Non-local variables are initialized in two phases.
Static intialization.
Dynamic initialization, if it applies.
In static initialization phase, a variable is initialized using constant initialization or zero initialization
Dyanmic initialization is used, if it applies, such as for objects that have the appropriate constructor or for objects that are initialized using an expression that can be evaulated at run time.
You can read more on the topic at https://en.cppreference.com.
Nothing is done? I thought maybe a constructor would have been called. Surely an empty string is something?
A reference cannot be zero-initialized. It can only be initialized using a object that it will be a reference to.
I have a class whose member is an enum declared inside this class:
#include<iostream>
class test
{
public:
enum TYPE{MAN, WOMAN};
TYPE type;
};
int main()
{
test x;
if(x.type == test::MAN) std::cout<<"MAN"<<std::endl;
if(x.type == test::WOMAN) std::cout<<"WOMAN"<<std::endl;
std::cout<<"ok"<<std::endl;
return 0;
}
I know that if an enum is declared at namespace scope, it has a default value 0 and when it's declared locally, it doesn't have any default values, which leads to undefined behavior.
My question is: what if I have an enum which belongs to a class? Is it undefined behavior as well?
I tested the above code and x.type is neither MAN nor WOMAN. However, I've done it for only one compiler and one operating system. I'm interested in a more general answer. I haven't found any information regarding this issue anywhere else.
Edit1: Can referring to this indeterminate value cause segmentation fault?
Edit2: I know this is not a well designed class- it's not mine and I'm trying to debug it. So telling me that I can default-initialize object doesn't solve my problem. Please, treat it as a theoretical question.
The default value of the first name in an enum is 0, regardless of the scope of the enum.
There is no guaranteed default value of an automatic local variable like test x; in main here. It has an indeterminate value. And it's Undefined Behavior to use that value.
You can ¹default-initialize it like this:
test x{};
¹ A subtle point is that at top level this gives a “value-initialization”.
If your object don't have any constructors, then it depends on where you create your object. If it's created globally, then all variables are zero-initialized. If not, they are not initialized properly and reading from them results in UB.
You can force zero-initialization of a non-global variable with test x{}; syntax.
First: "Testing" for undefined behavior is almost never going to give you the right answer.
This is undefined behavior because you are reading from an uninitialized variable with automatic storage duration. Such a variable has an indeterminate value and must not be read from. Every non-static function scope variable has automatic storage duration.
I think you are confusing the definition of the enum type (which happens inside the class definition) with the declaration of a variable of this type (at function scope). In your example x is a variable with automatic storage duration no matter where the type TYPE has been defined.
whats is the difference between these three statements in c++ ??
aa *obj;
aa *obj1 = new aa;
aa *obj2 = new aa();
where aa is a class
I am confucion in last two statement .
The first does not initialize the pointer.
In the latest specification,
If the new-initializer is omitted, the object is default-initialized (8.5); if no initialization is performed,
the object has indeterminate value.
Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 for direct initialization.
That is, if the class (you said it was a class) doesn't have a constructor, then the first form will act the same as a local scope definition and leave the memory un-initialized.
The empty initializer will force it to be initialized anyway, which gives the same results as a global variable of that type.
A class might not have a constructor, even a hidden constructor, if it contains nothing but data members of primitive types. You'll see that discussed as a "POD", or plain'ol data. For templates, the difference was found to be annoying, so the rules were refined to work, with (), uniformly for any type, even built-in types. new int() will give a pointer to a value holding 0. new int will give a pointer to a value holding whatever garbage happened to be at that address before.
Consider this code:
template <typename T>
void f()
{T x = T();}
When T = int, is x equal to 0 or to an arbitrary value?
Bonus question: and consequently, are arrays (both T[N] and std::array<T, N>) the only types where such a syntax may leave contents with arbitrary values.
T() gives value initialization, which gives zero-initialization for a type other than a class, union or array. (§8.5/7 bullet 3): "otherwise, the object is zero-initialized." For an array, each element of the array is value initialized.
For an array, the content will be of an arbitrary value if it's auto storage class, but zero initialized if it's static storage class -- i.e., global (assuming, of course, that you don't specify any initialization).
Regarding your first question, it is called value-initialization, and is well-covered in the standard (C++11 § 8.5 covers initialization in detail, with the specifics on () initialization and how it eventually leads to zero-initialization, starting with 8.5/16 (covers ()), which leads to 8.5/7 (value-initialization), and finally 8.5/5 (zero-initialization).
Regarding std::array<T,N>, if T is a class-type the constructors will fire for each element (user-provided or default if none are provided by the user). If default construction happens default-initialization will fire (which isn't very exciting). By the standard (8.5/6), each element is default-initialized. For T that is not a class type this is effectively the same as T ar[N]; which as you have pointed out, is indeterminate as well (because it is default initialized, which by the standard "no initialization is performed.".
Finally, if static storage is declared for your fixed array of non-class-type, it resides in zero-filled memory on inception. For automatic storage, its back to "no initialization is performed." as your end-game.
I hope i didn't miss something (and I know i'll hear it if I did). There are lots of interesting questions on SO that cover areas like this. If I get a chance I'll link a few.
I understand from the answer to this question that values of global/static uninitialized int will be 0. The answer to this one says that for vectors, the default constructor for the object type will be called.
I am unable to figure out - what happens when I have vector<int> v(10) in a local function. What is the default constructor for int? What if I have vector<int> v(10) declared globally?
What I am seeing is that vector<int> v(10) in a local function is resulting in variables being 0 - but I am not sure if that is just because of my compiler or is the fixed expected behaviour.
The zero initialization is specified in the standard as default zero initialization/value initialization for builtin types, primarily to support just this type of case in template use.
Note that this behavior is different from a local variable such as int x; which leaves the value uninitialized (as in the C language that behavior is inherited from).
It is not undefined behaviour, a vector automatically initialises all its elements. You can select a different default if you want.
The constructor is:
vector( size_type, T t = T() )
and for int, the default type (returned by int()) is 0.
In a local function this:
int x;
is not guaranteed to initialise the variable to 0.
int x = int();
would do so.
int x();
sadly does neither but declares a function.
The constructor you are using actually takes two arguments, the second of which is optional. Its declaration looks like this:
explicit vector(size_type n, const T& value = T())
The first argument is the number of elements to create in the vector initially; the second argument is the value to copy into each of those elements.
For any object type T, T() is called "value initialization." For numeric types, it gives you 0. For a class type with a default constructor, it gives you an object that has been default constructed using that constructor.
For more details on the "magic parentheses," I'd recommend reading Michael Burr's excellent answer to the question "Do the parentheses after the type name make a difference with new?" It discusses value initialization when used with new specifically, but for the most part is applicable to value initialization wherever else it can be used.
By default, vector elements are zero-initialized and not default-initialized. Those are two different but related concepts:
zero-initialization is what is done for static objects not having an explicit initialization and what is done for a member given in the initialized list with an initializer of (). For basic types, the value used is 0 converted to the type.
default-initialization is what is done for not explicitly initialized non static variables and members. For basic types it stay uninitialized.
(And C++0X introduces value-initialization which is still different).
As mentioned by others, what happens is the zero initialization kicks in. I actually use that a lot in my code (outside of vectors and other classes):
some_type my_var = some_type();
This allows me to make sure that my variables are always properly initialized since by default C/C++ do not initialize basic types (char, short, int, long, float, double, etc.)
Since C++11, you also can do so in your class definitions:
class MyClass
{
...
int my_field_ = 123; // explicit initialization
int your_field_ = int(); // zero initialization
};
For vectors, the std library uses T(). Whatever T() is, it will use that default initialization. For a class, it calls the default constructor. For a basic type, it uses zero ('\0', 0, 0.0f, 0.0, nullptr`).
As mentioned by James McNellis and Nawaz, it is possible to set the value used to initialize the vector as in:
std::vector<int> foo(100, 1234);
That feature is also available when you resize your vector (if the vector shrinks, the default value is ignored):
foo.resize(200, 1234);
So that way you can have a default initialization value. However, it's a be tricky since you have to make sure that all your definitions and resize() calls use that default value. That's when you want to write your own class which ensures that the default value is always passed to the vector functions.
However, if you want to have a way to auto-initialize to a specific value, you can mix both features this way:
struct my_value {
int v = 123;
};
std::vector<my_value> foo(100);
// here foo[n].v == 123 for n in [0, 100)
This is my preferred way of dealing with this issue (i.e. if I don't want zero by default). It's an extra .v, but much less prone to mistakes and you don't need to know of the default value when you create a vector of my_value.
Also, for those who think this will be slow, it won't. The struct is like syntactic sugar as far as C++ is concerned. One optimized, it will be exactly the same as a simple std::vector<int> foo(100, 123).
The default initialization for an int type is to initialize it to 0.
This is true of most (if not all) primitive types: char will initialize to (char)0 (or '\0' if you prefer), float will initialize to 0.0f, and any pointer initializes to NULL. For other types, the parameterless constructor is invoked.
In general, the default initialization should happen pretty much whenever you aren't able to specify a constructor (or choose not to).