[C++ advanced knowledge] 04 - function default arguments, default initialization, initializer_list

1. Function default arguments

The default arguments need to pay attention to the following points:

(1) The assignment of the default actual parameters of the function should be from right to left, otherwise a compilation error will be reported, because the parameters should be pushed into the stack from right to left.

void f(int, int, int = 1);
void f(int, int = 2, int);
void f(int = 3, int, int);

(2) The default argument outside the class will make the non-default constructor of the class become the default constructor.

class A
{
    
    
public:

	A(int a);

	void Print()
	{
    
    
		std::cout << i << std::endl;
	}

	int i;
};
// 类外初始化默认实参
A::A(int a = 100) : i(a) {
    
    }
/** 在类外初始化非默认构造函数,将其变为默认构造函数 */
A a = A();
a.Print();  // 输出:100

(3) If the default argument of the parameter of the function is added in the class, then the default argument of the parameter is defined again outside the class, and a redefinition error will occur.

(4) The default actual parameter of the virtual function will be determined according to the static type of the object (a type that is directly specified at compile time and will not change).

struct F
{
    
    
	virtual ~F()
	{
    
    
		// 父类
	}
};

struct C : F
{
    
    
	// 子类
};

/**
* 对于p来说静态类型就是F
* 对于p来说动态类型就是C
* 所以如果父类和子类都有默认实参的话,会使用F中的默认实参函数
*/
F* p = new C();

2. Default initialization

There is nothing difficult about default initialization. It should be noted that default initialization is newly added by C++11. Mainly look at bit field initialization.

struct B
{
    
    
	// int的低8位被初始化为12
	int x : 8 = 12;
	// int的低8位被初始化为17
	int y : 4 {
    
     17 };
};

When using bit field initialization, be sure to pay attention to the :priority of operators and used later.

3 initializer_list

3.1 The nature of initialization lists

#include <initializer_list>
std::initializer_list

template <class _Elem>
class initializer_list {
    
    
public:
    using value_type      = _Elem;
    using reference       = const _Elem&;
    using const_reference = const _Elem&;
    using size_type       = size_t;

    using iterator       = const _Elem*;
    using const_iterator = const _Elem*;

    constexpr initializer_list() noexcept : _First(nullptr), _Last(nullptr) {
    
    }

    constexpr initializer_list(const _Elem* _First_arg, const _Elem* _Last_arg) noexcept
        : _First(_First_arg), _Last(_Last_arg) {
    
    }

    _NODISCARD constexpr const _Elem* begin() const noexcept {
    
    
        return _First;
    }

    _NODISCARD constexpr const _Elem* end() const noexcept {
    
    
        return _Last;
    }

    _NODISCARD constexpr size_t size() const noexcept {
    
    
        return static_cast<size_t>(_Last - _First);
    }

private:
    const _Elem* _First;
    const _Elem* _Last;
};

It can be seen that initializer_list is a piece of memory space with begin and end.

int x[] = {
    
     1, 2, 3, 4, 5 };
std::vector<int> v{
    
    1, 2, 3, 4, 5};

It is equivalent to using initializer_list{1, 2, 3, 4, 5}, which is to construct an array { 1, 2, 3, 4, 5 } first, and then assign the first and last addresses to begin and end.

class A
{
    
    
public:
	/** 使用初始化列表构造并遍历 */
	A(std::initializer_list<int> list)
	{
    
    
		for (const int* item = list.begin(); item != list.end(); ++item)
		{
    
    
			std::cout << *item << std::endl;
		}
	}
};

Initialization priority:

/** 调用构造5个元素,每个元素都是5 */
std::vector<int> x1(5, 5);
/** 调用构造2个元素,5和5 */
std::vector<int> x2{
    
    5, 5};

3.2 Implicit narrowing conversions

Implicit narrowing conversion rules:
(1) High-order to low-order conversion, such as double to float, float to int.
(2) Conversion from an integer type to a type exceeding its maximum value, such as: int a = 999, conversion to char.

3.3 Specify initialization

To increase flexibility, C++20 adds designated initialization.

struct Point3D
{
    
    
	int x;
	int y;
	int z;
};
// 初始化列表构造,x=0,y=0,z=3
Point3D{
    
    .z = 3};

Although the designated initialization has been added, there are many limitations:
(1) If Point3D has a constructor, the initialization list will follow the constructor, and the designated member variables are likely to fail.
(2) The order of the specified initialization should be performed in the order of definition.
(3) Only one union can be specified at a time; specified initialization cannot be nested; specified initialization cannot be mixed with ordinary ones.

Guess you like

Origin blog.csdn.net/qq_45617648/article/details/131913019