Introduction to C++ (Part 2)

Table of contents

1. Inline functions

1.1 The concept of inline function

1.2 Use of inline functions

 1.3 Characteristics of inline functions

1.4 Advantages and disadvantages of macros

1.5 Technology that can replace macros in C++

Two, the auto keyword

2.1 What is the auto keyword

2.2 Introduction to auto

2.3 Auto usage rules

2.4 Scenarios that cannot be derived by auto

3. Range-based for loop (C++11)

3.1 Basic syntax of range for

3.2 Conditions of use of scope for

Fourth, the pointer null value nullptr (C++11)

4.1. Null pointers in C++98

4.2 Precautions


1. Inline functions

1.1 The concept of inline function

Inline function is a function added in C++, which can improve the efficiency of program execution. If the function is inline, the compiler will expand the inline function at the function call when compiling, so that there will be no additional overhead for the establishment of function stack frames, and the efficiency of code operation will be improved.

Inline functions can be compared to macro functions, but macro functions do not perform type checking.

#define  Add(x,y) ((x)+(y)) 

The Add function defined by the macro, we need to pay attention to

① Did you add the type in front of x and y?

②Are parentheses added to x and y?

#define Add(x,y) (x+y)

int main()
{
	int x = 1;
	int ret = Add(x | 10, x & 11);
	cout << ret << endl;
	return 0;
}

3e4c9c334279404db4e56a31db9991c7.png

It can be found that the calculated results do not match the expected results. Why is this happening? Because the macro function directly adds Add(x,y) at the calling place

Replaced with (x+y) --> (1|10+1&11), the priority of + is higher than | and & , so it becomes (1|11&11) in the end

So the result is not as expected

The following are parenthesized

#define Add(x,y) ((x)+(y))

int main()
{
	int x = 1;
	int ret = Add(x | 10, x & 11);
	cout << ret << endl;
	return 0;
}

f7b7462437924e87ba50b134a20ecb72.png

③Have you added brackets as a whole?

#define Add(x,y) (x)+(y)

int main()
{
	int x = 1,y = 2;
	int ret = Add(x+y, x +y)*10;
	cout << ret << endl;
	return 0;
}

8957f0c90d0440dfae44c344c9168514.png

 It can be found that the overall result without brackets does not match the expected result

The following are parenthesized

#define Add(x,y) ((x)+(y))

int main()
{
	int x = 1,y = 2;
	int ret = Add(x+y, x +y)*10;
	cout << ret << endl;
	return 0;
}

69c00d1647ee484ab66743f4b3837f6a.png

The result matches the expected result

According to the above macro function, we can find that a simple function requires us to pay a lot of attention. If we are not careful, there will be bugs. Obviously this is not the result we want. At the same time, macros do not support debugging, so C++ bosses The problem was discovered, so the inline function was introduced.

1.2 Use of inline functions

You only need to add a keyword inline at the front of the function, such as:

inline int Add(int x,int y)
{
	return x + y;
}

The above function uses inline, and the compiler will replace the function body with the function call during compilation, which can avoid the time spent on creating unnecessary function stack frames

cb9a45c78c9b43379f14ba18b8895d1f.png

 From the above figure, we can find that the function has not been expanded. This is because we are in debug mode at this time. In debug mode, in order to facilitate our debugging, it is not expanded by default. If it is in Release mode, the function will be expanded.

Ways to observe inline function expansion in Debug mode

6774a7f90e0f4ab683acb063eda7b73d.png

 Now you can observe the inline function expanded at the function call

7ebe6e752ce2406183abb46be78e307a.png

 1.3 Characteristics of inline functions

1. inline is a method of exchanging space for time . If the compiler treats the function as an inline function, it will replace the function call with the function body during the compilation phase. Defect: it may make the object file larger. Advantage: less Call overhead, improve program operating efficiency.

2. Inline is only a suggestion for the compiler . Different compilers may have different implementation mechanisms for inline. The general suggestion is: make the function smaller (that is, the function is not very long, there is no exact statement, it depends on the internal implementation of the compiler) , Functions that are not recursive and frequently called should be inline modified, otherwise the compiler will ignore the inline feature.

3. Inline does not recommend separation of declaration and definition , separation will lead to link errors. Because inline is expanded, there will be no function address, and the link will not be
found

1.4 Advantages and disadvantages of macros

(1) Advantages

① Improve code reusability

② Improve performance

(2) Disadvantages

①It is inconvenient to debug (because the preprocessing stage is replaced)

② Code readability is poor, maintainability is poor, and it is easy to misuse

③ There is no type safety check

1.5 Technology that can replace macros in C++

1. Constant definition, use const enum instead

2. Short function definition, use inline function instead

Two, the auto keyword

2.1 What is the auto keyword

The meaning of the auto keyword: thinking about type aliases

As the program becomes more and more complex, the types used in the program become more and more complex, as reflected in:

① type is difficult to spell

②The meaning of the type is not clear, which is easy to cause errors

as follows:

#include<iostream>
#include<string>
#include<vector>
int main()
{
	std::vector < std::string > str;
	std::vector < std::string > ::iterator it = str.begin();
	return 0;
}

std::vector < std::string > ::iterator is a type. When you see this, you may think that it is not good to use typedef

#include<iostream>
#include<string>
#include<vector>
typedef std::vector < std::string > ::iterator Vi;
int main()
{
	std::vector < std::string > str;
	Vi it = str.begin();
	return 0;
}

Although this can also be achieved, the simplification of the code, but when the typedef is used frequently, we may forget the type of our own typedef

So at this time we can use auto to directly deduce the type of it, which is convenient for viewing and writing, and it is not easy to make mistakes

#include<iostream>
#include<string>
#include<vector>
int main()
{
	std::vector < std::string > str;
	auto it = str.begin();
	return 0;
}

2.2 Introduction to auto

①In the early C/C++, the meaning of auto is: the variable modified with auto is a local variable with automatic memory, but unfortunately no one has been
using it. You can think about why?

Because the compiler defines local variables in the function by default, and global variables defined outside the function, generally do not need to be written, so in
C++11, the standard committee gave auto a new meaning: auto is no longer a storage type indicator, but as a new type indicator to indicate to the compiler that the variable declared by auto must be deduced by the compiler at compile time.
 

#include<iostream>
using namespace std;
int Add(int x, int y)
{
	return 1 + 2;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = 1.000;
	auto a1 = Add(1,2);
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	cout << typeid(a1).name() << endl;
	return 0;
}

typeid().name() is used to deduce the variable type , the result is as follows:

fdbf4f860b4740cdb31e5fb865298768.png

When using auto to define a variable, it must be initialized. In the compilation phase, the compiler needs to deduce the actual type of auto according to the initialization expression. Therefore, auto is not a "type" declaration, but a "placeholder" when the type is declared. The compiler will replace auto with the actual type of the variable during compilation.

int main()
{
	auto a;//会报错,因为没有推导的类型
	return 0;
}

2.3 Auto usage rules

1. Auto and pointers, references are used in combination

①Used in combination with pointers

#include<iostream>
using namespace std;

int main()
{  
	int a = 20;
	auto pa=&a;
	auto* pa1 = &a;
	cout << pa << ' ' << pa1 << endl;
	return 0;
}

4bd1b57d57b04599aafb34710745b3ee.png

At this point, it can be found that there is no difference between auto and auto*

② Auto and reference are used in combination

#include<iostream>
using namespace std;

int main()
{  
	int a = 20;
	auto a1 = a;//表示推导类型为int,不是引用
	auto& a2 = a;//表示推导类型为int&,是引用
	a1 += 1;
	cout << a << endl;
	a2 += 1;
	cout << a << endl;
	return 0;
}

 577d6ea7b0c54ca8847c10be8993a185.png

 It can be found that if you do not add a reference, then it is simply derivation and assignment, and adding the & symbol is a reference, so if you want to quote, you cannot remove &

2. Define multiple variables on the same line

When declaring multiple variables on the same line, these variables must be of the same type, otherwise the compiler will report an error, because the compiler
actually only deduces the first type, and then defines other variables with the deduced type.

#include<iostream>
using namespace std;

int main()
{  
	auto a = 1, b = 2;
	auto c = 'a', d = &a;//该行会报错,因为类型不同
	return 0;
}

2.4 Scenarios that cannot be derived by auto

1.auto cannot be used as a function parameter

int Add(auto x,auto,y)
{
	return x + y;
}

76e2522d1bcb463fa671c3c81ca6f18a.png

2.auto cannot be used to push to an array

int main()
{  
	int a[] = { 1,3,4,5 };
	auto arr[] = { 1,2,3,4,5 };
	return 0;
}

b0c15ccda3884eeb9af47fad174f95cb.png

Note: In order to avoid confusion with auto in C++98, C++11 only retains the usage of auto as a type indicator

The most common advantage of auto in practice is the new for loop provided by C++11 , and the lambda expression is used in conjunction with it.


3. Range-based for loop (C++11)

3.1 Basic syntax of range for

In C++98, if you want to traverse an array, you can use the traditional way of writing

#include<iostream>
using namespace std;

int main()
{  
	int a[] = { 1,2,3,4,5 };
	for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
		cout << a[i] << ' ';
	cout << endl;
	return 0;
}

For ranged collections , it is redundant and sometimes error-prone for the programmer to specify the range of the loop. Therefore, C++11 introduces a range-based for loop. 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

#include<iostream>
using namespace std;

int main()
{  
	int a[] = { 1,2,3,4,5 };
	for (auto i : a)
		cout << i << ' ';
	cout << endl;
	return 0;
}

Note: Similar to normal loops, you can use continue to skip this loop, and break to end the entire loop

3.2 Conditions of use of scope for

1. The range of for loop iterations must be definite

For arrays, it is the range of the elements of the first array and the elements of the last array. For classes, methods of begin and end should be provided, and begin and end are the range of for loop iterations

#include<iostream>
using namespace std;

void print(int *a)
{
	for (auto i : a)
		cout << i << ' ';
	cout << endl;
}
int main()
{  
	int a[] = {1,2,3,4,5};
	print(a);
	return 0;
}

The above code is wrong because the scope of for is ambiguous

2. The iterator object must implement the operations of ++ and -- (the relevant content of the iterator will be updated later)

Fourth, the pointer null value nullptr (C++11)

4.1. Null pointers in C++98


In good C/C++ programming practice, when declaring a variable, it is best to give the variable an appropriate initial value, otherwise unexpected errors may occur , such as uninitialized pointers. If a pointer has no valid pointing to, we basically
initialize it as follows:
 

#include<iostream>
using namespace std;

int main()
{  
	int* p = NULL;
	int* pa = 0;
	return 0;
}

NULL is actually a macro. You can see the following code in the traditional C header file (stddef.h):

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

It can be seen that NULL may be defined as a literal constant 0, or it may be defined as a constant of an untyped pointer (void*). No matter what definition is used, some troubles are inevitable when using a pointer to a null value. like:

#include<iostream>
using namespace std;


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;
}

c7371be3acef443ca7fefb2296fca779.png

 It can be found that the second call is intended to call the f(int*) function, but NULL is defined as 0, and the first function f(int) is called at this time, so it is contrary to the original intention of the program.

4.2 Precautions

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. To use it in pointer mode, it must be forced to (void*) 0
Note:

1. When using nullptr to represent the null value of the pointer, there is no need to include the header file, because nullptr was introduced as a keyword in C++11.

2. In C++, sizeof(nullptr) and sizeof((void*)0) occupy the same number of bytes

3. In order to improve the robustness of the code, it is recommended to use nullptr when writing the code to represent the null value of the pointer

That’s all for sharing, thank you for your support, please point out any mistakes, 886!

Guess you like

Origin blog.csdn.net/m0_72532428/article/details/130461679
Recommended