C/C++ programming: default initialization, value initialization

Default initialization

  • This is the initialization performed when the variable is constructed without using the initializer
  • If the initial value is not specified when the variable is defined, the variable is initialized by default . The initial value is related to the type of the variable and the location of the variable definition. There is a difference between default initialization of class objects and default initialization of built-in type variables.

grammar

grammar Example
T 对象 ; int i;
new T new int
new T ( ) (Before C++03) Obsolete, after C++03, perform value initialization

Default initialization situation

  • When declaring variables with automatic, static, or thread-local storage duration without an initializer
  • When new Tan object with dynamic storage duration is created with a new expression without an initializer , or whennew T ( ) an object is created with a new expression with an initializer consisting of an empty pair of parentheses (before C++03) ;
  • When a base class or non-static data member is not mentioned in the constructor initializer list , and the constructor is called

The effect of default initialization

  • If T is a non-POD (before C++11) type, consider each constructor and implement an empty argument list 重载决议. Call the selected constructor (one of the default constructors) to provide the initial value of the new object
  • If T is an array type, each array element is initialized by default
  • Otherwise, nothing is done: the object (and its children) of the object with automatic storage duration is initialized to an indeterminate value.

Default initialization of const objects

???

Read from indeterminate bytes

The use of uncertain values ​​obtained by default initialization of any variable of non-class type is undefined behavior, except for the following cases:

  • Assign an indeterminate value of type unsigned char or std::byte (since C++17) to another variable (which may be cv-qualified) of type unsigned char or std::byte (since C++17) ( The value of the variable becomes uncertain, but the behavior is not undefined);
  • Initialize another variable of type unsigned char or std::byte (since C++17) with an indeterminate value of type unsigned char or std::byte (since C++17);
  • Unsigned char or std::byte (since C++17) type indefinite value generated from the following occasions
    • The second or third operand of the conditional expression,
    • The right operand of the comma operator,
    • Cast or convert to (cv-qualified) unsigned char or std::byte (since C++17) operand,
    • Drop value expression.
int f(bool b)
{
    
    
    int x;               // OK:x 的值不确定
    int y = x;           // 未定义行为
    unsigned char c;     // OK:c 的值不确定
    unsigned char d = c; // OK:d 的值不确定
    int e = d;           // 未定义行为
    return b ? d : 0;    // 未定义行为,若 b 为 true
}

note

  • Default initialization of non-class variables with automatic and dynamic storage duration, resulting in objects with uncertain values ​​(static and thread-local objects are zero-initialized)
  • Cannot initialize reference and const scalar objects by default

For the default initialization of built-in type variables:

  • 1) Variables defined outside the function body are global variables, which are generally stored in the global area, and the variables stored in the global area generally perform value initialization. At this time, its initial value is related to the type of the variable. The initial value of the int type is 0, and the default initial value of the char type is''.

  • 2) The local variable defined in the function body is stored in the stack area. If the initial value is not specified, the local variable will not be initialized, which means that the value of the local variable is undefined and is random value. At this time, if you don’t assign a value to this local variable, then you cannot use the local variable, otherwise an error will occur. Note that this situation is not initialized, neither the default initialization nor the value initialization is used, and the value without initialization cannot be used. in use.

#include <string>
 
struct T1 {
    
     int mem; };
 
struct T2
{
    
    
    int mem;
    T2() {
    
     } // "mem" 不在初始化器列表中
};
 
int n; // 静态非类,进行两阶段初始化:
       // 1) 零初始化将 n 初始化为零
       // 2) 默认初始化不做任何事,令 n 保留为零
 
int main()
{
    
    
    int n;            // 非类,值不确定
    std::string s;    // 类,调用默认构造函数,值是 ""(空字符串)
    std::string a[2]; // 数组,默认初始化其各元素,值是 {"", ""}
//  int& r;           // 错误:引用
//  const int n;      // 错误:const 的非类
//  const T1 t1;      // 错误:const 的带隐式默认构造函数的类
    T1 t1;            // 类,调用隐式默认构造函数
    const T2 t2;      // const 类,调用用户提供的默认构造函数
                      // t2.mem 被默认初始化(为不确定值)
}

Official document

Value initialization

  • This is the initialization performed when the object is constructed with an empty initializer

grammar

grammar Example
1、 T()
1、T{}
2、new T ()
2、new T {}
3、Class::Class(...) : member() { ... }
3、 Class::Class(...) : member{} { ... }
4、T object {};

Value initialization occasion

  1. When creating an unnamed temporary object with empty brackets or curly brackets (since C++11) for the composed initializer;
  2. When the new expression creates an object with dynamic storage duration for the initializer composed of empty brackets or curly brackets (since C++11);
  3. When 成员初始化器initializing non-static data members or base classes composed of empty parentheses or curly braces (since C++11)
  4. When a named object (automatic, static, or thread-local) is declared with an initializer consisting of a pair of empty curly braces.

In all cases, if empty curly braces {} are used and T is an aggregate type, aggregate initialization is performed instead of value initialization.

If T is a class type that does not have a default constructor but has a constructor that accepts std::initializer_list, the list is initialized . (Since C++11)

annotation

  • Reference cannot be initialized by value.
  • All standard containers (std::vector, std::list, etc.), when constructed with a single size_type argument or grown by a call to resize(), initialize their individual elements by value, unless their allocator customizes the behavior of construct .
  • Starting from C++11, values ​​are initialized for classes that do not have a user-supplied constructor but have class type members. The member's class has a user-supplied constructor, and the member will be cleared before calling the member's constructor:
struct A
{
    
    
    int i;
    A() {
    
     } // 用户提供的默认构造函数,不初始化 i
};
 
struct B {
    
     A a; }; // 隐式定义的默认构造函数
 
std::cout << B().a.i << '\n'; // 值初始化 B 临时量
                              // C++03 中令 b.a.i 为未初始化
                              // C++11 中设 b.a.i 为零
// (注意 C++11 中 B{}.a.i 保留 b.a.i 为未初始化,但因为不同原因:
// 在 DR1301 后的 C++11 中,B{} 是聚合初始化,它值初始化拥有用户提供构造函数的 A)

example

#include <string>
#include <vector>
#include <iostream>
 
struct T1
{
    
    
    int mem1;
    std::string mem2;
}; // 隐式默认构造函数
 
struct T2
{
    
    
    int mem1;
    std::string mem2;
    T2(const T2&) {
    
     } // 用户提供的复制构造函数
};                    // 无默认构造函数
 
struct T3
{
    
    
    int mem1;
    std::string mem2;
    T3() {
    
     } // 用户提供的默认构造函数
};
 
std::string s{
    
    }; // 类 => 默认初始化,值为 ""
 
int main()
{
    
    
    int n{
    
    };                // 标量 => 零初始化,值为 0
    double f = double();    // 标量 => 零初始化,值为 0.0
    int* a = new int[10](); // 数组 => 每个元素的值初始化
                            //          每个元素的值为 0
    T1 t1{
    
    };                // 有隐式默认构造函数的类 =>
                            //     t1.mem1 被零初始化,值为 0
                            //     t1.mem2 被默认初始化,值为 ""
//  T2 t2{};                // 错误:类无默认构造函数
    T3 t3{
    
    };                // 有用户提供默认构造函数的类 =>
                            //     t3.mem1 被默认初始化为不确定值
                            //     t3.mem2 被默认初始化,值为 ""
    std::vector<int> v(3);  // 值初始化每个元素
                            // 每个元素的值为 0
    std::cout << s.size() << ' ' << n << ' ' << f << ' ' << a[9] << ' ' << v[2] << '\n';
    std::cout << t1.mem1 << ' ' << t3.mem1 << '\n';
    delete[] a;
}

Insert picture description here

Copy initialization (copy initialization)

concept

  • Copy from an existing object to the object being created, and type conversion is required if necessary

Direct initialization VS copy initialization

grammar

Occurs in the following 6 situations:

T object = other;     //(1) 当一个非引用类型T的具名变量通过 '=' 被一个表达式初始化时 
T object = {
    
    other};   //(2) 当一个纯量类型T的具名变量通过 '=' 被一个括号表达式初始化时

f(other)              //(3) 当通过值传递给函数的参数时

return other;         //(4) 当函数返回一个值时

throw object;

catch(T object)       //(5) 当throw/catch 一个表达式的值时

T array[N] = {
    
    other}; //(6) 使用聚合初始化,初始化每个元素

Instance

#include <iostream>

using namespace std;

struct Point
{
    
    
    int x, y;
    
    Point(int x, int y) 
        : x(x), y(y)
    {
    
    }
    
    //拷贝构造 (copy constructor)
    Point(Point const& other)
        : x(other.x), y(other.y)
    {
    
    }
};

int main()
{
    
    
    Point a(70, -130);
    
    Point b1(a);         //直接复制现有对象 a 的值
    Point b2(a.x, a.y); //最终也是复制了a,但相对麻烦,不是吗?
    
    cout << a.x << ", " << a.y << endl;
    cout << b1.x << ", " << b1.y << endl;
    cout << b2.x << ", " << b2.y << endl;
}

However, usually such a structure (or class) in a class basically does not need "self-made" copy constructor, because the operation of copying all the memory contents in the object (two member data x, y in the example) is simple like this. The compiler will do it, so when we don't define it, it will automatically generate one for us.

If there is member data of instruction type, the copy function generated by the compiler will only copy the pointer, thereby obtaining a copy of the same point (shallow copy). It is very likely that this is not what we want, and we need to consider doing it ourselves. Define the copy constructor.

Guess you like

Origin blog.csdn.net/zhizhengguan/article/details/114951753