Introduction to C++ Basics (Part 2): auto, range for loop, nullptr keyword

Table of contents

1. The auto keyword (C++11)

1.1 Introduction

1.2 Introduction

1.3 Rules of Use

1.3.1 Auto combined with pointers and references

1.3.2 Define multiple variables on the same line

1.3.3 Other applications

1.3.4 C++11 auto

1.4 Scenarios where auto cannot be derived

1.4.1 auto cannot be used as a function parameter

1.4.2. auto cannot be used directly to declare an array

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

2.1 Basic syntax

2.2 Conditions of use

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

3.1 Pointer null in C++98

3.2 Precautions


1. The auto keyword (C++11)

1.1 Introduction

With the deepening of learning, the program and the types used become more and more complex, for example: the type is difficult to spell / the meaning is unclear, which makes it easy to make mistakes.

eg:

#include <string>
#include <map>

int main() {
    std::map<std::string, std::string> m{
   
   {"apple", "苹果"},
                                         {"orange","橙子"},
                                         {"pear",  "梨"}};
    std::map<std::string, std::string>::iterator it = m.begin();
    while (it != m.end()) {
        //....
    }
    return 0;
}

std::map<std::string, std::string>::iterator is a type, but the type is too long, it is easy to write wrong, and it is relatively troublesome to use.

1.2 Introduction

The meaning of auto in early C/C++ 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?

In C++11, the standard committee gave auto a brand-new meaning: auto is no longer a storage type indicator, but a new type indicator to instruct the compiler, and the variables declared by auto must be compiled by the compiler derived from time period.

#include <iostream>
#include <typeinfo>
using namespace std;

int TestAuto()
{
    return 10;
}

int main() {
    int a = 10;
    auto b = a;
    auto c = 'a';
    auto d = TestAuto();
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    cout << typeid(d).name() << endl;
    //auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
    return 0;
}

In C++, typeidis an operator used to obtain the type information of an expression. When you call it , it returns a string representing the type of the expression. This string is usually compiler specific and may not be human readable, but can be used for debugging or other purposes.typeid(d).name()d

To use typeid, you need to include the header file <typeinfo>.

Reminder: When using auto to define a variable, it must be initialized. At the compilation stage, 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.

1.3 Rules of Use

1.3.1 Auto combined with pointers and references

When using auto to declare a pointer type, there is no difference between using auto and auto* , but when using auto to declare a reference type, you must add &

int main() {
    int x = 10;
    auto a = &x;
    auto *b = &x;
    auto &c = x;
    cout << typeid(a).name() << endl;
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    *a = 20;
    *b = 30;
    c = 40;
    return 0;
}

Here it will be deduced that both a and b are of type int* , and c is of type int

1.3.2 Define multiple variables on the same line

When declaring multiple variables on the same line, the 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 uses the deduced type to define other variables.

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

1.3.3 Other applications

The most common advantage of auto in practice is to use it in conjunction with the new for loop provided by C++11, which will be mentioned later, and lambda expressions.

1.3.4 C++11 auto

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

1.4 Scenarios where auto cannot be derived

1.4.1 auto cannot be used as a function parameter

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}

The C++ standard stipulates that the formal parameter types of functions must be explicitly specified types, rather than using type deduction. Therefore, using code like this autoas a function parameter type will fail to compile.

1.4.2. auto cannot be used directly to declare an array

void TestAuto()
{
    int a[] = {1,2,3};
    auto b[] = {4,5,6};
}

In C++, autowhen using to declare a variable, the compiler will deduce the type of the variable based on the type of the initialization expression.

However, auto b[] = {4, 5, 6};such syntax is not allowed.

For this case, the compiler cannot deduce bthe type of the array because the initializer {4, 5, 6}is an initializer list, not a concrete array type. When used auto, the compiler needs to know the type of the variable explicitly in order to correctly allocate memory and perform type checking.

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

C++11 introduces a new for loop syntax called Range-based for loop. It allows you to iterate over the elements in the container more conveniently, without using iterators or indexes. This loop works for all containers that support iterators, such as arrays, vectors, lists, sets, etc.

2.1 Basic syntax

for (element_type variable : container) {
    // 循环体,使用 variable 访问容器中的元素
}

where element_typeis the type of element in the container, variableis the variable name you use in the loop, and containeris the container to traverse.

As an example, we use a range-based for loop to iterate over a vector:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // 使用基于范围的for循环遍历向量
    for (int num : numbers) {
        std::cout << num << " ";
    }

    return 0;
}

In this example, we declare a numbersvector named , then use a range-based for loop to iterate over the elements in that vector, and print each element to standard output.

The range-based for loop has great advantages in terms of code readability and conciseness, especially for traversing all elements in the container. However, if you need to access an index or iterator within the body of the loop, you still need to use a traditional for loop.

See here because the reader may not have touched the container, the author uses the array example again:

#include <iostream>

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

    // 使用基于范围的for循环遍历数组
    for (int num : arr) {
        std::cout << num << " ";
    }

    return 0;
}

The output will be:1 2 3 4 5

In the above example, we declared an arrarray named and used a range-based for loop to iterate over the elements in that array and printed each element to standard output.

It should be noted that when using a range-based for loop to traverse an array, the type of the loop variable must match the type of the array element , otherwise a compile error will result.

2.2 Conditions of use

The range of for loop iterations must be deterministic

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.

Example:

void TestFor(int array[])
{
    for(auto& e : array)
        cout<< e <<endl;
}

 There is a problem with the above code, because after the array is passed in, the array is no longer an array, but a pointer, which makes the range of for uncertain.

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

3.1 Pointer null in C++98

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

void TestPtr()
{
    int* p1 = NULL;
    int* p2 = 0;
// ……
}

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

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

As you can see, NULL may be defined as the literal constant 0, or as a constant of an untyped pointer (void*). No matter what kind of definition is adopted, some troubles will inevitably be encountered when using pointers of null values, 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 since NULL is defined as 0, it is contrary to 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. To use it in pointer mode, it must be cast to (void *)0 .

3.2 Precautions

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

So far, the introduction to C++ is over! !

Guess you like

Origin blog.csdn.net/weixin_57082854/article/details/132089384