C + + introductory syntax

Namespace

In c and c + +, there are a large number of variables, functions and classes to be learned later. The names of these variables, functions and classes exist in the global scope, which may lead to many conflicts. The purpose of using namespace is to localize the identifier name to avoid naming conflict and name pollution. The emergence of namespace keyword is aimed at this problem.

namespace keyword

To define a namespace, you need to use the namespace keyword, followed by the name of the namespace, followed by a team of {}, which are members of the namespace.
for instance

Members in {} are members in the namespace. To use the namespace, you need to use the scope qualifier (::)

namespace s
{
	int a=4;
}
int main()
{
	printf("%d",s::a);
}

The function of namespace is to prevent naming conflicts, which is also a defect of c language.
For example:

Code 1: no error is reported and can be run. Because the identifier cannot be a keyword, scanf is not a keyword.

int main()
{
	int scanf = 4;
	printf("%d\n", scanf);
}

Code 2: error reporting. When we use things, we generally follow the proximity principle (now look locally and then look globally). We define a local variable scanf, but scanf is a function in the C library. When executing code, the compiler does not know that it is correct to use it, so an error is reported.

int main()
{
	int scanf = 4;
	scanf("%d", &scanf);
}


Code 3: no error, can run. The namespace is used in our code 3, which solves the defects in C

namespace N1
{
	int scanf;
}
int main()
{
	scanf("%d", &(N1::scanf));
	printf("%d\n", N1::scanf);
}

The role of using namespace std in program

When we started the c language, the first code was to output Hello,World!
In c + +, we will also write a Hello,World!

#include<iostream>

using namespace std;
int main()
{
	cout << "Hello World" ;
}

But what does using namespace std do?

First of all, we should understand that in order to prevent naming conflicts, the c + + library defines everything in its library in an std namespace.
Using namespace std
But such a disadvantage is easy to name conflict

If we don't expand std, we can write it like this

#include<iostream>
int main()
{
	std::cout << "Hello World" ;
}

Namespace nesting

Namespaces can also be nested

namespace N2
{
	int a;
	int b;
	int Add(int left, int right)
	{
		return left + right;
	}

	namespace N3
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}

C + + input & output

Hello world!

#include<iostream>
int main()
{
	char num[10];
	std::cin>>num;
	std::cout << num<<endl;
}

CIN > > this object controls the extraction from standard input into num in the form of byte stream.
Cout < < this object controls the insertion of standard output in the form of byte stream.

Default parameters

The default parameter is to specify a default value for the parameter of the function when declaring or defining the function. If the argument is not specified, the default value is used when calling the function.

using namespace std;

void TestFunc(int a = 0)
{
	cout << a << endl;
}
int main()
{
	TestFunc(); // When no parameter is passed, the default value of the parameter is used
	TestFunc(10); // When passing parameters, use the specified arguments
}

Default parameter classification

  • All default parameters
using namespace std;

void TestFunc(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
int main()
{
	TestFunc(); // When no parameter is passed, the default value of the parameter is used
	TestFunc(1,2,3); // When passing parameters, use the specified arguments
}

  • Semi default parameter
using namespace std;

void TestFunc(int a , int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
int main()
{
	TestFunc(1); // When no parameter is passed, the default value of the parameter is used
	TestFunc(1,2,3); // When passing parameters, use the specified arguments
}


be careful:

  1. Semi default parameters must be given from right to left in turn, and cannot be given at intervals
  2. The default parameter cannot appear in the function declaration and definition at the same time (Note: if the declaration and definition locations appear at the same time, and the values provided by the two locations happen to be different, the compiler cannot determine which to use
    (a default.)
  3. The default value must be a constant or a global variable
  4. C language not supported (compiler not supported)

function overloading

Function overloading: it is a special case of functions. C + + allows to declare several functions with the same name with similar functions in the same scope. The formal parameter list (number or type or order of parameters) of these functions with the same name must be different. It is commonly used to deal with the problem of different data types with similar functions

Example 1: constitute overload

int add(int x, int y)
{
	return x + y;
}
double add(double x, double y)
{
	return x + y;
}
int main()
{
	std::cout<<add(1,2)<<std::endl;
	std::cout<<add(1.1,2.2)<<std::endl;
}


Example 2: constitute overload

int add(int a, int b,int c=10)
{
	return a+b+c;
}
double add(int a,int b)
{
	return a+b;
}
int main()
{
	std::cout<<add(1,2)<<std::endl
}

Example 2: two add functions are overloaded.
However, when compiling, the compiler does not know which function to choose (the call is not clear)

The reason why C does not support function overloading (name modification)

To understand this, we need to pave the way for a little knowledge!
In C/C + +, a program needs to go through the following stages to run: preprocessing, compiling, assembling and linking.
Preprocessing: header file expansion, macro replacement, annotation removal, conditional compilation. Become – > I file
Compile: check syntax and produce assembly code. Become – > S file
Assembly: convert assembly code into binary machine code. Become – > O documents
Link: merge segment tables, merge symbol tables, and relocate symbol tables to form executable files. Become – > Out file

int add(int a, int b,int c=10)
{
	return a+b+c;
}
double add(int a,int b)
{
	return a+b;
}
int main()
{
	std::cout<<add(1,2)<<std::endl
}

In the linking stage, when the linker sees that a.o calls add but does not have the address of add, it will find the address of add in the b.o symbol table and link together. However, facing the add function, which name will the linker use to find it?
Under Linux, after compiling with g + +, the name of the function changes. The compiler adds the information of the function parameter type to the modified name, so it supports function overloading. There is no such modification rule in c, so function overloading is not supported.

extern"C"

Sometimes in C + + projects, some functions may need to be compiled in the style of C. adding extern "C" before the function means telling the compiler to compile the function according to the rules of C language. For example, tcmalloc is a project implemented by google in C + +. It provides two interfaces: tcmallc() and tcfree. However, if it is a C project, it cannot be used, so it uses extern "C" to solve it.

However, function overloading cannot be supported with extern"C".

quote

Reference concept

Instead of defining a new variable, a reference gives an alias to an existing variable. The compiler will not open up memory space for the referenced variable. It shares the same memory space with the variable it references.

using namespace std;
int main()
{
	int a=10;
	int& b=a;
	cout<<a<<endl;
	cout<<b<<endl;
	cout<<&a<<endl;
	cout<<&b<<endl;
}


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

Reference properties

  1. References must be initialized when defined
  2. A variable can have multiple references
  3. Reference once an entity is referenced, other entities cannot be referenced
using namespace std;
int main()
{
	int a=10;
	int&b=a;
	int&c=a;
	cout << &a<<endl<<&b<<endl<<&c<< endl;
}

Often cited

int main()
{
	const int a=10;
	//int &ra=a;// 1. There will be an error when compiling this statement. A is a constant
	const int &b=a;
	cout << a <<endl<< b << endl;
}

When the variable is used for const but not used for reference, the permission will be expanded, so an error will be reported.

using namespace std;
int main()
{
	int a=10;
	const int&b=a;
	cout<<a<<endl<<b<<endl;
}


Variable a does not use const, but it is used for reference. This is called expanding the permission, which is not in the way.

Usage scenario

Make parameters

For example:

Value transmission:

void Change(int Ch)
{
	Ch=0;
}
using namespace std;
int main()
{
	int a=10;
	Change(a);
	cout<<a<<endl;
}

Pass the temporary copy of a to the function. The function call only changes the temporary copy of a to 0. At the end of the function, the function in the stack is destroyed, and a does not change.

Address:

void Change(int* Ch)
{
	*Ch=0;
}
using namespace std;
int main()
{
	int a=10;
	Change(&a);
	cout<<a<<endl;
}

Send the address, find a through the address and change a

quote:

void Change(int& Ch)
{
	Ch=0;
}
using namespace std;
int main()
{
	int a=10;
	Change(a);
	cout<<a<<endl;
}

To change the alias of a is to change a

Do return value

For example:

Return by value: returns a copy of the returned object c

int add(int x,int y)
{
	int c=x+y;
	return c;
}
using namespace std;
int main()
{
	int a=1,b=2;
	int ret=add(a,b);
	cout<<ret<<endl;
}

The add function returns a temporary variable in the stack, which is given to ret. c was destroyed at the end of the function call.

int add(int x,int y)
{
	int c=x+y;
	return c;
}
using namespace std;
int main()
{
	int a=1,b=2;
	//int& ret=add(a,b);// Because the c in the add function is destroyed at the end of the call, a temporary variable is passed to ret. the temporary variable is constant, so const should be added when referencing
	const int& ret=add(a,b);
	cout<<ret<<endl;
}


The reference returned by c itself

int& add(int x,int y)
{
	//int c=x+y; After the function call, the stack frame is destroyed and C is gone.
	static int c=x+y;//With static, c is put into the static area. The destruction of stack frames has an impact on each of the static areas.
	return c;
}
using namespace std;
int main()
{
	int a=1,b=2;	
	int ret=add(a,b);
	cout<<ret<<endl;
}

In fact, if the function scope is out, the return variable does not exist and cannot be referenced.

Comparison of efficiency of value transfer and reference transfer

Taking value as a parameter or return value type, during parameter transfer and return, the function will not directly transfer the argument or return the variable itself, but transfer a temporary copy of the argument or return variable. Therefore, using value as a parameter or return value type is very inefficient, especially when the parameter or return value type is very large.

Value transfer and reference transfer

#include <time.h>
struct A
{ 
	int a[10000]; 
};
void TestFunc1(A a){}
void TestFunc2(A& a){}
using namespace std;
void main()
{
	A aa;
	// Take value as function parameter
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(aa);
	size_t end1 = clock();
	// Take reference as function parameter
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(aa);
	size_t end2 = clock();
	// Calculate the time after the two functions run respectively
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}


Return by value and reference

#include <time.h>
struct A
{ 
	int a[10000]; 
};
A aa;
A TestFunc1(A a){return a;}
A& TestFunc2(A a){return a;}
using namespace std;
void main()
{
	// Take value as function parameter
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(aa);
	size_t end1 = clock();
	// Take reference as function parameter
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(aa);
	size_t end2 = clock();
	// Calculate the time after the two functions run respectively
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}


Summary:

When the parameter and return value are relatively large variables, passing reference, passing parameter and passing reference as return value can also improve efficiency. As long as the conditions are met, try to use reference to pass parameters and reference as the return value (deep copy can be avoided)

The difference between reference and pointer

Syntactically, a reference is an alias. There is no independent space, and it shares the same space with its reference entity.
In fact, there is space in the underlying implementation, because the reference is implemented in the form of pointer.

Differences between references and pointers:

  1. References must be initialized when defined, and pointers are not required
  2. After a reference references an entity during initialization, it can no longer reference other entities, and the pointer can point to any entity of the same type at any time
    entity
  3. There is no NULL reference, but there is a NULL pointer
  4. In sizeof, the meaning is different: the reference result is the size of the reference type, but the pointer is always the number of bytes in the address space (32-bit platform)
    (4 bytes)
  5. Reference self addition means that the referenced entity increases by 1, and pointer self addition means that the pointer is offset by one type backward
  6. There are multi-level pointers, but there are no multi-level references
  7. The access to entities is different. The pointer needs to be explicitly dereferenced, and the reference compiler handles it by itself
  8. References are relatively safer to use than pointers

Inline function

The function decorated with inline is called inline function. During compilation, the C + + compiler will expand where the inline function is called. There is no overhead of function stack. The inline function improves the efficiency of program operation.

C language provides macro function support for small functions to avoid the consumption of establishing stack frames, which is expanded in the preprocessing stage
Since C language has solved the problem of small functions to avoid the establishment of stack frames, why does C + + provide inline functions?
Disadvantages of macro functions (interview questions and test sites):

  1. Debugging is not supported
  2. Macro function syntax is complex and error prone
    For example: write a macro function of ADD
//Typical wrong writing
#define ADD(int x,int y) return x+y
//Semicolon is not allowed
#define ADD(x,y) x+y;
#define ADD(x,y) (x)+(y)

//Standard writing
#define ADD(x,y) ((x)+(y))
  1. No type safety check

characteristic:

  1. Inline is a method of exchanging space for time, which saves the cost of calling functions. Therefore, functions with long code or loop / recursion are not suitable for inline functions.
  2. Inline is only a suggestion for the compiler. The compiler will optimize automatically. If there is loop / recursion in the function defined as inline, the compiler will ignore inline during optimization.
  3. Inline does not recommend the separation of declaration and definition, which will lead to link errors. Because the inline is expanded, there will be no function address, and the link will not be found.

auto keyword (C++11)

In C++11, the standards committee gives a new meaning to auto, that is, auto is no longer a storage type indicator, but as a new type indicator to indicate the compiler. The variables declared by auto must be derived by the compiler at compile time.

using namespace std;
int main()
{
	int a=10;
	auto b=a;
	auto c=a;
	//auto f; Unable to compile, variables must be initialized when using auto to define them
	cout<<a<<endl<<b<<endl<<c<<endl;
}


be careful:

When using auto to define variables, it must be initialized. In 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" for type declaration. The compiler will replace Auto with the actual type of variable at compile time.

Usage rules of auto

  1. When declaring pointer type with auto, there is no difference between auto and auto *, but when declaring reference type with auto, it must be added&
int main()
{
	int x = 10;
 	auto a = &x;
 	auto* b = &x;
 	auto& c = x;
}
  1. 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 actually deduces only the first type, and then uses the derived type to define other variables.
 `void TestAuto()
{
 auto a = 1, b = 2; 
 auto c = 3, d = 4.0; // This line of code will fail to compile because the initialization expression types of c and d are different`
}

Scenes that cannot be deduced by auto

  1. auto cannot be an argument to a function
// The code here fails to compile. auto cannot be used as a formal parameter type because the compiler cannot deduce the actual type of A
void TestAuto(auto a)
{}
  1. auto cannot be used directly to declare arrays
void TestAuto()
{
 int a[] = {1,2,3};
 auto b[] = {4,5,6};
}
  1. In order to avoid confusion with auto in C++98, C++11 only retains the use of auto as a type indicator

Range based for loop (C++11)

In C++98, if you want to traverse an array, you can do it as follows:

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 set with a range, it is redundant for the programmer to explain the range of the loop, and sometimes it is easy to make mistakes. Therefore, a range based for loop is introduced in C++11. The parenthesis after the for loop is divided into two parts by the colon ":": the first part is the variable used for iteration within 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)
 //Automatically traverse, take out the elements in the array and assign them to e until the end.
 cout << e << " ";
 
 return 0;
}

Note: similar to ordinary loops, you can use continue to end this loop or break to jump out of the whole loop.

Pointer null - nullptr(C++11)

Pointer null value in C++98

In good C/C + + programming habits, 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 legal point, we basically initialize it in the following way:

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 a literal constant 0 or as a constant of a typeless pointer (void *). No matter what definition is adopted, it is inevitable to encounter some troubles when using NULL pointers, 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 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 regards it as an integer constant by default. If you want to use it as a pointer, you must forcibly convert it (void *)0.
be careful:

  1. When nullptr is used to represent pointer null value, it is not necessary to include header file, because nullptr is introduced by C++11 as a new keyword.
  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 in the subsequent representation of pointer null values.

Keywords: C++ Programming

Added by BigX on Wed, 09 Feb 2022 03:21:39 +0200