Do Built-In Types Have Default Constructors

Do built-in types have default constructors?

A constructor is a member function (constructors are fully specified in clause 12 of the C++ Standard, which covers special member functions like constructors and destructors).

A member function can only be defined for a class type (C++03 9.3/1 says "Functions declared in the definition of a class, excluding those declared with a friend specifier, are called member functions of that class").

So non-class types (including fundamental types, array types, reference types, pointer types, and enum types) do not have constructors.

I don't have a copy of The C++ Programming Language to read the context of the quote that "Built-in types also have default constructors," but I would guess that Stroustrup is either using the term "constructor" in a loose, non-technical sense, or the meaning of the term or the way in which it is used in the Standard changed between when the book was published and when the language was standardized. I'd guess the former is far more likely than the latter.

Are members of a built-in type ever default-initialised?

First let's go over some examples and the correct terminology.

T a1;            // default initialization
T a2{}; // value initialization
T(); // also value initialization
new T; // default initialization
new T(); // value initialization
new T{}; // also value initialization
class C1 {
C1() {}
T x;
}; // no initializer for C1::x; default-initialized
class C2 {
T x;
}; // implicit default constructor default-initializes C2::x
class C3 {
C3() : x() {}
T x;
}; // C3::x will be value-initialized.
class C4 {
C4() : x{} {}
T x;
}; // C4::x will also be value-initialized.
// DANGER
T a(); // declares a function; not value initialization (quirk of C++)

Generally, the rule is that when there is no initializer, it is default initialization, and when the initializer is () or {}, it is value initialization. Note that there is an exception for statics and thread-locals, which I'll discuss later.

For an integer or floating-point type, value initialization sets it to 0. For a pointer type, value initialization sets it to null. Default initialization does nothing for scalar types. Therefore, if an object of scalar type only receives default initialization, then it has indeterminate value.

a) Does initialisation of members of a built-in type have anything to do with wheher a user-defined constructor is defined or not,

The default constructor for a class default-initializes members. A member is also default-initialized when no mem-initializer is explicitly provided for it. The examples C1 and C2 illustrate this. However, note that when a class type is value-initialized, and the class's default constructor is either implicitly defined or explicitly defaulted, the class's members will be zeroed out. This zeroing out occurs only in this case, and doesn't occur for a user-provided default constructor. So the answer to your question is "yes" in this sense.

C1 y1;   // y1 is default-initialized; y1.x is indeterminate
C1 y2{}; // y2 is value-initialized; y2.x is indeterminate
C2 y3; // y3 is default-initialized; y3.x is indeterminate
C2 y4{}; // y4 is value-initialized; y4.x is set to 0
C3 y5; // y5 is default-initialized; y5.x is set to 0
C3 y6{}; // y6 is value-initialized; y6.x is set to 0

b) do members of a built-in type always need to be initialised manually, and c) are there any circumstances under which an object's storage is zeroed-out before the constructor is called?

I assume you mean "class members with built-in type". I covered a case above in which they are automatically initialized to 0: where the class object is value-initialized and its constructor is not user-provided (or deleted). Another case is when the class object has static or thread-local storage duration. In this case, the members will also be zeroed out at the very beginning, so there is no chance of them ending up with indeterminate value.

Is it guaranteed that defaulted constructor initialize built in types automatically to 0?

Test = default will default initialize its members.
but for type as int or float, default initialization is different than value-initialization

so

Test t; // t.s and t.m have unitialized value

whereas

Test t{}; // t.s == 0 and t.m == 0.0f;

custom constructors for built-in types in c++

Well, there is no constructor for a basic pointer - in the sense that there is no function implicitly called in order to initialise the pointer.

The closest you can come is to use a user-defined conversion operator function

class Entity
{
public:

operator int *();
};

Entity::operator int *()
{
// return something appropriate that is of type int *
}


// sample usage in a function somewhere

int *p = some_entity; // implicitly conversion that calls the operator int *()

int *another_p = static_cast<int *>(some_entity); // explicit conversion

int *yet_another_p = some_entity.operator int *();

There are variants of this, depending on what form of const qualification is needed (e.g. if the operator function doesn't change the object it acts on, it should be const and may be defined as operator const int *()).

It is necessary to ensure that the pointer returned by the operator function is treated appropriately. If the user defined operator function returns a member of some_entity, it cannot be used once some_entity ceases to exist. Similarly, if it uses dynamic memory allocation (e.g. return the result of a new expression) the caller must explicitly release that memory to avoid a memory leak.

Is there still a need to provide default constructors to use STL containers?

This quote is from the C++ Programming Language, Special edition , 2005 by Bjarne Stroustrup in section 16.3.4:

If a type does not have a default constructor, it is not possible to create a vector with elements of that type, without explicitly providing the value of each element.

So it was indeed a standard requirement. It was also required that (section 17.1.4) :

To be an element of a container, an object must be of a type that allows the container implementation to copy it. The container may copy it using a copy constructor or an assignment; in either case the result of the copy must be an equivalent object.

So yes, there were "official" constructor requirementsand the library implementation were supposed to be interchangeable and not add other requirements. (Already in the very first proposal for STL in 1995, the authors tried as much as possible to clearly indicate specifications and narrow down the implementation dependent flexibility.)

You therefore had to provide a default constructor in the case where you declared other constructors:

If a user has declared a default constructor, that one will be used; otherwise, the compiler will try to generate one if needed and if the user hasn't declared other constructors.

Nowadays, this requirement is relaxed. Since C++11:

The requirements that are imposed on the elements depend on the actual operations performed on the container.

So you can define a vector for a class without default constructor, if it doesn't make sense. This for example perfectly works (online demo):

class MyClass {
public:
MyClass(int x) {}
};
int main() {
vector<MyClass> v;
MyClass test{1};
v.push_back(test);
}

But it works only as long as you don't use any operation that would need the default constructor. For instance v.resize(6); would fail to compile.

Built-in datatypes versus User defined datatypes in C++

While initializing a built-in datatype variable, the variable also HAS to be "built from dust" . So, are there also constructors for built in types?

Per request, I am rebuilding my answer from dust.

I'm not particularly fond of that "Constructors build objects from dust" phrase. It is a bit misleading.

An object, be it a primitive type, a pointer, or a instance of a big class, occupies a certain known amount of memory. That memory must somehow be set aside for the object. In some circumstances, that set-aside memory is initialized. That initialization is what constructors do. They do not set aside (or allocate) the memory needed to store the object. That step is performed before the constructor is called.

There are times when a variable does not have to be initialized. For example,

int some_function (int some argument) {
int index;
...
}

Note that index was not assigned a value. On entry to some_function, a chunk of memory is set aside for the variable index. This memory already exists somewhere; it is just set aside, or allocated. Since the memory already exists somewhere, each bit will have some pre-existing value. If a variable is not initialized, it will have an initial value. The initial value of the variable index might be 42, or 1404197501, or something entirely different.

Some languages provide a default initialization in case the programmer did not specify one. (C and C++ do not.) Sometimes there is nothing wrong with not initializing a variable to a known value. The very next statement might be an assignment statement, for example. The upside of providing a default initialization is that failing to initialize variables is a typical programming mistake. The downside is that this initialization has a cost, albeit typically tiny. That tiny cost can be significant when it occurs in a time-critical, multiply-nested loop. Not providing a default initial value fits the C and C++ philosophy of not providing something the programmer did not ask for.

Some variables, even non-class variables, absolutely do need to be given an initial value. For example, there is no way to assign a value to a variable that is of a reference type except in the declaration statement. The same goes for variables that are declared to be constant.

Some classes have hidden data that absolutely do need to be initialized. Some classes have const or reference data members that absolutely do need to be initialized. These classes need to be initialized, or constructed. Not all classes do need to be initialized. A class or structure that doesn't have any virtual functions, doesn't have an explicitly-provided constructor or destructor, and whose member data are all primitive data types, is called plain old data, or POD. POD classes do not need to be constructed.

Bottom line:

  • An object, whether it is a primitive type or an instance of a very complex class, is not "built from dust". Dust is, after all, very harmful to computers. They are built from bits.
  • Setting aside, or allocating, memory for some object and initializing that set-aside memory are two different things.
  • The memory need to store an object is allocated, not created. The memory already exists. Because that memory already exists, the bits that comprise the object will have some pre-existing values. You should of course never rely on those preexisting values, but they are there.
  • The reason for initializing variables, or data members, is to give them a reliable, known value. Sometimes that initialization is just a waste of CPU time. If you didn't ask the compiler to provide such a value, C and C++ assume the omission is intentional.
  • The constructor for some object does not allocate the memory needed to store the object itself. That step has already been done by the time the constructor is called. What a constructor does do is to initialize that already allocated memory.

The initial response:

A variable of a primitive type does not have to be "built from dust". The memory to store the variable needs to be allocated, but the variable can be left uninitialized. A constructor does not build the object from dust. A constructor does not allocate the memory needed to store the to-be constructed object. That memory has already been allocated by the time the constructor is called. (A constructor might initialize some pointer data member to memory allocated by the constructor, but the bits occupied by that pointer must already exist.)

Some objects such as primitive types and POD classes do not necessarily need to be initialized. Declare a non-static primitive type variable without an initial value and that variable will be uninitialized. The same goes for POD classes. Suppose you know you are going to assign a value to some variable before the value of the variable is accessed. Do you need to provide an initial value? No.

Some languages do give an initial value to every variable. C and C++ do not. If you didn't ask for an initial value, C and C++ are not going to force an initial value on the variable. That initialization has a cost, typically tiny, but it exists.

Do primitive types have also constructors in C++?

What Bjarne means is that you can write int(56) or even int() to construct an integer. What the links means is that a struct/class is only a POD if it does not have a constructor declared. So Bjarne talks about primitive non-struct types and the link talks about structs/classes so the two sources can coexist without contradicting each other.

Here is part of the definition from the link:

a POD type's non-static data members must be public and can be of any of these types

Of course, this can only hold for structs. An int has no "data members". So although the link never mentions it directly, it only refers to structs and classes.

Does a c++ struct have a default constructor?

The simple answer is yes.

It has a default constructor.

Note: struct and class are identical (apart from the default state of the accesses specifiers).

But whether it initializes the members will depends on how the actual object is declared. In your example no the member is not initialized and a has indeterminate value.

void func()
{
_bar_ a; // Members are NOT initialized.
_bar_ b = _bar_(); // Members are zero-initialized
// From C++14
_bar_ c{}; // New Brace initializer (Members are zero-initialized)


_bar_* aP = new _bar_; // Members are NOT initialized.
_bar_* bP = new _bar_(); // Members are zero-initialized
// From C++14
_bar_ cP = new _bar_{}; // New Brace initializer (Members are zero-initialized)
}

// static storage duration objects
// i.e. objects at the global scope.
_bar_ c; // Members are zero-initialized.

The exact details are explained in the standard at 8.5 Initializers [dcl.init] paragraphs 4-10. But the following is a simplistic summary for this situation.

A structure without a user defined constructor has a compiler generated constructor. But what it does depends on how it is used and it will either default initialize its members (which for POD types is usually nothing) or it may zero initialize its members (which for POD usually means set its members to zero).

PS. Don't use a _ as the first character in a type name. You will bump into problems.



Related Topics



Leave a reply



Submit