[C++11]——List initialization, declaration and STL changes

 

Table of contents

1. Introduction to C++11

2. Unified list initialization

2.1 {} initialization

2.2 initializer_list container

3. Statement

auto

decltype

nullptr

4. Some changes in STL


1. Introduction to C++11

  1. In 2003, the C++ Standards Committee submitted a technical corrigendum (TC1 for short), so that the name C++03 has replaced the name of the latest C++ standard before C++98 was called C++11. However, since C++03 (TC1) mainly fixes the loopholes in the C++98 standard, and the core part of the language has not been changed, people habitually combine the two standards and call it the C++98/03 standard. .
  2. From C++0x to C++11, the C++ standard has been grinding for 10 years, and the second true standard has come late. Compared with C++98/03, C++11 has brought a considerable number of changes, including about 140 new features and corrections to about 600 defects in the C++03 standard, which makes C ++11 is more like a new language born from C++98/03.
  3. In comparison, C++11 can be better used for system development and library development, the syntax is more general and simplified, more stable and secure, not only is the function more powerful, but also can improve the development efficiency of programmers. The company's actual It is also used frequently in project development, so we should study it as a focus. The grammatical features added by C++11 are very lengthy and we cannot explain them one by one here. We mainly explain the more practical grammar in practice. For details, see the official website ( C++11 - cppreference.com ).
  • 1998 was the first year that the C++ Standards Committee was established. It was originally planned to update the standard every five years based on actual needs. When the C++ International Standards Committee was studying the next version of C++03, it initially planned to release it in 2007, so initially This standard is called C++07. But by 2006, officials felt that C++07 would definitely not be completed in 2007, and officials felt that it might not be completed in 2008. In the end, it was simply called C++ 0x. x means I don’t know whether it will be completed in 2007, 2008 or 2009. As a result, it was not completed in 2010, and the C++ standard was finally completed in 2011. So it was finally named C++11.

2. Unified list initialization

2.1 {} initialization

In C++98 , the standard allows the use of curly braces {} for uniform list initialization of array or structure elements. for example:

struct Point
{
	int _x;
	int _y;
};
int main()
{
	int array1[] = { 1, 2, 3, 4, 5 };
	int array2[5] = { 0 };
	Point p = { 1, 2 };
	return 0;
}

C++11 expands the use of lists enclosed in curly braces (initialization lists) so that they can be used for all built-in types and user-defined types. When using initialization lists, you can add an equal sign (=), or No need to add .

struct Point
{
	int _x;
	int _y;
};
int main()
{
	int x1 = 1;//建议就用这个
	int x2 = { 2 };
	int x3{ 2 };
	int array1[]{ 1, 2, 3, 4, 5 };
	int array2[5]{ 0 };
	Point p{ 1, 2 };
	// C++11中列表初始化也可以适用于new表达式中
	int* p1 = new int(1);
	int* p2 = new int[3]{ 1, 3, 4 };
	return 0;
}

When creating an object, you can also use list initialization to call constructor initialization .

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2022, 10, 7); // old style
	// C++11支持的列表初始化,这里会调用构造函数初始化
	Date d2 = { 2022, 10, 9 };//类似隐式类型转换 + 优化
	Date d3{ 2022, 10, 8 };
 
	Date* p1 = new Date(1, 2, 3);
	Date* p2 = new Date[3]{ { 2022, 1, 1 }, { 2022, 1, 2 }, { 2022, 1, 3 } };
	return 0;
}

2.2 initializer_list container

Introduction to initializer_list:

The initializer_list container has been added to c++11. The introduction document is as follows: Introduction to initializer_list

This container provides only three member functions and one constructor:

int main()
{
	initializer_list<double> ilt = { 3.3, 5.5, 9.9 };
	initializer_list<double>::iterator it = ilt.begin();
	while (it != ilt.end())
	{
		cout << *it << " ";//3.3 5.5 9.9
		it++;
	}
	cout << endl;
	for (auto e : ilt)
	{
		cout << e << " ";//3.3 5.5 9.9
	}
	return 0;
}

This type is used to access values ​​in a C++ initialization list, which is a list of elements of the type. Objects of this type are automatically constructed by the compiler from an initializer list declaration, which is a comma-separated list of elements enclosed in braces: a braced list of const T。constants is recognized by the compiler as an initializer_list.

int main()
{
	// the type of il is an initializer_list
	auto il = { 10, 20, 30 };
	cout << typeid(il).name() << endl;//class std::initializer_list<int>
	return 0;
}

Usage scenarios of initializer_list:

initializer_list is generally used as a parameter of the constructor. C++11 adds initializer_list as a parameter to the constructor of many containers in STL, which makes it more convenient to initialize container objects. It can also be used as a parameter of operator =, so that values ​​can be assigned using braces:

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//vector
	vector<int> v1 = { 1, 2 ,3 ,4, 5 };
    // 使用大括号对容器赋值,{}调用构造函数构造一个vector对象,再赋值
    v1 = {10, 20, 30};
	vector<int> v2{ 1, 2, 3, 4, 5 };
	vector<Date> v3 = { { 2022, 1, 1 }, { 2022, 1, 2 }, { 2022, 1, 3 } };
 
	//list
	list<int> lt1{ 1, 2, 3 };
 
	//set
	set<int> s1{ 3, 4, 5, 6, 3 };
 
	//map
    // 这里{"sort", "排序"}会先初始化构造一个pair对象
	map<string, string> dict = { {"string", "字符串" }, {"sort", "排序" } };
	return 0;
}

As mentioned earlier, it is so convenient to initialize container objects like the above code because many containers in C++11STL add initializer_list as a parameter to the constructor: it can also be used as a parameter of operator =, so that you can use braces to assign values. :

 

Usage example of initializer_list:

I have already simulated and implemented vector ( simulated implementation of vector ) before. Next, I will modify it to support {} initialization and assignment.

namespace cpp
{
	template<class T>
	class vector {
	public:
		typedef T* iterator;
		vector(initializer_list<T> l)
		{
			_start = new T[l.size()];
			_finish = _start + l.size();
			_endofstorage = _start + l.size();
			//迭代器遍历
			/*
			iterator vit = _start;
			typename initializer_list<T>::iterator lit = l.begin();
			while (lit != l.end())
			{
				*vit++ = *lit++;
			}
			*/
			//范围for遍历
			for (auto e : l)
			{
				*vit++ = e;
			}
		}
		vector<T>& operator=(initializer_list<T> l) {
			vector<T> tmp(l);
			std::swap(_start, tmp._start);
			std::swap(_finish, tmp._finish);
			std::swap(_endofstorage, tmp._endofstorage);
			return *this;
		}
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

The constructor here can be implemented even simpler, by directly initializing the three member variables in the initialization list, traversing l, and reusing push_back to insert them into the vector in sequence:

vector(initializer_list<T> l)
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstoage(nullptr)
{
	for (auto e : l)
	{
		push_back(e);
	}
}

Notice:

  1. When using iterator traversal, you need to add typename to indicate that this is a type name, because this iterator type is defined in a class template, and the compiler cannot recognize this type before the class template is instantiated.
  2. It is best to add an assignment operator overloaded function that takes initializer_list as a parameter to support direct assignment of container objects using lists, but it is not necessary to add it.
vector<int> v1 = { 1, 2 ,3 ,4, 5 };
// 使用大括号对容器赋值,{}调用构造函数构造一个vector对象,再赋值
v1 = {10, 20, 30};

For the assignment operation in the second line, implicit type conversion is involved. First, use {} to call the constructor to construct a vector object, and then assign the value.

3. Statement

C++11 provides several ways to simplify declarations, especially when using templates.

auto

In C++98, auto is a storage type specifier, indicating that the variable is a local automatic storage type. However, local variables defined in a local domain default to an automatic storage type, so auto has no value. The original usage of auto is abandoned in C++11 and used to implement automatic type breaking. This requires explicit initialization, allowing the compiler to set the type of the defined object to the type of the initialized value. See the previous blog post for details.

decltype

decltype declares the type of the variable to be the type specified by the expression.

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
	decltype(t1 * t2) ret;
	cout << typeid(ret).name() << endl;//double
}
int main()
{
	const int x = 1;
	double y = 2.2;
	decltype(x * y) ret; // ret的类型是double
	decltype(&x) p; // p的类型是int*
	cout << typeid(ret).name() << endl;//int const * __ptr64
	cout << typeid(p).name() << endl;//int
	F(1, 'a');
	return 0;
}

Let's distinguish between typeid and decltype:

  • typeid (variable name).name(): specially used to output the type of a variable, and returns a string. Help us observe the type of this string, which cannot be used to define variables.
  • decltype: declares the type of the variable as the type specified by the expression, which can be used to define the variable

nullptr

Since NULL in C++ is defined as literal 0, this may cause some problems, because 0 can represent both a pointer constant and an integer constant. Therefore, for the sake of clarity and safety, nullptr is added in C++11 to represent a null pointer.

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endi

If no macro is defined, NULL is defined as 0 in cplusplus. As you can see, NULL may be defined as a literal constant 0, or as a constant of an untyped pointer (void*). No matter what definition is adopted, you will inevitably encounter some troubles when using null-valued pointers, such as:

The original intention of the program is to call the pointer version of the f(int*) function through f(NULL), but because NULL is defined as 0, it goes against the original intention of the program. In C++98, the literal constant 0 can be either an integer number or an untyped pointer (void*) constant, but the compiler treats it as an integer constant by default. If you want to treat it as To use it in pointer mode, it must be cast (void *)0 .

Notice:

  1. When using nullptr to represent a pointer null value, there is no need to include the header file because nullptr was introduced as a new keyword in C++11.
  2. In C++11, sizeof(nullptr) occupies the same number of bytes as sizeof((void*)0).
  3. In order to improve the robustness of the code, it is recommended to use nullptr when subsequently representing a pointer null value.

4. Some changes in STL

new container

  • Circled in orange are some new containers in C++11, but the most useful ones are unordered_map and unordered_set.

 

1, array container

Array is a static array with two template parameters. The first template parameter represents the stored data type, and the second is a non-type template parameter, which represents the number of stored elements:

int main()
{
	array<int, 10> a1;
	array<double, 15> a2;
	return 0;
}

The biggest difference between arrays and ordinary arrays is the check for out-of-bounds access:

int main()
{
	int a[10];
	cout << a[10] << endl;//越界不一定能检查出来
	array<int, 10> b;
	cout << b[10] << endl;//只要越界,一定能检查出来
	return 0;
}

Summarize:

  1. The objects of the array container are built in the stack area and are not suitable for defining large arrays.
  2. The array container may be designed to replace static arrays, because array containers are safer and can detect out-of-bounds errors, while static arrays may not necessarily be able to detect them.
  3. Vector has everything that array array has. Personally, I think vector is better.

2. forward_list container

The forward_list container is essentially a singly linked list. The difference compared to list is that forward_list saves space. In actual use, the rate of using forward_list is still relatively low, and it is more convenient to use list.

3. unordered_map and unordered_set containers

It is quite practical and will be introduced in a blog post later.

Some new methods in containers

If we take a closer look, we will find that some C++11 methods have been added to basically every container, but in fact many of them are rarely used.

After the C++11 update, the new methods added to the container finally use the rvalue reference version of the inserted interface function.

Guess you like

Origin blog.csdn.net/m0_49687898/article/details/131967788