Getting Started with C++11
C++11 is an important standard of the C++ language, released in 2011. It introduces many new features and improvements aimed at improving development efficiency and code quality.
Compared with C++98/03, C++11 has brought a considerable number of changes, including about 140 new features, and about 600 bug fixes in the C++03 standard, which makes C ++11 is more like a new language conceived from C++98/03. In comparison, C++11 can be better used for system development and library development, the syntax is more generalized and simplified, more stable and safer, not only more powerful, but also can improve the development efficiency of programmers.
1. Uniform list initialization
1.1 {} initialization
In C++98, the standard allows the use of curly braces {} for uniform list initialization of array or structure elements.
struct Point {
int _x;
int _y;
};
int main() {
int array1[] = {
1, 2, 3, 4, 5};
int array2[5] = {
0};
Point p = {
1, 2};
Point p{
1,2 };
return 0;
}
C++11 expands the scope of use of the list enclosed in curly braces (initialization list), so that it can be used for all built-in types and user-defined types. When using the initialization list, you can add an equal sign (=), or Do not add . Here are some examples:
struct Point {
int _x;
int _y;
};
int main() {
int num1{
10}; // 初始化整型变量
double pi{
3.14159}; // 初始化浮点型变量
char ch{
'A'}; // 初始化字符型变量
bool flag{
true}; // 初始化布尔型变量
std::string name{
"John"};// 初始化字符串对象
int array1[]{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int array2[5]{
0};
Point p{
1, 2};
std::vector<int> numbers{
1, 2, 3, 4};// 初始化容器对象
std::list<int> lt{
1, 2, 3, 4};
std::pair<int, double> p{
42, 3.14};// 初始化pair对象
//C++11中列表初始化也可以适用于new表达式中
int *pa = new int[4]{
0};
return 0;
}
When creating an object, you can also use the list initialization method to call the 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, 1, 1);// old style()
// C++11支持的列表初始化,这里会调用构造函数初始化
Date d2{
2022, 1, 2};//new style{}
Date d3 = {
2022, 1, 3};
return 0;
}
When list initialization is initialized, if type truncation occurs, a warning or error will be reported
#include <iostream>
using namespace std;
int main() {
short c = 65535;
short d{
65535};
return 0;
}
//narrowing conversion of '65535' from 'int' to 'short int' inside { } [-Wnarrowing]
1.2 std::initializer_list
std::initializer_list
is a class template introduced in the C++11 standard library to simplify the handling of initialization lists. It allows passing a set of values in the form of an initializer list in a function or constructor and accessing these values in a uniform way on the receiving end.
std::initializer_list
Provides an easy way to access elements in the initialization list by overloading the constructor and the iterator interface. It can be used to define function parameters, constructor parameters, and other container classes that accept initializer lists.
What is the type of std::initializer_list
void test() {
auto i1 = {
10, 20, 30, 40, 50, 60, 70 };
auto i2 = {
10, 20, 30 };
cout << typeid(i1).name() << endl; //class std::initializer_list<int>
cout << typeid(i2).name() << endl; //class std::initializer_list<int>
}
std::initializer_list features and usage
-
Constructor and member functions:
std::initializer_list
is a template class that accepts elements of any type and provides constructors and member functions to handle initialization lists. These functions include:-
initializer_list()
: The default constructor, which creates an empty initializer list. -
initializer_list(size_type count, const T& value)
: Constructor that creates an initialization list containing the specified number of elements, each of which is a given value. -
const T* begin() const
: Returns a pointer to the first element in the initialization list. -
const T* end() const
: Returns a pointer to the next position of the last element in the initialization list. -
size_type size() const
: Returns the number of elements in the initialization list.
-
-
Syntax usage: The syntax for using
std::initializer_list
is similar to using curly braces{}
to initialize a list. For example:std::initializer_list<int> myList = { 1, 2, 3, 4, 5};
-
Function parameters: can be
std::initializer_list
used as parameters of a function to pass a set of values when the function is called. For example:void myFunction(std::initializer_list<int> values) { // 使用初始化列表中的值 for (const auto& value : values) { // 处理每个值 } } // 调用函数 myFunction({ 1, 2, 3, 4, 5});
-
Constructor Parameters: The constructor of a class can accept
std::initializer_list
parameters for initialization using an initialization list when the object is created. For example:class MyClass { public: MyClass(std::initializer_list<int> values) { // 使用初始化列表中的值 for (const auto& value : values) { // 处理每个值 } } }; // 创建对象时使用初始化列表 MyClass obj = { 1, 2, 3, 4, 5};
std::initializer_list usage scenarios
std::initializer_list
Generally, it is used as a parameter of the constructor. C++11 adds a std::initializer_list
constructor as a parameter to many containers in STL, so that it is more convenient to initialize the container object. It can also be used as a parameter of operator=, so that it can be assigned with curly braces
void test() {
auto i1 = {
10, 20, 30, 40, 50, 60, 70 };
auto i2 = {
10, 20, 30 };
initializer_list<int>::iterator it1 = i1.begin();
initializer_list<int>::iterator it2 = i2.begin();
cout << it1 << endl; //00000053083EF1F8
cout << it2 << endl; //00000053083EF268
initializer_list<int> i3 = {
10,20,30,40,50,60,70,80 };
initializer_list<int>::iterator it3 = i3.begin();
cout << it3 << endl; //00000053083EF308
}
void test2() {
Date d1(2023,5,20);
Date d2(2023,5,21);
//initializer_list<Date> 从C++11开始常量数组会被识别成initializer_list
vector<Date> v1 = {
d1,d2 };
vector<Date> v2 = {
Date(2023,5,20),Date(2023,5,21) };
vector<Date> v3 = {
{
2023,5,20},{
2023,5,21} };
map<string, string> dict = {
{
"sort","排序"},{
"string","字符串"},{
"Date","日期"}};
pair<string, string> kv1 = {
"Date","日期" };
pair<string, string> kv2{
"Date","日期" };
}
std::initializer_list
The introduction of makes working with initializer lists in C++ more convenient and consistent. Its use in function parameters and constructor parameters provides a concise and flexible way of manipulating a set of values.
Let the simulated vector also support {} initialization and assignment
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
typename initializer_list<T>::iterator lit = l.begin();
while (lit != l.end())
{
*vit++ = *lit++;
}
//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;
};
2. Declaration
2.1 auto
Before C++11 (same as C language), auto
it was a storage class specifier used to indicate that the variable has automatic storage duration. When declaring a variable inside a function, if no storage-class specifier is explicitly specified, it defaults to auto
the storage class.
auto
Storage class means that variables are automatically created when they enter their scope and destroyed when they leave their scope. auto
This is the default behavior of variables in C, so it's uncommon to use the keyword in C.
But the local variable defined in the local field is the automatic storage type by default, so auto is of little value. In C++11, the original usage of auto is discarded, and it is used to realize automatic type inference. This requires explicit initialization, which lets the compiler set the type of the defining object to the type of the initialization value.
When you use the auto keyword, the compiler analyzes the initialization expression on the right side of the assignment operator (=) and determines the appropriate type for the variable. Then, the type is deduced and assigned to the variable.
Here is an example illustrating the use of auto:
void test(){
auto x = 42; // x 被推导为 int 类型
auto y = 3.14; // y 被推导为 double 类型
auto z = "Hello, World!"; // z 被推导为 const char* 类型
// 你也可以在更复杂的类型中使用 auto,比如迭代器:
std::vector<int> numbers = {
1, 2, 3, 4, 5};
auto it = numbers.begin(); // it 被推导为 std::vector<int>::iterator 类型
int i = 10;
auto p = &i;
auto pf = strcpy;
cout << typeid(p).name() << endl; //int * __ptr64
cout << typeid(pf).name() << endl; //char * __ptr64 (__cdecl*)(char * __ptr64,char const * __ptr64)
}
2.2 decltype
decltype
It is a type deduction mechanism introduced by C++11 . It is used to deduce the type of a variable or expression from an expression. The type of an expression is evaluated at compile time and used as part of the return type of a variable or function.
decltype
The syntax for is as follows:
decltype(expression)
Among them, expression
is an expression, which can be a variable name, function call, type conversion, etc.
decltype
works like this:
- If
expression
is an identifier (variable name),decltype(expression)
the type of the identifier will be deduced. - If
expression
is a function call,decltype(expression)
the return type of the function call will be deduced. - If
expression
is an expression,decltype(expression)
the type of the expression will be deduced.
Here are some examples to illustrate decltype
the usage of :
void test(){
int x = 5;
decltype(x) y = 10; // 推导出 y 的类型为 int
int foo();
decltype(foo()) result = foo(); // 推导出 result 的类型为 foo() 函数的返回类型
int a = 1;
int b = 2;
decltype(a + b) c = a + b; // 推导出 c 的类型为 int,表达式 a + b 的类型为 int
}
decltype
A common use of is in template programming, where it can be used to deduce types from function return values or expressions and use them as template parameters or variable types for other types.
It should be noted that decltype
the expression is not executed, it is only used to deduce the type of the expression. Also, decltype
deduced types may preserve reference and cv-qualifiers (const and volatile).
Some usage scenarios of decltype
template<class T1, class T2>
void F(T1 t1, T2 t2) {
decltype(t1 * t2) ret;
cout << typeid(ret).name() << endl; //int
}
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; //double
cout << typeid(p).name() << endl; //int const * __ptr64
F(1, 'a');
// vector存储的类型跟x*y表达式返回值类型一致
// decltype推导表达式类型,用这个类型实例化模板参数或者定义对象
vector<decltype(x* y)> v;
return 0;
}
2.3 nullptr
nullptr
is a null pointer constant used to represent a null pointer. It is a new feature introduced by the C++11 standard, which aims to solve some ambiguity and ambiguity problems of NULL in C++.
In C++, pointers are usually used to point to objects or functions in memory. Whereas, a null pointer means that the pointer does not point to any valid object or function. In previous versions of C++, NULL
macros were usually used to represent null pointers, which were usually defined as 0 or (void*)0
. However, such a definition may cause ambiguity in some cases, since 0 may be used to represent the zero value of integer types.
To solve this problem, C++11 introduced nullptr
the keyword, which is an explicit null pointer constant. Use nullptr
to unambiguously indicate that a pointer is null without confusion with the integer 0. This avoids some programming errors and ambiguities.
The syntax used nullptr
is very simple, just assign it directly to the pointer variable, for example:
int* ptr = nullptr; // 整型指针指向空
nullptr
Can also be used for comparison operations with other pointers. When compared against other pointers, nullptr
the result of will be true (i.e. the pointer is null). For example:
int* ptr = nullptr;
if (ptr == nullptr) {
// 指针为空
}
nullptr
It is a null pointer constant introduced by C++11, which is used to clearly represent the null pointer without causing ambiguity. It provides better type safety and code clarity, and is the recommended representation of null pointers in modern C++ programming.
3. Range for loop
3.1 Syntax of range for
In C++98, if you want to traverse an array, you can do it in the following way:
void TestFor() {
int array[] = {
1, 2, 3, 4, 5};
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
array[i] *= 2;
for (int *p = array; p < array + sizeof(array) / sizeof(array[0]); ++p)
cout << *p << endl;
}
For a ranged collection , it is redundant and sometimes error-prone for the programmer to specify the range of the loop. Therefore, range-based for loops were introduced in C++11. The parentheses after the for loop are divided into two parts by the colon ":": the first part is the variable used for iteration in the range, and the second part represents the range to be iterated.
void TestFor() {
int array[] = {
1, 2, 3, 4, 5};
for (auto &e: array)
e *= 2;
for (auto e: array)
cout << e << " ";
}
Note: Similar to ordinary loops, you can use continue to end this loop, or use break to jump out of the entire loop.
3.2 Conditions of use of scope for
1. The range of for loop iterations must be definite
**For an array, it is the range of the first element and the last element in the array; **For a class, methods of begin and end should be provided, and begin and end are the range of for loop iterations.
Note: The following code is problematic because the scope of for is uncertain
void TestFor(int array[]) {
for (auto &e: array)
cout << e << endl;
}
2. The iterated object must implement ++ and == operations .
4. Changes in STL
C++11 introduces some important improvements and new features, as well as some changes and enhancements to the STL (Standard Template Library). The following are the main changes in STL after C++11:
- Move Semantics: C++11 introduces Rvalue references, Move constructors, and Move assignment operators. These features enable STL containers and algorithms to manage resources more efficiently and provide better support for movable types such as std::unique_ptr and std::shared_ptr.
- New container types: C++11 introduces two new container types:
- std::array: is a fixed-size array container that provides a safer and more convenient interface than C-style arrays.
- std::unordered_XXX: introduces hash containers such as std::unordered_map and std::unordered_set to provide fast hash-based lookup and insertion operations.
- New algorithms: C++11 introduces some new algorithms to enrich the functions of STL:
- std::move, std::move_backward: used to move elements.
- std::copy_if, std::move_if: Copy or move elements based on conditions.
- std::find_if_not: Finds the first element that does not satisfy the given condition.
- std::is_sorted, std::is_partitioned: Used to check whether a sequence is sorted or partitioned.
- std::all_of, std::any_of, std::none_of: Used to check whether elements in a sequence satisfy a certain condition.
- Lambda expressions: C++11 introduces lambda expressions, which allow anonymous functions to be written in a more compact and convenient way in STL algorithms. Using lambda expressions, you can more easily write custom predicates (predicates) and other function objects.
- Concurrent programming support: C++11 introduces concurrent programming primitives such as std::thread and std::mutex, making STL easier to use in a multi-threaded environment. In addition, atomic operation types such as std::atomic and std::atomic_flag are introduced to implement lock-free algorithms.
- New standard library components: C++11 introduces some new standard library components, such as:
- std::tuple: A tuple used to represent a fixed number of heterogeneous values.
- std::chrono: Provides time and date types and operations.
- std::regex: A library that supports regular expressions.
These changes and enhancements make the post-C++11 STL more powerful, more efficient, and provide more tools and options to handle the demands of modern C++ programming.