The most complete and detailed explanation of C++ entry for C++ elementary

Please add a picture description

1. C++ keywords (C++98)

C++ has a total of 63 keywords, and C language has 32 keywords
insert image description here

2. Namespace

In C/C++, there are a large number of variables, functions, and classes to be learned later. The names of these variables, functions, and classes will all exist in the global scope, which may cause many conflicts
. The purpose of using the namespace is to localize the name of the identifier
to avoid naming conflicts or name pollution. The appearance of the namespace keyword is aimed at this problem. Let's look at the following code:

#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
    
    
	printf("%d\n", rand);
	return 0;
}

We have learned in C language that because rand is a function, rand cannot be used as a variable name. Therefore, the following error will appear when compiling this code.
insert image description here
C language cannot solve naming conflicts like this, so C++ proposes namespace to solve it.

2.1 Namespace definition

To define a namespace, you need to use the namespace keyword, followed by the name of the namespace, and then a pair of {}, which
are members of the namespace.
For example:

namespace xiaoxie
{
    
    
 	int rand = 10;
 	int Add(int left, int right)
 	{
    
    
 		return left + right;
 	}
 	struct Node
 	{
    
    
 	struct Node* next;
 	int val;
 	};
}

xiaoxie is the name of the namespace. In general development, the project name is used as the namespace name.
Variables/functions/types can be defined in a namespace.
Namespaces can also be nested, as follows:

//test.cpp
namespace N1
{
    
    
	int a;
	int b;
	int Add(int left, int right)
 	{
    
    
     	return left + right;
 	}
	namespace N2
 	{
    
    
     	int c;
     	int d;
     	int Sub(int left, int right)
     	{
    
    
         	return left - right;
     	}
 	}
}

Multiple namespaces with the same name are allowed in the same project, and the compiler will finally combine them into the same namespace.
The test.h in a project and the two N1s in the above test.cpp will be merged into one
. For example:

// test.h
namespace N1
{
    
    
	int Mul(int left, int right)
 	{
    
    
     	return left * right;
 	}
}

Note: A namespace defines a new scope, and all content in the namespace is limited to the namespace

2.2 Namespace usage

How to use the members in the namespace? for example:

namespace xiaoxie
{
    
    
 	int a = 0;
 	int b = 1;
 	int Add(int left, int right)
 	{
    
    
 		return left + right;
 	}
 	struct Node
 	{
    
    
 		struct Node* next;
 		int val;
 	};
}
int main()
{
    
    
 	printf("%d\n", a);
	return 0;
}

In C language, we call variables/functions/types directly, but when calling variables/functions/types in C++ namespace, there are three ways:

1. Add namespace name and scope qualifier

int main()
{
    
    
    printf("%d\n", xiaoxie::a);
    return 0;    
}

This method needs to be expanded separately every time a variable/function/type in the namespace is called, which is the most cumbersome and the safest method.

2. Use using to introduce a member in the namespace

using xiaoxie::b;
int main()
{
    
    
    printf("%d\n", xiaoxie::a);
    printf("%d\n", b);
    return 0;    
}

This method is to expand a certain variable/function/type in the namespace, and the variable/function/type can be used directly after subsequent calls without expanding again. This method is recommended.

3. Use the using namespace namespace name to import

using namespce xiaoxie;
int main()
{
    
    
    printf("%d\n", xiaoxie::a);
    printf("%d\n", b);
    Add(10, 20);
    return 0;    
}

This method expands all the variables/functions/types in the namespace, and can be called directly when using the variables/functions/types in the namespace later, but generally in the project, this way of writing is not recommended, because this not safe.

3. C++ input & output

Let's first look at how C++ outputs hello world

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

insert image description here

std is the namespace name of the C++ standard library. C++ puts the definition and implementation of the standard library in this namespace
to explain:

  1. When using the cout standard output object (console) and the cin standard input object (keyboard), you must include the <iostream> header file and use std by namespace usage.
  2. cout and cin are global stream objects, and endl is a special C++ symbol that represents newline output . They are all included in the <iostream> header file.
  3. << is the stream insertion operator and >> is the stream extraction operator .
  4. It is more convenient to use C++ input and output, and does not need to manually control the format like printf/scanf input and output. The input and output of C++ can automatically identify the variable type .
  5. In fact, cout and cin are objects of type ostream and istream respectively. >> and << also involve operator overloading and other knowledge. Later, we will learn more about the usage and principle of IO stream.
    Note: In the early standard library, all functions were implemented in the global domain, declared in the header file with the suffix of . , and in order to use the namespace correctly, it is stipulated that the C++ header file does not contain .h; the old compiler (vc 6.0) also supports the <iostream.h> format, which is not supported by subsequent compilers, so it is recommended to use + std .
#include <iostream>
using namespace std;
int main()
{
    
    
	int a;
	double b;
	char c;

	cin >> a;
	cin >> b >> c;

	cout << a << endl;
	cout << b << " " << c << endl;
	return 0;
}

insert image description here
It can be seen that cin and cout in C++ can automatically identify the type of variable. There are many more complex usages of cout and cin, such as controlling the output precision of floating-point numbers, controlling the binary format of integer output, and so on.
Usage conventions of the std namespace:
std is the namespace of the C++ standard library. How to expand the use of std is more reasonable?

  1. In daily practice, it is recommended to directly use namespace std, which is very convenient.
  2. When using namespace std is expanded, the standard library is fully exposed. If we define a type/object/function with the same name as the library, there will be conflicts. This problem rarely occurs in daily practice, but it is easy to occur when there are many codes and large scale in project development. Therefore, it is recommended to use it in project development. When using it like std::cout, specify the namespace + using std::cout to expand common library objects/types, etc.

4. Default parameters

4.1 Default parameter concept

The default parameter is to specify a default value for the parameter of the function when declaring or defining the function . When calling the function, if no actual parameter is specified, the default value of the formal parameter is adopted, otherwise the specified actual parameter is used. For example:

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

insert image description here
When no parameter is passed, the default value of the parameter is used, and when the parameter is passed, the specified actual parameter is used.

4.2 Classification of default parameters

All default parameters

void Func(int a = 10, int b = 20, int c = 30)
 {
    
    
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

semi-default parameter

void Func(int a, int b = 10, int c = 20)
 {
    
    
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

have to be aware of is:

  1. The semi-default parameters must be given sequentially from right to left, and cannot be given alternately.
  2. Default parameters cannot appear in function declaration and definition at the same time. If life and definition appear at the same time, and the values ​​provided by the two places happen to be different, then the compiler cannot determine which default value should be used, and it is usually placed in the declaration
  3. The default value must be a constant or a global variable
  4. C language does not support (compiler does not support)

5. Function overloading

In natural language, a word can have multiple meanings, and people can judge the true meaning of the word through the context, that is, the word is overloaded.

5.1 Concept of function overloading

Function overloading : It is a special case of functions. C++ allows several functions with the same name to declare similar functions in the same scope. These functions with the same name have different formal parameter lists (parameter number or type or type order), which are often used to deal with Implementing functions is similar to the problem of different data types. The input and output streams cin and cout we mentioned earlier also realize the self-defined identification type in this way.

5.1.1 Different parameter types

int Add(int left, int right)
{
    
    
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
double Add(double left, double right)
{
    
    
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}
int main()
{
    
    
	cout << Add(10, 20) << endl;
	cout << Add(10.1, 20.2) << endl;
	return 0;
}

insert image description here

5.1.2 The number of parameters is different

void f()
{
    
    
	cout << "f()" << endl;
}
void f(int a)
{
    
    
	cout << "f(int a)" << endl;
}
int main()
{
    
    
	f();
	f(10);
	return 0;
}

insert image description here

5.1.3 Parameter type order is different

void f(int a, char b)
{
    
    
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
    
    
	cout << "f(char b, int a)" << endl;
}
int main()
{
    
    
	f(10, 'a');
	f('a', 10);
	return 0;
}

insert image description here

5.2 Principle of C++ Supporting Function Overloading – Name Mangling

Why does C++ support function overloading, but C language does not support function overloading?
In C/C++, for a program to run, it needs to go through the following stages: preprocessing, compiling, assembling, and linking .
The following is an overview of GCC’s extended compilation process.
insert image description here
The first step is the pre-compilation stage (.i) to compile preprocessing instructions, the second step is compilation (.s) for syntax analysis, lexical analysis, semantic analysis, symbol summary, and the third step is assembly (.o ) to form a symbol table, which is converted from assembly instructions to binary instructions, and finally the link is completed by the linker.

The actual project is usually composed of multiple header files and multiple source files. In the final linking stage, which name will the linker use to find it? Each compiler has its own function name modification rules. Due to the The modification rules of vs are too complicated, while the modification rules of g++ under Linux are simple and easy to understand. Below we use g++ to demonstrate the modified name. Let's first look at the following c program

#include <stdio.h>
int add(int a, int b)
{
    
    
	return a + b;
}
int main()
{
    
    
	int a = 1;
	int b = 2;
	add(a, b);
	return 0;
}

insert image description here

During the assembly process of the C program, the function call name has not been changed, let's look at the C++ program

#include<iostream>
using namespace std;
int Add(int left, int right)
{
    
    
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
double Add(double left, double right)
{
    
    
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}
int main()
{
    
    
	cout << Add(10, 20) << endl;
	cout << Add(10.1, 20.2) << endl;
	return 0;
}

insert image description here
insert image description here
insert image description here
Conclusion : Under Linux, after compiling with g++, the modification of the function name changes, and the compiler adds the function parameter type information to the modified name. Through this, I understand that the C language cannot support overloading, because there is no way to distinguish functions with the same name. C++ is distinguished by function modification rules. As long as the parameters are different, the modified names are different, and overloading is supported. If the function names and parameters of two functions are the same, but the return values ​​are different, it does not constitute overloading, because the compiler has no way to distinguish when calling.

6. Citation

6.1 Reference concepts

A reference is not a new definition of a variable, but an alias for an existing variable. The compiler will not open up memory space for the reference variable
, and it shares the same memory space with the variable it refers to.

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

6.2 Citation Properties

  1. References must be initialized when they are defined
  2. A variable can have multiple references
  3. Once a reference refers to an entity, it cannot refer to another entity

For example, if the reference in the comment part of the following code is not initialized, an error will occur during compilation, and the following two references are the correct methods to use

#include<iostream>
using namespace std;
void TestRef()
{
    
    
	int a = 10;
	// int& ra;   
	int& ra = a;
	int& rra = a;
	printf("%p %p %p\n", &a, &ra, &rra);
}
int main()
{
    
    
	TestRef();
	return 0;
}

insert image description here

6.3 Common references

#include<iostream>
using namespace std;
void TestConstRef()
{
    
    
	const int a = 10;
	//int& ra = a;   // 该语句编译时会出错,a为常量
	const int& ra = a;
	// int& b = 10; // 该语句编译时会出错,10为常量
	const int& b = 10;
	double d = 12.34;
	//int& rd = d; // 该语句编译时会出错,类型不同
	const int& rd = d;
	cout << a << " " << ra << " " << b << " " << d << " " << rd << endl;
	printf("%p\n%p\n%p\n%p\n%p\n", &a, &ra, &b, &d, &rd);
}
int main()
{
    
    
	TestConstRef();
	return 0;
}

insert image description here

6.4 Usage Scenarios

1. Make parameters

#include<iostream>
using namespace std;
void Swap(int& left, int& right)
{
    
    
	int temp = left;
	left = right;
	right = temp;
}
int main()
{
    
    
	int a = 1;
	int b = 2;
	cout << a << " " << b << endl;
	Swap(a, b);
	cout << a << " " << b << endl;
	return 0;
}

insert image description here
2. Make the return value
First of all, we need to be clear that the essence of return by value is to generate a copy of the returned object as the return value of the function call.
Let’s look at the return by reference, first look at the following code

#include<iostream>
using namespace std;
int& Count()
{
    
    
	int n = 0;
	n++;
	return n;
}
int main()
{
    
    
	int& ret = Count();
	printf("%d\n", ret);
	printf("%d\n", ret);
	return 0;
}

insert image description here
The result of ret here is undefined. If the stack frame ends, the system will clear the stack frame and set it to a random value, so the result of the second pass is a random value. Although the result of the first pass is 1, it is returned by reference The essence is wrong, and the result is not guaranteed, so we come to a conclusion: when the function scope is exceeded, the returned object will be destroyed, so it must not be returned by reference, and must be returned by value.
If it is changed to the following scenario, you can use the reference to return

#include<iostream>
using namespace std;
int& Count()
{
    
    
	static int n = 0;
	n++;
	return n;
}
int main()
{
    
    
	int& ret = Count();
	printf("%d\n", ret);
	printf("%d\n", ret);
	cout << ret << endl;
	cout << ret << endl;
	return 0;
}

insert image description here
The following is a common use of references, such as stack structures, which can also be used. Here I use a simple sequence table to demonstrate, the code is as follows:

#include<iostream>
using namespace std;
typedef struct list
{
    
    
	int arr[10];
	int size;
}L;
int& ret(L& a, int b)
{
    
    
	return a.arr[b];
}
int main()
{
    
    
	L a;
	a.arr[0] = 0;
	ret(a, 0)++;
	cout << a.arr[0] << endl;
	ret(a, 0) = 10;
	cout << a.arr[0] << endl;
	return 0;
}

insert image description here

6.5 Comparison of pass-by-value and pass-by-reference efficiency

Values ​​are used as parameters or return value types. During parameter passing and return, the function does not directly pass actual parameters or return the variable itself directly, but passes actual parameters or returns a temporary copy of the variable, so values ​​are used as parameters Or the return value type is very inefficient, especially when the parameter or return value type is very large, the efficiency is even lower.

#include<iostream>
#include <time.h>
using namespace std;
struct A {
    
     int a[10000]; };
void TestFunc1(A a){
    
    }
void TestFunc2(A& a){
    
    }
void TestRefAndValue()
{
    
    
 	A a;
 	// 以值作为函数参数
 	size_t begin1 = clock();
 	for (size_t i = 0; i < 10000; ++i)
 		TestFunc1(a);
 	size_t end1 = clock();
 	// 以引用作为函数参数
 	size_t begin2 = clock();
 	for (size_t i = 0; i < 10000; ++i)
 		TestFunc2(a);
 	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
 	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
 	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
int main()
{
    
    
	TestRefAndValue();
	return 0;
}

insert image description here
Performance comparison of value and reference as return type

#include<iostream>
#include <time.h>
using namespace std;
struct A {
    
     int a[10000]; };
A a;
// 值返回
A TestFunc1() {
    
     return a; }
// 引用返回
A& TestFunc2() {
    
     return a; }
void TestReturnByRefOrValue()
{
    
    
	// 以值作为函数的返回值类型
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc1();
	size_t end1 = clock();
	// 以引用作为函数的返回值类型
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc2();
	size_t end2 = clock();
	// 计算两个函数运算完成之后的时间
	cout << "TestFunc1 time:" << end1 - begin1 << endl;
	cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
int main()
{
    
    
	TestReturnByRefOrValue();
	return 0;
}

insert image description here
Through the comparison of the above codes, it is not difficult to find that the efficiency of passing values ​​and pointers as parameters and return value types is very different.

6.6 Differences between references and pointers

In the grammatical concept, a reference is an alias, which has no independent space and shares the same space with its referenced entity.

int main()
{
    
    
	int a = 10;
	int& ra = a;
	cout<<"&a = "<<&a<<endl;
	cout<<"&ra = "<<&ra<<endl;
	return 0;
}

In the underlying implementation, there is actually space, because references are implemented in the form of pointers. For example, the following code

#include<iostream>
#include <time.h>
using namespace std;
int main()
{
    
    
	int a = 10;
	int& ra = a;
	ra = 20;
	int* pa = &a;
	*pa = 20;
	return 0;
}

Look at the difference between its assembly implementation reference and pointer:
insert image description here

  1. A reference conceptually defines an alias for a variable, and a pointer stores the address of a variable.
  2. References must be initialized when they are defined, pointers are not required
  3. After the reference refers to an entity during initialization, it cannot refer to other entities, and the pointer can point to any entity of the same type at any time
  4. There are no NULL references, but there are NULL pointers
  5. The meaning is different in sizeof: 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 under the 32-bit platform)
  6. The self-increment of the reference means that the referenced entity increases by 1, and the self-increment of the pointer means that the pointer offsets the size of a type backward
  7. Multi-level pointers, but no multi-level references
  8. There are different ways to access entities, the pointer needs to be explicitly dereferenced, and the reference compiler handles it by itself
  9. References are relatively safer to use than pointers

7. Inline functions

7.1 Concept

A function decorated with inline is called an inline function, and the C++ compiler will expand it at the place where the inline function is called during compilation, without the overhead of creating a stack
frame for function calls, and the inline function improves the efficiency of program operation. for example:

#include<iostream>
#include <time.h>
using namespace std;
int Add(int left, int right)
{
    
    
	return left + right;
}
int main()
{
    
    
	int ret = 0;
	ret = Add(1, 2);
	return 0;
}

insert image description here
If you add the inline keyword before the above function to change it into an inline function, the compiler will replace the function call with the function body during compilation.
View by:

  1. In release mode, check whether call Add exists in the assembly code generated by the compiler
  2. In debug mode, the compiler needs to be set, otherwise it will not be expanded (because in debug mode, the compiler will not optimize the code by default, the setting method of vs2022 is given below)
    insert image description here
    insert image description here
    insert image description here
#include<iostream>
#include <time.h>
using namespace std;
inline int Add(int left, int right)
{
    
    
	return left + right;
}
int main()
{
    
    
	int ret = 0;
	ret = Add(1, 2);
	return 0;
}

insert image description here

7.2 Features

  1. inline is a way 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 the efficiency of program operation.
  2. Inline is just a suggestion for the compiler. Different compilers may have different inline implementation mechanisms. 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), no Functions that are recursive and frequently called should be inlined, otherwise the compiler will ignore the inline feature. The fifth edition of "C++prime" has a suggestion about inline: the inline specification is just a request to the compiler, and the compiler can choose to ignore this request. In general, the inlining mechanism is used to optimize small-scale, direct-flow, and frequently-called functions. Many compilers don't support inlining recursive functions, and it's unlikely that a 75-line function will expand inline at the call site.
  3. Inline does not recommend separation of declaration and definition, which will lead to link errors. Because inline is expanded, there is no function address, and the link will not be found. For example, the following code is a typical inline function definition and declaration separation
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
    
    
 cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
    
    
 f(10);
 return 0;
}
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl f(int)" (?f@@YAXH@Z),该符号在函数 _main 中被引用

8. auto keyword (C++11)

8.1 Type alias considerations

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

  1. Type is hard to spell
  2. Ambiguity leads to error-prone
#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 this type is too long, so it is easy to
write wrong. Some people may think that the type can be aliased through typedef, such as:

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

Using typedef to alias the type can indeed simplify the code, but typedef will encounter new difficulties:

typedef char* pstring;
int main()
{
    
    
 	const pstring p1;    // 编译成功还是失败?
 	const pstring* p2;   // 编译成功还是失败?
 	return 0;
}

When programming, it is often necessary to assign the value of an expression to a variable, which requires that the type of the expression be clearly known when declaring the variable
. However, sometimes it is not so easy to do this, so C++11 gives auto a new meaning.

8.2 Introduction to auto

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 <time.h>
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;
}

insert image description here
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.

8.3 Rules for the use of auto

  1. Auto is used in combination 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 &.
    For example:
#include<iostream>
#include <time.h>
using namespace std;
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;
	cout << x << endl;
	*b = 30;
	cout << x << endl;
	c = 40;
	cout << x << endl;
	return 0;
}

insert image description here
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 uses the deduction The type that comes out defines other variables.

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

8.3 Scenarios where auto cannot be derived

  1. auto cannot be used as a function parameter
void TestAuto(auto a)
{
    
    }

Compilation failed, auto cannot be used as a formal parameter type, because the compiler cannot deduce the actual type of a

  1. auto cannot be used directly to declare arrays
void TestAuto()
{
    
    
    int a[] = {
    
    1,2,3};
    auto b[] = {
    
    456};
}

Also this is an example of an error

  1. In order to avoid confusion with auto in C++98, C++11 only retains the usage of auto as a type indicator
  2. The most common advantage of auto in practice is to use it in conjunction with the new for loop provided by C++11 mentioned later, as well as lambda expressions.

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

9.1 Syntax of scoped for

In C++98, if you want to traverse an array, you can do it in the following way:

#include<iostream>
#include <time.h>
using namespace std;
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;
}
int main()
{
    
    
	TestFor();
	return 0;
}

insert image description here
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.

#include<iostream>
#include <time.h>
using namespace std;
void TestFor()
{
    
    
	int array[] = {
    
     1, 2, 3, 4, 5 };
	for (auto& e : array)
		e *= 2;
	for (auto e : array)
		cout << e << " ";
}
int main()
{
    
    
	TestFor();
	return 0;
}

insert image description here
Similar to ordinary loops, you can use continue to end this loop, or use break to jump out of the entire loop.

9.2 Conditions for the use of range for

  1. The range of the for loop iteration must be determined.
    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 the for loop iteration. .
    Note: The following code is problematic because the scope of for is uncertain
void TestFor(int array[])
{
    
    
    for(auto& e : array)
        cout<< e <<endl;
}
  1. The iterated object must implement ++ and == operations.

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

10.1 Null pointers 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

It can be seen that NULL may be defined as a 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;
}

insert image description here

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 forced to (void *)0.

Notice:

  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 new keyword in C++11.
  2. In C++11, 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 representing the null value of the pointer later.

11. Mutual call between C program and C++ program

Here we take the stack structure declaration and definition as an example
insert image description here

11.1 C++ program calling C program

Suppose we need to call the header file definition of the C program in the C++ program.
First, open the established C program, right-click the project name, and open the project properties
insert image description here

insert image description here
Right-click the project name again and click Regenerate.
insert image description here
At this point, the static library file of the C project is successfully generated.
insert image description here
We find the location of the static library file. It will be used later.
insert image description here
Generally, it is in the file directory where you created the project. The specific location depends on your use. For X86 or X64, release or Debug, find it in the corresponding folder. Below
we create a C++ main program with matching brackets, and call the stack in the C program to make the program run successfully.
![Insert picture description here](https://img-blog.csdnimg.cn/144be7242f8944e68a

After it is built, open the project properties first
, find the general linker option, and add the library directory in the column of the additional dependent library directory (the path is the Debug folder where the static library we just generated) and add the additional dependency
insert image description here
name
as The static library name generated by the 6_24 project is generally the project name + .lib
"Properties Panel" —> "Configuration Properties" —> "Linker" —> "Input", and enter the static library name 6_24.lib in the additional dependent library.
insert image description here
We first include the external C program header file
insert image description here

generate plan
insert image description here

The result is an error, which means that there is a problem in the linking process, that is, our program cannot find the address of the function in the static library. The reason is that our static library is in C language, and the function is not modified, but in Our caller is a C++ program, and the modified function name is found during the linking process, so the address of the function cannot be found.

At this time, we can use extern "C" to change the linking rules of the C++ program, and let C++ find the function name according to the rules of C, that is, the function name without any modification, then we can definitely find the address of the function and call it correctly. static library.
insert image description here
Now we regenerate the solution and debug and run
insert image description here
to see success.

11.2 C program calling C++ program

insert image description here
It should be noted that in C++, we can directly use extern to change the compilation rules, so as to call the C program, but the C program cannot adapt to the C++ compilation rules, so we need to modify the C++ program before generating the static library. head File

#ifdef __cplusplus
extern "C"
{
    
    
#endif
void StackInit(ST* ps);
void StackDestroy(ST* ps);
void StackPush(ST* ps, STDataType x);
void StackPop(ST* ps);
STDataType StackTop(ST* ps);
int StackSize(ST* ps);
bool STackEmpty(ST* ps);
#ifdef __cplusplus
}
#endif

In this way, when the static library file is generated, the library is encapsulated according to the compilation rules of the C language, and the C program can directly call it. After that, the steps are the same as above to
generate the static library file, and then directly refer to the external header file.
insert image description here
insert image description here

epilogue

Interested friends can pay attention to the author, if you think the content is good, please give a one-click triple link, you crab crab! ! !
This article is the longest since the author wrote the blog, and it is also the longest time-consuming. It is not easy to make. If there is any inaccuracy, please point it out. Thank you for your
visit. UU’s viewing is the motivation for me to persevere
in time. Under the catalyst, let us all become better people from each other! ! !
Please add a picture description

Guess you like

Origin blog.csdn.net/kingxzq/article/details/131341271