[Elementary C++] Beginner’s introduction to C++

Table of contents

Foreword:

From the beginning of this article, I have officially entered the study of C++. C++ adds object-oriented ideas and many libraries based on the C language. Compared with the C language, C++ greatly improves the efficiency in use. At the same time, it also fills in the grammar. Get into the trap of C language. This article introduces the basic syntax knowledge for getting started with C++. Let’s read on together~
Insert image description here

1. C++ keywords

C++ has a total of 63 keywords, and C language has 32. After all, it "evolved" from the C language, and there must be more to add. We already know some keywords, and a small part will be introduced later in this article. Others will be focused on in the future. Here we just take a look at the keywords in C++.
Insert image description here

2. Namespace

2.1 What is a namespace?

Namespace is a concept in the C++ standard library. As the name suggests, it simply means to define the name of a variable or function in a specified space. This space also has its name. Programmers must be able to find the variables to be used through the namespace. Or function. For example, std is the namespace name defined by the C++ standard library. To use functions such as cout and cin, you must not only have the corresponding header file, but also expand the namespace (this will be discussed in detail later), because C++ uses the standard The definition and implementation of the library are placed in this namespace.

2.2 Why there is a namespace?

We found a problem when we learned C language before, or this is the grammatical rule of C language. C language syntax rules: In the global scope, two variables cannot have the same name, and the same is true for functions.

But what if the same name must be used in some scenarios? Having to modify the variable name or function name can easily cause confusion.

Therefore, C++ has added the new syntax of namespace, which can support the appearance of two or more variables or functions with the same name in the global scope. As long as a variable is defined in the namespace and the other variable is a global variable (or in another namespace), we only need to find that variable in the corresponding namespace to use it. There will be no difference if the two names are the same. Naming conflict.

Let’s give an example: Xiao Ming’s grandfather planted a fruit forest on the mountain. Xiao Ming went to pick fruits on the mountain. But by chance, there was another fruit forest on the mountain. So Xiao Ming was confused. He didn’t know which fruit forest was planted by his grandfather. What if he picked it? If he made a mistake, he would be asking for trouble, so Xiao Ming had no choice but to go down the mountain. Later, grandpa built a wall in his orchard, and his name was marked on the door. Xiao Ming went up the mountain again and found his grandpa's orchard, and he could enter to pick fruits.

Look at the following code:

#include <stdio.h>
#include <stdlib.h>

int rand = 10;
int main()
{
    
    
	printf("%d\n", rand);
	return 0;
}

We have defined a global variable rand, but there is a header file #include <stdlib.h>. In this header file rand is a function name, which means that the name of the global variable we defined overlaps with the name of the function. , so the compiler reports an error.
Insert image description here

2.3 How to use namespace

2.3.1 How to write namespace

To define a namespace, you need the namespace keyword——namespace, and then after the namespace is the name of the namespace, the name It is customized, and it is generally customary to write the initials of your name. Variables, functions, and types can be defined in the namespace.

// 关键字+命名空间名
namespace yss
{
    
    
	int Add(int a, int b)//定义函数
	{
    
    
		int c = a + b;
		return c;
	}
}

After defining this function, we need to use this function in the main function, and we also need to understand a symbol

Scope qualifier - ::

The scope qualifier consists of two colons, and functions in the namespace can be accessed through the scope qualifier.

//   命名空间名 + 域作用限定符 + 函数名
int ret = yss::Add(1, 3);

Note: As long as the variable, function or type defined is in the namespace, it must be accessed in this way, otherwise the variable/function/type cannot be found.

2.3.2 Namespaces can be nested

namespace yss
{
    
    
	namespace yss1
	{
    
    
		int Add1(int a, int b)
		{
    
    
			int c = a + b;
			return c;
		}
	}
	int Add(int a, int b)
	{
    
    
		int c = a + b;
		return c;
	}
}

int main()
{
    
    
	int ret = yss::Add(1, 3);
	int ret1 = yss::yss1::Add1(2, 4);
	return 0;
}

To find the Add1 function, you must first enter the yss namespace, and then enter the yss1 namespace.

Note: If there are the same namespaces in multiple files, that is, the names of these namespaces are the same, then the compiler will integrate them into a whole; similarly, there are multiple identical namespaces in one file. of.

2.3.3 Three ways to use namespaces

Type 1: Expand all

namespace yss
{
    
    
	int Add(int a, int b)
	{
    
    
		int c = a + b;
		return c;
	}
}
using namespace yss;//
int main()
{
    
    
	int ret = Add(1, 3);
	printf("%d %d", ret);
	return 0;
}

This method eliminates the need for scope qualifiers.

Second type: local expansion

namespace yss
{
    
    
	int Add(int a, int b)
	{
    
    
		int c = a + b;
		return c;
	}
}
using yss::Add;
int main()
{
    
    
	int ret = Add(1, 3);
	printf("%d %d", ret);
	return 0;
}

The third method: bring namespace name + domain qualifier (the previous one)

3. C++ input and output

3.1 First introduction to cout and cin

When we first learn C language, the first program we output is Hello World!; when we are new to C++, we also need to know its output function and use it to print out Hello World!

#include <iostream>
using namespace std;
int main()
{
    
    
	cout << "Hello World!" << endl;
	return 0;
}

Insert image description here
Let’s ignore what cout, endl and << are, and then look at the input and output of the first C++ program:

#include <iostream>
using namespace std;
int main()
{
    
    
	int a = 0;
	cin >> a;
	cout << a;
	return 0;
}

Insert image description here
After reading the input and output of C++, we found that it is much easier to write than the input and output of C language. However, we need to understand the input and output of C++.

cout is the standard output object; cin is the standard input object; endl is a special C++ symbol that represents a newline. They are all included in the header file < iostream > and used according to the namespace usage std .

<< is the stream insertion operator; >> is the stream extraction operator

3.2C++ input and output can automatically identify variable types

#include <iostream>
using namespace std;
int main()
{
    
    
	float f = 0;
	cin >> f;
	cout << f << endl;
	int i = 0;
	cin >> i;
	cout << i << endl;
	return 0;
}

Insert image description here
Then there is another question, what if you want to keep one decimal place? It's actually very simple, because C++ is compatible with C, so things in C can also be used in C++.

Control the decimal point:

printf("%.1f", f);

Supplement:
std is the namespace of the C++ standard library. Using using namespace std to expand the standard library will expose all the variables/functions/types defined in the library. There will be conflicts in the names. This kind of problem usually occurs when there are a lot of codes and the scale is large, such as project development. Therefore, in project development, partial expansion is generally used. We rarely use the functions in those libraries in daily practice, and there are not many codes. In order to facilitate operation and practice, we can use all expansion in daily practice.

4. Default parameters

4.1 What are the default parameters?

Default parameters specify one or more default values ​​for the parameters of the function when declaring or defining the function. When calling this function, if no value is passed for the actual parameter, the default value of the formal parameter will be used as the parameter, otherwise the specified actual parameter will be used. C language does not support default parameters.

For example, we normally call a function like this:

void Func(int a, int b)
{
    
    
	cout << a + b << endl;
}
int main()
{
    
    
	Func(1, 2);
	return 0;
}

The formal parameter part is type + variable name

Call a function with default parameters:

void Func(int a = 10, int b = 20)
{
    
    
	cout << a + b << endl;
}
int main()
{
    
    
	Func();
	return 0;
}

Insert image description here
When no parameters are passed, the default value of the parameter is used, that is, the default parameter.

Note: The default value must be a constant or global variable

4.2 Classification and use of default parameters

Full default parameters:

void Func(int a = 1, int b = 2, int c = 3)
{
    
    
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}
int main()
{
    
    
	Func();
	return 0;
}

Insert image description here
Semi-default parameters:

void Func(int a, int b = 2, int c = 3)
{
    
    
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}
int main()
{
    
    
	Func(10);
	Func(10, 20);
	Func(10, 20, 30);
	return 0;
}

Insert image description here

Semi-default parameters must be given sequentially from right to left, and cannot be given at intervals.

int a, int b = 2, int c
int a = 1, int b = 2, int c

The above is wrong!

Default parameters cannot appear in function declarations and definitions at the same time
Because when two default parameters appear at the same time, it doesn’t matter if the default values ​​on both sides are the same. If you call this function differently, you don't know which default value to use, causing ambiguity. Therefore, when the function declaration and definition are separated, it is better to give default parameters at the time of declaration.

5. Function overloading

5.1 Definition and use of function overloading

In C++, functions with the same name but different types/number of parameters/type order are allowed to appear in the same scope. Such functions are called overloaded functions. Often used to solve problems with similar functions but different data types.

Different types:

void Add(int a, int b)
{
    
    
	cout << a + b << endl;
}
void Add(double a, double b)
{
    
    
	cout << a + b << endl;
}
int main()
{
    
    
	Add(1, 3);
	Add(1.1, 3.3);
	return 0;
}

Insert image description here
The number of parameters is different:

void Add(int a, int b)
{
    
    
	cout << a <<endl << b << endl;
}
void Add(int a)
{
    
    
	cout << a << endl;
}
int main()
{
    
    
	Add(1, 3);
	Add(6);
	return 0;
}

Insert image description here

The type order is different:

void Func(int a, char c)
{
    
    
	cout << a << endl;
	cout << c << endl;
}
void Func(char a, int c)
{
    
    
	cout << a << endl;
	cout << c << endl;
}
int main()
{
    
    
	Func(1, 'y');
	Func('u', 2);
	return 0;
}

Insert image description here

5.2 Overloading ambiguity distinction

1: Function parameter types are different

void Add(int a, int b)
{
    
    
	cout << a + b << endl;
}
void Add(double a, double b)
{
    
    
	cout << a + b << endl;
}
int main()
{
    
    
	Add(1, 3);
	Add(1.1, 3.3);
	Add(1, 3.3);///
	return 0;
}

The type of one parameter of the third Add function is an integer and the other is a floating point type. So at this time, the Add function does not know which of the above two should be called. If it is the first one, double is converted to int. If it is the second one, int is converted to double. Therefore, there is ambiguity here and the compiler will report an error.
Insert image description here
2: When no parameters are passed, formal parameters are default parameters
Let’s first look at a normal running code:

void Func()
{
    
    
	cout << "func" << endl;
}
void Func(int a)
{
    
    
	cout << "func" << endl;
}
int main()
{
    
    
	Func();
	Func(1);
	return 0;
}

The function without formal parameters is called without parameters, and the function with formal parameters is called with parameters. Next let the formal parameter a = 10

void Func()
{
    
    
	cout << "func" << endl;
}
void Func(int a = 10)
{
    
    
	cout << "func" << endl;
}
int main()
{
    
    
	Func();
	Func(1);
	return 0;
}

We can find such an error:
Insert image description here
Because when calling a function without passing parameters, it can either be the function corresponding to the top or the function below. The missing function in front After learning about provincial parameters, when no parameters are passed, the default value is used by default, so ambiguity occurs.

Three: The function names and parameter types are the same, but the return values ​​are different
When we first learned about overloaded functions, we knew that the overloaded functions have the same function name, different types, or different parameters. The numbers are different. But why can't the return value be different?
Look at the following code:

int Func()
{
    
    
	return 2;
}
double Func()
{
    
    
	return 2.3;
}int main()
{
    
    
	Func();
	return 0;
}

The error is as follows:
Insert image description here
We call a function, but we don’t know which function above returns it, so different return values ​​cannot constitute overloading.

5.3 Why does C++ support overloading but C does not?

To understand why C++ supports overloading, we must first understand the process of running the program

The scale of a project is often relatively large, so it is impossible to squeeze all the code into one file. It is necessary to write code in separate files. Then, when the program is running, it roughly needs to go through these 4 steps:

Preprocessing——>Compile——>Assembly——>Link

Assume that a.cpp calls a function defined in b.cpp and has not yet been linked after compilation. The object file of a.o does not have the address of the function. Because the function is defined in b.cpp, the address of the function is in b.o. The next link will work. If a.o calls the function but does not have the function's address, the linker will find the function's address in the symbol table of b.o and then link it.

And how will the linker find it here? The answer is to find it through the function name, but different compilers have different ways of finding it, that is, the function name modification rules are different.

Here we use gcc and g++ to take a look at Linux (Windows is more complicated)
gcc compiler:
Insert image description here
g++ compiler: a>
Insert image description here
It can be seen that in the Linux environment, the function name has not changed after the gcc compiler is completed; and the modified function name after the g++ compiler is completed is [_Z+function length+function name+type initial letter] . This can explain why the C language does not support overloading, because the modified function name remains unchanged and cannot be distinguished; while the C++ compiler uses function name modification rules as long as the number of function parameters is different or the parameter type is different. Being able to distinguish them supports overloading.

6. Quote

6.1 What is a reference

A reference is not a newly added variable, but an alias for an existing variable. The reference and the referenced object share a memory space.

For example, Tieniu is an alias for Li Kui, and Tieniu and Li Kui are the same person.

int main()
{
    
    
	int a = 1;
	int& b = a;
	return 0;
}

a is an existing variable, b is a reference or alias of a, b does not occupy a separate space, but shares a memory space with a, so changing b is changing a.

6.2 Why are there citations?

We know that pointer operations in C language are relatively dangerous. If used improperly, there will be very serious consequences. Therefore, C++ has added the concept of reference, which can modify the referenced object, and also makes the program more readable and safer.

6.3 Instructions for use of quotations

6.3.1 How to write quotations

Type & reference variable name (object name) = reference entity;

	int a = 3;
	int& b = a;

Note: the reference type must be of the same type as the referenced entity

6.3.2 References must be initialized

A reference must have an object that is referenced, that is, initialized.

int main()
{
    
    
	int a = 3;
	int& b;
	return 0;
}

No initialization is an error. You can see the compiler display:
Insert image description here

6.3.3 References cannot change their pointing

We have studied linked lists before. The pointer in the linked list changes the content pointed to to realize the addition, deletion, check and modification of the linked list. However, C++ stipulates that the reference cannot change the pointer.
For example:

int main()
{
    
    
	int a = 2;
	int b = 3;
	int& c = a;
	int& c = b;
	return 0;
}

Insert image description here

c is an alias of a and b. If c is modified, should a be changed or b? So the reference cannot change the pointer.

6.3.4 An object can have multiple aliases

The previous one is one alias for multiple objects, here is one object multiple aliases. It's like a person has multiple nicknames.

int main()
{
    
    
	int a = 10;
	int& b = a;
	int& c = a;
	int& d = a;
	return 0;
}

Insert image description here

6.4 How to use quotes

6.4.1 Reference as parameter

To implement the exchange of two numbers, see the following code:

void Swap(int& a, int& b)
{
    
    
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
    
    
	int a = 3;
	int b = 5;
	Swap(a, b);
	cout << a << endl << b << endl;
	return 0;
}

Insert image description here
Let’s look at the previous way of writing it, using pointers:

void Swap(int* pa, int* pb)
{
    
    
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}
int main()
{
    
    
	int a = 4;
	int b = 7;
	Swap(&a, &b);
	cout << a << endl << b << endl;
}

Insert image description here
Through comparison, it is easy to see that using references as parameters is more concise and the program is more readable.

6.4.2 Reference as return value

The first way of writing: reference return value

int& Func(int a, int b)
{
    
    
	int c = a + b;
	return c;
}
int main()
{
    
    
	int ret = Func(1, 2);
	cout << ret << endl;
	return 0;
}

Insert image description here
The program has the following error:
Insert image description here
Analysis:
This code returns c as the sum of two numbers, and what is returned is the alias of c.
Insert image description here
Let’s look at a piece of code again:

int& Func(int a, int b)
{
    
    
	int c = a + b;
	return c;
}
int main()
{
    
    
	int ret = Func(1, 2);
	cout << ret << endl;

	Func(3, 4);
	cout << ret << endl;

	return 0;
}

Insert image description here
The variable ret receives the alias of the return value c. What is the alias of c is handled by the compiler and we do not need to worry about it. But we know that this scope will be destroyed after the function ends. The specific value of this c alias returned to ret depends on whether the compiler has cleaned it up. If it is cleaned up, it will be a random value, otherwise it will be the size of a+b. This is an uncertain value, and it has not been cleaned up under VS. Call this function again, because the variable ret occupies a separate space, and the return value it received from the first call was 3. However, after the second call to the function, the scope was destroyed, and ret did not receive the second call. The return value of the call, so the content of ret does not change, it is still 3

The second way of writing: The received variable is also a reference object, add & before ret

int& Func(int a, int b)
{
    
    
	int c = a + b;
	return c;
}
int main()
{
    
    
	int& ret = Func(1, 2);
	cout << ret << endl;

	Func(3, 4);
	cout << ret << endl;

	return 0;
}

Running result:
Insert image description here
Analysis:
Insert image description here
At this time, ret is also an alias, an alias for the return value. The difference from the previous one is that ret It does not occupy separate memory space. Directly speaking, ret is an alias of c. As mentioned earlier, the alias of c is passed as an uncertain value, depending on whether the compiler has cleaned it up. So the first call is 3 under VS, and the second call is 7

The third way of writing: add static modification
The first two ways of writing are wrong, because the results implemented under different compilers are different. So you have to use static here, which can lengthen the life cycle of the modified variable.

int& Func(int a, int b)
{
    
    
	static int c = a + b;
	return c;
}
int main()
{
    
    
	int& ret = Func(1, 2);
	cout << ret << endl;

	Func(3, 4);
	cout << ret << endl;

	return 0;
}

Run result:
Insert image description here
Why are both 3? Because the static variable modified by static can only be initialized once, that is to say, the first time the function is called, it is initialized to 3 (a+b=3). When the function is called the second time, its initial value is 3. If it is called again, static int c = a + b is equivalent to the second initialization, which is not acceptable.

Adjust it a bit:

int& Func(int a, int b)
{
    
    
	static int c;
	c = a + b;
	return c;
}
int main()
{
    
    
	int& ret = Func(1, 2);
	cout << ret << endl;

	Func(3, 4);
	cout << ret << endl;

	return 0;
}

Run result:
Insert image description here
After initialization, c = a+ b can modify C

6.4.3 Frequent references

const is a modified constant variable. What will happen if the referenced object is preceded by const modification or if the reference is preceded by const?

int main()
{
    
    
	const int a = 1;
	int& b = a;
	return 0;
}

Insert image description here
Const variable a means that variable a has become a constant variable and cannot be changed. And b is an alias of a. If you change b, it is equivalent to changing a, which amplifies the authority.

Permissions cannot be enlarged, but can be reduced or panned
Permissions can be reduced:

int main()
{
    
    
	int a = 1;
	const int& b = a;
	return 0;
}

Note: b cannot be changed at this time.

Permission translation: add const to both

int main()
{
    
    
	const int a = 1;
	const int& b = a;
	return 0;
}

6.4.4 Constant references and type conversions

We know that the references must be of the same type. If they are different, the compiler will report an error.

int main()
{
    
    
	int a = 3;
	double& b = a;
	return 0;
}

Insert image description here
This error prompts that when type conversion occurs, the referenced object must be a constant.

Adding const will solve the problem

	const double& b = a;

Because whenever type conversion occurs, a temporary variable will be generated. This temporary variable is the result of a to be converted. a itself is unchanged, and then the temporary variable is assigned to b. Why can just add const? Because the generated temporary variable has constant attributes, const must be added.

6.5 References and pointers

6.5.1 Similarities and differences between references and pointers

Referenced ingrammatical concept is an alias. It has no independent space and shares the same space with its referenced entity. There is actually space in the underlying implementation because references are implemented in the form of pointers.
Look at the following assembly code:
Insert image description here

6.5.2 Differences

  1. A reference is an alias for a variable, and a pointer stores the address of a variable.
  2. References must be initialized, pointers do not need to be used
  3. A reference cannot change the object it points to, a pointer can
  4. There are multi-level pointers, no multi-level references
  5. There are no NULL references, there are NULL pointers
  6. The meaning in sizeof is different: the reference result is the size of the reference type, but the pointer is always the number of bytes occupied by the address space (4 bytes on a 32-bit platform)
  7. Reference self-increment means that the referenced entity is increased by 1, and pointer self-increment means that the pointer is offset backward by the size of the type.
  8. The way to access entities is different. The pointer needs to be explicitly dereferenced, and the reference compiler handles it by itself.
  9. References are safer to use than pointers

7. Inline functions

7.1 What is an inline function?

Functions modified with the keyword inline are called inline functions. During compilation, they will be expanded at the place where the inline function is called. There is no need to create a stack frame, which improves program running efficiency.

// inline 关键字
inline int Add(int a, int b)
{
    
    
	int c = a + b;
	return c;
}
int main()
{
    
    
	int ret = Add(3, 5);
	cout << ret << endl;
	return 0;
}

7.2 Why there are inline functions?

Ordinary functions will create the overhead of stack frames. If you don't use a function, although you don't need to create a stack frame if you change it to a macro, the shortcomings of macros are also obvious.

Disadvantages of macros:
Lots of syntax details, prone to errors
Cannot be debugged
No safe type checking< /span>

Therefore, C++ introduces the concept of inlining, which can improve the efficiency of the program.

7.3 Characteristics of inline functions

7.3.1 Improve program operation efficiency

If the compiler treats a function as an inline function, the function call will be replaced with the function body during the compilation phase. It reduces the calling overhead and improves efficiency; but the disadvantage is that the target file may become larger.

7.3.2inline is just a suggestion

When we use inline, we just send such a request to the compiler. Different compilers have different processing results, which means that whether the function will be converted into an inline function depends on the compiler. It is generally recommended that if the function is small in size, not recursive and called frequently, you can use inline modification.
Insert image description here

7.3.3 Inline declaration and call cannot be separated

Detachment can cause linking errors. Because inline is expanded, there is no function address and the link will not be found.

#include <iostream>
using namespace std;
inline int Add(int a, int b);
//
#include "List.h"
int Add(int a, int b)
{
    
    
	int c = a + b;
	return c;
}
/
int main()
{
    
    
	int ret = Add(3, 5);
	cout << ret << endl;
	return 0;
}

You can see the following error message:
Insert image description here

8.auto keyword

8.1Introduction to auto

To put it simply, auto is used fortype deduction, and it can also be called an alias for a type.

int a = 10;
auto b = a;

The type derived by auto is int

8.2 Why is auto

When programming, when we return the return value of a function, we need to know its return type. But what if the return type of a function is very long and it is very cumbersome to write, so auto can be used instead of the long return type.

vector<string> v;
vector<string>::iterator it = v.begin();//写法1
auto it = v.begin();//写法2

8.3 Rules for using auto

1.auto is used in combination with pointers and references
When using auto to declare pointer types, there is no difference between using auto and auto*, but when using auto to declare reference types, you must add &

    int x = 10;
    auto a = &x;
    auto* b = &x;
    auto& c = x;

2. Define multiple variables on the same line
When multiple variables are declared on the same line, these variables must be of the same type, otherwise the compiler will report an error because the compiler The compiler actually only deduces the first type, and then uses the deduced type to define other variables.

auto a = 1, b = 2; 
auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同

Note: auto cannot be used as a parameter of a function; it cannot be used directly to declare an array

9. Pointer null value nullptr (C++11)

In C language, we have learned that NULL is a null pointer. NULL is actually a macro, see the following code:

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

When programming, if a pointer is not given a specific pointer, then NULL is used as the initialization value. However, NULL may be defined as a literal constant 0, or as a constant of an untyped pointer (void*). No matter which definition is adopted, you will inevitably encounter some troubles when using a null-valued pointer, such as :

void f(int)
{
    
    
 cout<<"f(int)"<<endl;
}
void f(int*)
{
    
    
 cout<<"f(int*)"<<endl;
}
int main()
{
    
    
 f(0);
 f(NULL);
 f((int*)NULL);
 return 0;
}

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 to (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.

Guess you like

Origin blog.csdn.net/2301_77459845/article/details/133905194