C + + Learning comb

Posted by edwardsbc on Thu, 24 Feb 2022 15:22:02 +0100

C + + Learning comb

:: scope operator

Usually, if there are two variables with the same name, one is a global variable and the other is a local variable, then the local variable has higher priority within its scope (the proximity principle, commonly known as the strong dragon does not suppress the local snake, I decide my territory), which will mask the global variable.

Scope operators can be used to solve the problem of duplicate names of local variables and global variables

Name control

Creating names is one of the most basic activities in the process of programming. When a project is large, it will inevitably contain a large number of names. c + + allows us to control the generation and visibility of names.

We were learning the c language before. We can use the static keyword to make the name visible only in this compilation unit. In c + +, we will control the access to the name through a namespace.

Namespace

Namespace is used to solve the problem of name conflict among modules in large projects. In large projects, each function (module) is assigned to each person for development, but there will be inevitably the problem of name conflict when each person develops. Previously, in C, you can only modify the variable name or add modifiers. Therefore, the namespace is used to solve the name conflict of each module in a large project. Therefore, the namespace must be defined under the global scope and used by the whole project. Instead of resolving name conflicts in separate files, name conflicts in separate files can be resolved through code blocks.

Characteristics of namespace

  1. The namespace must be defined globally

    namespace A{
    	int a = 10;
    }
    namespace B{
    	int a = 20;
    }
    void test(){
    	cout << "A::a : " << A::a << endl;
    	cout << "B::a : " << B::a << endl;
    }
    
  2. Namespace nested namespace

    namespace A{
    	int a = 10;
    	namespace B{
    		int a = 20;
    	}
    }
    void test(){
    	cout << "A::a : " << A::a << endl;
    	cout << "A::B::a : " << A::B::a << endl;
    }
    
  3. The namespace is open, that is, new members can be added to the existing namespace at any time

    namespace A{
    	int a = 10;
    }
    
    namespace A{
    	void func(){
    		cout << "hello namespace!" << endl;
    	}
    }
    
    void test(){
    	cout << "A::a : " << A::a << endl;
    	A::func();
    }
    
  4. Namespace declaration and definition are separable

    1. Header file declaration
    #Header file
    #pragma once
    
    namespace MySpace{
    	void func1();
    	void func2(int param);
    }
    
    1. cpp file definition (Implementation)
    #cpp file
    void MySpace::func1(){
    	cout << "MySpace::func1" << endl;
    }
    void MySpace::func2(int param){
    	cout << "MySpace::func2 : " << param << endl;
    }
    
  5. Anonymous namespace means that the identifier in the namespace can only be accessed in this file, which is equivalent to adding static to this identifier so that it can be used as an internal link

    namespace{
    	
    	int a = 10;
    	void func(){ cout << "hello namespace" << endl; }
    }
    void test(){
    	cout << "a : " << a << endl;
    	func();
    }
    
  6. Namespaces can use aliases

    namespace veryLongName{
    	
    	int a = 10;
    	void func(){ cout << "hello namespace" << endl; }
    }
    
    void test(){
    	namespace shortName = veryLongName;//Define alias
    	cout << "veryLongName::a : " << shortName::a << endl;
    	veryLongName::func();
    	shortName::func();
    }
    
    

Use of namespaces

Three ways to use namespaces

  1. Use:: scope operator

    You must add a namespace to each name

    namespace A{
    	int paramA = 20;
    	int paramB = 30;
    	void funcA(){ cout << "hello funcA" << endl; }
    	void funcB(){ cout << "hello funcA" << endl; }
    }
    
    void test(){
    	//1. Through namespace domain operator
    	cout << A::paramA << endl;
    	A::funcA();
    	//2. using statement
    	using A::paramA;
    	using A::funcA;
    	cout << paramA << endl;
    	//cout << paramB << endl; // Not directly accessible
    	funcA();
    	//3. Conflict of the same name
    	//int paramA = 20; // Note the conflict of the same name in the same scope
    }
    
  2. using declaration

    1. The using declaration makes the specified identifier available.
    2. The namespace functions declared by using have overloads, and all overloads of functions will be declared.
    namespace A{
    	void func(){}
    	void func(int x){}
    	int  func(int x,int y){}
    }
    void test(){
    	using A::func;
    	func();
    	func(10);
    	func(10, 20);
    }
    
  3. Compiling instructions using

    Using the using compilation instruction under the scope is equivalent to declaring all variables in the namespace.

    The using compilation instruction can be used in a global or local scope. Used globally, it is equivalent to seeing the whole file. The namespace name can be omitted, but the open namespace under this file is not visible to other files. Therefore, in the source file where the main function is located, do not use the using compilation instruction in the global scope, otherwise it is easy to cause name conflict and ambiguity.

    Under the local scope, you can open the namespace under the local scope. Only under this local scope can you directly omit the namespace and use it directly.

    namespace A{
    	int paramA = 20;
    	int paramB = 30;
    	void funcA(){ cout << "hello funcA" << endl; }
    	void funcB(){ cout << "hello funcB" << endl; }
    }
    
    void test01(){
    	using namespace A;
    	cout << paramA << endl;
    	cout << paramB << endl;
    	funcA();
    	funcB();
    
    	//No ambiguity
    	int paramA = 30;
    	cout << paramA << endl;
    }
    
    namespace B{
    	int paramA = 20;
    	int paramB = 30;
    	void funcA(){ cout << "hello funcA" << endl; }
    	void funcB(){ cout << "hello funcB" << endl; }
    }
    
    void test02(){
    	using namespace A;
    	using namespace B;
    	//Ambiguity occurs. I don't know whether to call paramA of A or B
    	//cout << paramA << endl;
    }
    

    ####Comparison between using declaration and using compilation

    using A::paramA;//statement
    using namespace A;//compile
    
    1. Quantitative comparison

      The using declaration is to open a specific variable name in the namespace, while the using compilation instruction is to open the entire namespace.

    2. Declaration method

      The namespace keyword is not required for declaration, but for compilation.

      The declaration needs to execute namespace specific names to open a single name.

    The key problem to remember is that when a global using compilation directive is introduced, the namespace is opened for the file, and it will not affect any other files, so the control of namespaces can be adjusted in each implementation file. For example, if you find that there are too many naming conflicts caused by using instructions in an implementation file, you need to make a simple change to the file and eliminate the name conflict through explicit qualification or using declaration, so you don't need to modify other implementation files.

Global variable detection enhancement

int a = 10; //Assignment, as definition
int a; //No assignment, as a declaration

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

This code failed to compile in c + +, and passed the compilation in c

All variables and functions in C + + must have types

//i has no write type and can be any type
int fun1(i){
	printf("%d\n", i);
	return 0;
}
//i has no write type and can be any type
int fun2(i){
	printf("%s\n", i);
	return 0;
}
//There is no write parameter, which means that any type of argument can be passed
int fun3(){ 
	printf("fun33333333333333333\n");
	return 0;
}

//In C language, if the function has no parameters, it is recommended to write void, which means there are no parameters
int fun4(void){
	printf("fun4444444444444\n");
	return 0;
}

g(){
	return 10;
}

int main(){

	fun1(10);
	fun2("abc");
	fun3(1, 2, "abc");
	printf("g = %d\n", g());

	return 0;
}

The above c code can be compiled by c compiler, but it cannot be compiled by c + + compiler.

  • In C language, int fun() means a function with a return value of int and accepting any parameters, and int fun(void) means a parameterless function with a return value of int.
  • In C + +, int fun() and int fun(void) have the same meaning. They both represent parameterless functions with a return value of int.

Stricter type conversion

In C + +, different types of variables generally cannot be assigned directly, and corresponding strong conversion is required.

typedef enum COLOR{ GREEN, RED, YELLOW } color;
int main(){

	color mycolor = GREEN;
	mycolor = 10;
	printf("mycolor:%d\n", mycolor);
	char* p = malloc(10);
	return EXIT_SUCCESS;
}

The above c code can be compiled by the c compiler, but the c + + compiler cannot.

struct type reinforcement

  • Structure variables defined in c need to be added with struct keyword, which is not required in c + +.
  • The structure in c can only define member variables, not member functions. c + + can define both member variables and member functions.
//1. Member variables and member functions can be defined in the structure
struct Student{
	string mName;
	int mAge;
	void setName(string name){ mName = name; }
	void setAge(int age){ mAge = age; }
	void showStudent(){
		cout << "Name:" << mName << " Age:" << mAge << endl;
	}
};

//2. There is no need to add struct keyword to define structure variables in C + +
void test01(){
	Student student;
	student.setName("John");
	student.setAge(20);
	student.showStudent();
}

"New" bool type keyword

The bool type of standard c + + has two built-in constants: true (converted to integer 1) and false (converted to integer 0) to represent the state. These three names are keywords.

  • bool type has only two values, true(1 value) and false(0 value)

  • bool type takes up 1 byte

  • When assigning a value to bool type, the non-0 value will be automatically converted to true (1), and the 0 value will be automatically converted to false(0)

    void test()
    {	cout << sizeof(false) << endl; //Is 1, and the / / bool type takes up one byte
    	bool flag = true; // There is no such type in c language
    	flag = 100; //When assigning a value to bool type, the non-0 value will be automatically converted to true (1), and the 0 value will be automatically converted to false(0)
    }
    

bool type in c language

c There are also in the language bool Type, in c99 There was no standard before bool keyword, c99 Standards already exist bool Type, including header file stdbool.h,You can use and c++same bool Type.

Function enhancement of ternary operator

  • The return value of c language ternary operation expression is the data value, which is the right value and cannot be assigned.

    	int a = 10;
    	int b = 20;
    	printf("ret:%d\n", a > b ? a : b);
    	//Think about a question, (a > b? A: b) what is returned by the expression of ternary operation?
    	
    	//(a > b ? a : b) = 100;
    	//The returned value is the right value
    
  • The return value of c + + ternary operation expression is the variable itself (Reference), which is an lvalue and can be assigned.

Left value right value

  • Lvalue is lvalue, L stands for Location, which means that the memory can be addressed and assigned.

  • The right value is Rvalue, and R stands for Read, which means you can know its value.

In c + +, the left value can be placed on the left of the assignment operator, and the right value can be placed on the right of the assignment operator. Some variables can be left-hand or right-hand values.

For example: int temp = 10; temp has an address in memory, but 10 doesn't, but its value can be Read.

const in C/C + +

The word const literally means constant. It is a keyword in c/c + + and a qualifier. It is used to limit that a variable cannot be changed. It converts an object into a constant.

const int a = 10;
A = 100; //Compilation error. const is a constant and cannot be modified

The difference of const in C/C + +

const in C

Constants were introduced in early versions of c + +, when the standard c specification was being developed. At that time, although the c committee decided to introduce const into c, they understood const in c as "an ordinary variable that cannot be changed", that is, const should be a read-only variable. Since it is a variable, memory will be allocated to const, const in c is a global read-only variable, and the read-only variable modified by const in c language is externally connected.

If so:

const int arrSize = 10;
int arr[arrSize];

It may seem like a reasonable coding, but it will lead to an error. Because arrSize occupies a certain block of memory, the C compiler does not know its value at compile time?

const in C + +

**In c + +, a const does not need to create memory space, but in c, a const always needs a piece of memory space** In c + +, whether to allocate memory space for const constant depends on how to use it. Generally speaking, if a const is only used to replace a name with a value (just like using #define), the storage space does not have to be created.

  • If there is no memory allocated in the storage space, the value may be folded into the code after the data type check to make the code more effective.

  • However, taking a const address or defining it as extern will create memory space for the const.

  • In c + +, const, which appears outside all functions, acts on the whole file (that is, it is invisible outside the file). It defaults to internal connection, and other identifiers in c + + generally default to external connection.

Summary of similarities and differences of const in C/C + +

  • c language global const will be stored in read-only data segment. Global const in c + +. When declaring extern or taking the address of a variable, the compiler will allocate a storage address, and the variable is stored in a read-only data segment. Both are protected by read-only data segments and cannot be modified.

    const int constA = 10;
    int main(){
        int* p = (int*)&constA;
        *p = 200;
    }
    The above code is in c/c++It is compiled in and modified during the run time constA A write error occurred while the value of. The reason is to modify the data of the read-only data segment.
    
  • In c language, local const is stored in the stack area, but the value of const read-only variable cannot be modified directly through the variable, but the compiler check can be skipped and the const value can be modified indirectly through the pointer.

    	const int constA = 10;
    	int* p = (int*)&constA;
    	*p = 300;
    	printf("constA:%d\n",constA);
    	printf("*p:%d\n", *p);
    
    Operation results:
        constA:300
        *p=300
    c In language, it can be indirectly assigned by pointer constA Value of.
    
  • In c + +, local const variables should be treated differently:

    1. For the basic data type, that is const int a = 10, the compiler will put it into the symbol table without allocating memory. When taking the address, it will allocate memory (open up a temporary variable, and the address is actually the address of the temporary variable).
    ```c
    	const int constA = 10;
    	//6. Seven lines of code are equivalent to the following three lines of code
    	//int temp; 
    	//int *p = &temp;
    	//*p=300;
    	int* p = (int*)&constA;
    	*p = 300;
    	cout << "constA:" << constA << endl;
    	cout << "*p:" << *p << endl;
    ```
    
    ```c
     Operation results:
        constA:10
        *p=300
    constA In the symbol table, when we constA Take the address. At this time constA Allocated new space,*p The operation is the allocated space, and constA Is the value obtained from the symbol table.
    ```
    
    1. For basic data types, if const variables are initialized with a variable, and const int a = b, memory will also be allocated to a.

      	int b = 10;
      	const int constA = b;
      	int* p = (int*)&constA;
      	*p = 300;
      	cout << "constA:" << constA << endl;
      	cout << "*p:" << *p << endl;
      
      Operation results:
          constA:300
          *p=300
      constA Memory is allocated, so we can modify it constA Value in memory.
      
    2. For custom data types, such as class objects, memory will also be allocated.

          const Person person; //age not initialized
      	//person.age = 50; // Cannot be modified
      	Person* pPerson = (Person*)&person;
      	//Pointer indirect modification
      	pPerson->age = 100;
      	cout << "pPerson->age:" << pPerson->age << endl;
      	pPerson->age = 200;
      	cout << "pPerson->age:" << pPerson->age << endl;
      
      Operation results:
          pPerson->age:100
          pPerson->age:200
       by person Memory is allocated, so we can modify it through the indirect assignment of the pointer person object
      
  • Const in c defaults to external connection, and const in c + + defaults to internal connection There will be errors in both const and const files. In c + +, it will not, because const in c + + is internally connected by default. If you want const in c + + to have an external connection, you must display the declaration as: external const int a = 10;

    stay C In, the compiler increases global variables at compile time extern,Including global const Modified variable, but this variable is read-only during the compiler.
    

Const was adopted by c + + and added to standard c, although they are very different. In c, the compiler treats const like a variable, but with a special tag that means "you can't change me". When const is defined in c, the compiler creates space for it, so if multiple consts with the same name are defined in two different files, the linker will have a link error.

Can I define arrays with variables

In compilers that support the c99 standard, you can use variables to define arrays.

1. Microsoft official description vs2013 Compiler does not support c99.: 
Microsoft C conforms to the standard for the C language as set forth in the 9899:1990 edition of the ANSI C standard. 
2. The following code is in Linux GCC support c99 Compiler compilation passed
-----------------------------------
int a = 10;
int arr[a];
int i = 0;
for(;i<10;i++) 
	arr[i] = i;
i = 0;
for(;i<10;i++)
	printf("%d\n",arr[i]);

Try to replace #define with const

In the old version of C, if you want to establish a constant, you must use the "preprocessor"

#define MAX 1024;

The macro MAX we defined has never been seen by the compiler, because in the preprocessing stage, all MAX has been replaced with 1024, so MAX does not add it to the symbol table. However, when we use this constant to obtain a compilation error message, it may cause some confusion, because this message may refer to 1024, but it does not mention MAX. if MAX is defined in a header file not written by you, you may not know what 1024 stands for, and it may take a long time to solve this problem.

The solution is to replace the above macro with a constant.

const int max= 1024;

Summary of differences between const and #define

  1. const has types, which can be used for compiler type safety check# define has no type and cannot be type checked.

    Macro constant has no type. param called an overloaded function of int type. const has a type, so param calls the function of the desired short type.

    #include <iostream>
    using namespace std;
    
    #define PARAM 128
    const short param = 128;
    
    void func(short a){
    	cout << "short!" << endl;
    }
    void func(int a){
    	cout << "int" << endl;
    }
    
    int main()
    {
    	func(param);//short
    	func(PARAM);//int
    	system("pause");
    	return 0;
    }
    
  2. const has a scope, but #define does not pay attention to the scope. The default definition is from the end of the file If you define constants that are valid under the specified scope, #define cannot be used. (it can be realized through #define #undef)

    void func1(){
    	const int a = 10;
    	#define A 20 
        //#undef A / / uninstall macro constant a
    }
    void func2(){
    	//cout << "a:" << a << endl; // Inaccessible, out of const int a scope
    	cout << "A:" << A << endl; //#The define scope is accessible from the definition to the end of the file or to #undef
    }
    int main(){
    	func2();
    	return EXIT_SUCCESS;
    }
    
    
    Can a macro constant have a namespace?

    Macro constants are just text substitutions and have no namespaces.

    namespace MySpace{
    	#define num 1024
    }
    void test(){
    	//cout << MySpace::NUM << endl; // error
    	//int num = 100; // name conflict
    	cout << num << endl;
    }
    

quote

Reference basic syntax

**Reference is an important extension of c + + to c** In c/c + +, the functions of pointers are basically the same, but c + + adds another way to pass addresses to functions, which is pass by reference. It also exists in some other programming languages and is not the invention of c + +.

  • The variable name is essentially the alias of a continuous memory space, which is a label (house number)

  • The program uses variables to apply for and name memory space

  • The storage space can be used by the name of the variable

Can only one alias be used for a continuous memory space?

The concept of reference has been added in c + +, which can be used as an alias of a defined variable.

Basic syntax:

Type& ref = val;

matters needing attention:

  • &Here is not an address operation, but an identification function.
  • The type identifier refers to the type of the target variable.
  • Must be initialized when declaring a reference variable.
  • The reference cannot be changed after initialization.
  • Cannot have NULL references. You must ensure that the reference is associated with a legal storage unit.
  • You can create a reference to an array (but you cannot create a reference array)
/1. Cognitive citation
void test01(){

	int a = 10;
	//Give variable a an alias b
	int& b = a;
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "------------" << endl;
	//Operation b is equivalent to operation a itself
	b = 100;
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "------------" << endl;
	//A variable can have n aliases
	int& c = a;
	c = 200;
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "c:" << c << endl;
	cout << "------------" << endl;
	//a. The addresses of B and C are the same
	cout << "a:" << &a << endl;
	cout << "b:" << &b << endl;
	cout << "c:" << &c << endl;
}
//2. Precautions for using references
void test02(){
	//1) References must be initialized
	//int& ref; // Error: reference must be initialized
	//2) Once a reference is initialized, it cannot be changed
	int a = 10;
	int b = 20;
	int& ref = a;
	ref = b; //The reference cannot be changed
	//3) There can be an array of references, and the array element cannot be a reference (but a reference can be established to the array)
	int arr[10];
	//int& ref3[10] = arr;
}
//1. Create array reference method 1
	typedef int ArrRef[10];
	int arr[10];
	ArrRef& aRef = arr;
	for (int i = 0; i < 10;i ++){
		aRef[i] = i+1;
	}
	for (int i = 0; i < 10;i++){
		cout << arr[i] << " ";
	}
	cout << endl;
	//2. Create array reference method 2
	int(&f)[10] = arr;
	for (int i = 0; i < 10; i++){
		f[i] = i+10;
	}
	for (int i = 0; i < 10; i++){
		cout << arr[i] << " ";
	}
	cout << endl;

References in functions

The most common place to see references is in function parameters and return values. When a reference is used as a function parameter, any modification of the reference within the function will change the parameters outside the function. Of course, you can do the same thing by passing a pointer, but references have clearer syntax.

If you return a reference from a function, you must treat it as if you returned a pointer from a function. When the function returns a value, the memory associated with the reference must exist.

//pass by value
void ValueSwap(int m,int n){
	int temp = m;
	m = n;
	n = temp;
}
//Address delivery
void PointerSwap(int* m,int* n){
	int temp = *m;
	*m = *n;
	*n = temp;
}
//Reference passing
void ReferenceSwap(int& m,int& n){
	int temp = m;
	m = n;
	n = temp;
}
void test(){
	int a = 10;
	int b = 20;
	//pass by value
	ValueSwap(a, b);
	cout << "a:" << a << " b:" << b << endl;
	//Address delivery
	PointerSwap(&a, &b);
	cout << "a:" << a << " b:" << b << endl;
	//Reference passing
	ReferenceSwap(a, b);
	cout << "a:" << a << " b:" << b << endl;
}

The effect of referencing parameters is the same as passing by address. The syntax of the reference is clearer and simpler:

  1. The argument passed during function call does not need to be marked with "&"

  2. It is not necessary to add "*" before the parameter in the called function

References exist as aliases for other variables, so they can replace pointers in some cases. C + + advocates using reference passing instead of address passing, because reference syntax is easy and error free.

//Returns a local variable reference
int& TestFun01(){
	int a = 10; //local variable
	return a;
}
//Returns a static variable reference
int& TestFunc02(){	
	static int a = 20;
	cout << "static int a : " << a << endl;
	return a;
}
int main(){
	//Cannot return a reference to a local variable
	int& ret01 = TestFun01();
	//If the function is an lvalue, it must return a reference
	TestFunc02();
	TestFunc02() = 100;
	TestFunc02();

	return EXIT_SUCCESS;
}
  • Cannot return a reference to a local variable.
  • When a function is an lvalue, it must return a reference.

The essence of reference

The essence of reference is a pointer constant implemented in c + +

c + + compiler uses constant pointer as the internal implementation of reference in the compilation process, so the space occupied by reference is the same as that of pointer, but this process is the internal implementation of compiler and not visible to users.

//Found to be a reference, converted to int * const ref = & A;
void testFunc(int& ref){
	ref = 100; // Ref is a reference, converted to * ref = 100
}
int main(){
	int a = 10;
	int& aRef = a; //Automatically convert to int * const aref = & A; This also explains why references must be initialized
	aRef = 20; //It is found internally that aRef is a reference, which is automatically converted to: * aRef = 20;
	cout << "a:" << a << endl;
	cout << "aRef:" << aRef << endl;
	testFunc(a);
	return EXIT_SUCCESS;
}

Pointer reference

In c language, if you want to change the point of a pointer instead of the content it points to, the function declaration may be as follows:

void fun(int**);

Give the pointer variable an alias:

Type* pointer = NULL;  
Type*& = pointer;
struct Teacher{
	int mAge;
};
//The pointer indirectly modifies the age of the teacher
void AllocateAndInitByPointer(Teacher** teacher){
	*teacher = (Teacher*)malloc(sizeof(Teacher));
	(*teacher)->mAge = 200;  
}
//Reference to modify teacher age
void AllocateAndInitByReference(Teacher*& teacher){
	teacher->mAge = 300;
}
void test(){
	//Create Teacher
	Teacher* teacher = NULL;
	//Pointer indirect assignment
	AllocateAndInitByPointer(&teacher);
	cout << "AllocateAndInitByPointer:" << teacher->mAge << endl;
	//Reference assignment, and transfer the teacher itself to the ChangeAgeByReference function
	AllocateAndInitByReference(teacher);
	cout << "AllocateAndInitByReference:" << teacher->mAge << endl;
	free(teacher);
}

For the definition in c + +, the syntax is much clearer. The function parameter becomes a reference to the pointer without obtaining the address of the pointer.

const reference

const Type& ref = val;

Constant reference note:

  • A literal cannot be assigned to a reference, but it can be assigned to a const reference

  • const modified reference, cannot be modified.

void test01(){89*8
	int a = 100;
	const int& aRef = a; //At this point, aRef is a
	//aRef = 200;  Cannot pass the value of aref
	a = 100; //OK
	cout << "a:" << a << endl;
	cout << "aRef:" << aRef << endl;
}
void test02(){
	//You cannot assign a literal to a reference
	//int& ref = 100;
	//But you can assign a literal to a constant reference
	const int& ref = 100; //int temp = 200; const int& ret = temp;
}

const reference usage scenario

Constant references are mainly used for formal parameters of functions, especially copy / copy constructors of classes.

Benefits of defining function parameters as constant References:

  • Reference does not generate new variables, which reduces the cost of shape participating in argument passing.

  • Since references may cause arguments to change with formal parameters, defining them as constant references can eliminate this side effect.

    If you want the argument to change with the formal parameter, use the general reference. If you don't want the argument to change with the formal parameter, use the constant reference.

    //Const int & param prevents accidental modification of data in functions
    void ShowVal(const int& param){
    	cout << "param:" << param << endl;
    }
    

    inline function

    Introduction of inline function

    An important feature of c + + inheriting from c is efficiency. If the efficiency of c + + is obviously lower than that of c, then a large number of programmers will not use c + +.

    In c, we often write some short and frequently executed calculations as macros instead of functions. The reason for this is to improve execution efficiency. Macros can avoid the cost of function calls, which are completed by preprocessing.

    There are two problems after using c + + preprocessing macros:

    • The first one will also appear in c. the macro looks like a function call, but there will be hidden errors that are difficult to find.

    • The second problem is unique to c + +. The preprocessor does not allow access to class members, that is, the preprocessor macro cannot be used as a member function of a class.

    In order to maintain the efficiency of preprocessing macros and increase security, and can be accessed freely in the class like general member functions, c + + introduces inline function.

    In order to inherit the efficiency of macro functions, inline functions have no cost of function call. Then, like ordinary functions, they can check the security of parameters and return value types, and can be used as member functions.

Defects in preprocessing macros

The key to the problem of preprocessor macros is that we may think that the behavior of preprocessor is the same as that of compiler. Of course, it is also because macro function calls and function calls look the same in appearance, because they are also easy to be confused. But there are also some subtle problems:

  • Question 1: when macro parameters are function parameters

    #define ADD(x,y) x+y
    inline int Add(int x,int y){
    	return x + y;
    }
    void test(){
    	int ret1 = ADD(10, 20) * 10; //The desired result is 300
    	int ret2 = Add(10, 20) * 10; //I hope the result is 300
    	cout << "ret1:" << ret1 << endl; //210
    	cout << "ret2:" << ret2 << endl; //300
    }
    
  • Problem 2: side effects of self increasing and decreasing macro parameters

    #define COMPARE(x,y) ((x) < (y) ? (x) : (y))
    int Compare(int x,int y){
    	return x < y ? x : y;
    }
    void test02(){
    	int a = 1;
    	int b = 3;
    	//cout << "COMPARE(++a, b):" << COMPARE(++a, b) << endl; // 3
    	cout << "Compare(int x,int y):" << Compare(++a, b) << endl; //2
    }
    
  • Problem 3: the predefined macro function has no scope concept and cannot be used as a member function of a class, that is, the predefined macro has no way to represent the scope of a class.

Connotation function

Basic concepts of inline function

In c + +, the concept of predefined macro is realized by inline function, and the inline function itself is also a real function. Inline functions have all the behaviors of normal functions. The only difference is that inline functions expand like predefined macros in place, so there is no cost of function calls. Therefore, you should use inline functions instead of macros.

Add the inline keyword in front of a normal function (non member function) to make it an inline function. However, it must be noted that the function body and declaration must be combined, otherwise the compiler will treat it as an ordinary function.

inline void func(int a);

The above method has no effect. It is just a function declaration. It should be done as follows:

inline int func(int a){return ++;}

Note: the compiler will check whether the function parameter list is used correctly and return the value (make the necessary conversion). These things cannot be done by the preprocessor.

Inline functions do take up space, but the advantage of inline functions over ordinary functions is that they save the overhead of stack pressing, jump and return when calling functions. We can understand that inline functions trade space for time.

Inline function inside class

In order to define an inline function, you usually have to put an inline keyword before the function definition. However, it is not necessary to define inline functions inside a class. Any function defined inside a class automatically becomes an inline function.

class Person{
public:
	Person(){ cout << "Constructor!" << endl; }
	void PrintPerson(){ cout << "output Person!" << endl; }
}
Constructor Person,Member function PrintPerson It is defined inside the class and automatically becomes an inline function.

Inline functions and compilers

Inline functions are not always valid. In order to understand when inline functions are valid, you should know how the compiler will deal with inline functions?

For any type of function, the compiler will put the function type (including function name, parameter type and return value type) into the symbol table. Similarly, when the compiler sees the inline function and analyzes the inline function body and finds no errors, it will also put the inline function into the symbol table.

When calling an inline function, the compiler first ensures that the incoming parameter types are correctly matched. Or if the types are not exactly matched, but can be converted to the correct type, and the return value matches the correct type in the target expression, or can be converted to the target type, the inline function will directly replace the function call, This eliminates the overhead of function calls. If the inline function is a member function, the object this pointer will also be put in place.

Type checking and type conversion, including putting the object this pointer in the right place, are all things that the preprocessor cannot do.

However, there are some limitations in c + + inline compilation. The compiler may not consider inline compilation of functions in the following cases:

  • There cannot be any form of circular statement
  • There cannot be too many conditional statements
  • The function body cannot be too large
  • Function cannot be addressed

Inline is just a suggestion to the compiler, which may not be accepted by the compiler. If you do not declare the function as an inline function, the compiler may also compile the function inline. A good compiler will inline small, simple functions.

Default parameters for function

c + + can specify the default (default) parameter value for one or more parameters when declaring the function prototype. If this value is not specified when the function is called, the compiler will automatically replace it with the default value.

void TestFunc01(int a = 10, int b = 20){
	cout << "a + b  = " << a + b << endl;
}
//Note:
//1. If parameter b sets the default parameter value, then parameter c in the following position also needs to set the default parameter
void TestFunc02(int a,int b = 10,int c = 10){}
//2. If the function declaration is separated from the function definition, and the function declaration sets default parameters, the function definition cannot set default parameters
void TestFunc03(int a = 0,int b = 0);
void TestFunc03(int a, int b){}

int main(){
	//1. If no parameters are passed, the default parameters will be used
	TestFunc01();
	//2. If one parameter is passed, the second parameter uses the default parameter
	TestFunc01(100);
	//3. If two parameters are passed in, both parameters will use the parameters we passed in
	TestFunc01(100, 200);

	return EXIT_SUCCESS;
}

Note:

  • The default parameters of the function are from left to right. If a parameter is set with a default parameter, the parameters after this parameter must be set with a default parameter.

  • If the function declaration and function definition are written separately, the function declaration and function definition cannot set default parameters at the same time.

Placeholder parameter of function

When c + + declares a function, you can set the placeholder parameter. Placeholder parameters have only parameter type declarations and no parameter name declarations. In general, placeholder parameters cannot be used inside the function body.

void TestFunc01(int a,int b,int){
	//A placeholder parameter cannot be used inside a function
	cout << "a + b = " << a + b << endl;
}
//The occupancy parameter can also be set to the default value
void TestFunc02(int a, int b, int = 20){
	//The placeholder parameter cannot be used inside the function
	cout << "a + b = " << a + b << endl;
}
int main(){

	//Error call. The placeholder parameter is also a parameter and must be passed
	//TestFunc01(10,20); 
	//Call correctly
	TestFunc01(10,20,30);
	//Call correctly
	TestFunc02(10,20);
	//Call correctly
	TestFunc02(10, 20, 30);

	return EXIT_SUCCESS;
}

Function overload

Function overloading overview

Can make the name easy to use, is an important feature of any programming language!

In our real life, we often encounter some words with different meanings in different scenes, such as the polyphonic word "Zhong" in Chinese.

When we say, "he's so heavy, I can't carry it!" According to the context, we know that "heavy" means weight here and now.

If we say, "why do you write so much duplicate code? It's too bad to maintain!" In this place, we know that "heavy" means repetition.

The same word has different meanings in different scenes. Then there is a similar phenomenon in c + +. The same function name can have different meanings in different scenarios.

In the traditional c language, the function name must be unique, and the function with the same name is not allowed in the program. In c + +, functions with the same name are allowed. This phenomenon is called function overloading.

The purpose of function overloading is to use the function name conveniently.

Function overloading is not complicated. When you finish learning, you will understand when you need them and how to compile and link them.

function overloading

Basic syntax of function overloading

Conditions for realizing function overloading:

  1. Same scope

  2. Different number of parameters

  3. Different parameter types

  4. The order of parameters is different

//1. Function overload condition
namespace A{
	void MyFunc(){ cout << "No parameters!" << endl; }
	void MyFunc(int a){ cout << "a: " << a << endl; }
	void MyFunc(string b){ cout << "b: " << b << endl; }
	void MyFunc(int a, string b){ cout << "a: " << a << " b:" << b << endl;}
    void MyFunc(string b, int a){cout << "a: " << a << " b:" << b << endl;}
}
//2. The return value is not used as the basis for function overloading
namespace B{
	void MyFunc(string b, int a){}
	//int MyFunc(string b, int a) {} / / functions distinguished only by return value cannot be overloaded
}

Note: function overloading and default parameters are used together, so we need to pay extra attention to ambiguity.

void MyFunc(string b){
	cout << "b: " << b << endl;
}
//Function overload hits default parameter
void MyFunc(string b, int a = 10){
	cout << "a: " << a << " b:" << b << endl;
}
int main(){
	MyFunc("hello"); //At this time, both functions can be matched and called, resulting in ambiguity
	return 0;
}

Think: why is the return value of a function not an overload condition?

When the compiler can determine the of the unique function from the context, such as int ret = func(), this is certainly no problem. However, we can ignore its return value in the process of writing the program. At this time, a function is void func(int x); The other is int func(int x); When we call func(10) directly, the compiler is not sure which function to call. Therefore, it is forbidden to use return value as overload condition in c + +.

Implementation principle of function overloading

In order to realize function overloading, the compiler also does some behind the scenes work for us by default. The compiler modifies different function names with different parameter types, such as void func(); The compiler may modify the function name to_ Func, when the compiler encounters void func(int x), the compiler may modify the function name to_ func_int, when the compiler encounters void func(int x,char c), the compiler may modify the function name to_ func_int_char I use the word "possible" here because there is no unified standard for how compilers modify overloaded function names, so different compilers may produce different internal names.

void func(){}
void func(int x){}
void func(int x,char y){}

The compiled function names generated by the above three functions under linux are:

_Z4funcv //v stands for void, no parameters
_Z4funci //i means the parameter is of type int
_Z4funcic //i means that the first parameter is of type int and the second parameter is of type char

Analysis of extern "C"

The following are tested under Linux:

c function: void MyFunc**(){} ,**Compiled into functions: MyFunc

c++function: void MyFunc**(){},**Compiled into functions: _Z6Myfuncv

Through this test, because c++ needs to support function overloading, C and c++ have different function names after compiling the same function, which leads to a problem. If c++ calls a function written in C language, C + + will find and link the function according to the name modification of c++. Then there will be a link error. In the example, the MyFunc function is called in c++, and Z6Myfuncv will be searched in the link stage. The result is not found, because the MyFunc function is written in C language, and the symbol generated is MyFunc.

So what if I want to call c functions in c + +?

The main function of extern "C" is to realize that c + + code can call other c language code. After adding extern "C", this part of the code compiler compiles and links in the way of c language, rather than in the way of c + +.

MyModule.h

#ifndef MYMODULE_H
#define MYMODULE_H

#include<stdio.h>

#if __cplusplus
extern "C"{
#endif

	void func1();
	int func2(int a,int b);

#if __cplusplus
}
#endif

#endif

MyModule.c

#include"MyModule.h"

void func1(){
	printf("hello world!");
}
int func2(int a, int b){
	return a + b;
}

TestExternC.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

#if 0

	#ifdef __cplusplus
	extern "C" {
		#if 0
			void func1();
			int func2(int a, int b);
		#else
			#include"MyModule.h"
		#endif
	}

	#endif

#else

	extern "C" void func1();
	extern "C" int func2(int a, int b);

#endif

int main(){
	func1();
	cout << func2(10, 20) << endl;
	return EXIT_SUCCESS;
}

Topics: C++