C + + Summary - grammar

Static keyword

1. Static global variable

Static modifies a global variable to make it a static global variable. This variable is stored in the static storage area and can only be used in this file. Therefore, other files can also define variables with the same name without conflict.

2. Static local variable

It refers to static modifying local variables. The scope is still a local scope. It is stored in the global data area of memory and will not disappear until the program runs, but it is only visible in the function that defines it and initialized only once. If no initial value is assigned, it will automatically be 0.

3. Static function

When static modifies a function, the function becomes a static function. It can only be seen in the file declaring it and cannot be used by other files. Functions with the same name can be defined in other files without conflict.

4. Static data member

static modifies the data member of a class. characteristic:

  1. For ordinary data members, each class instance will have its own copy. For static data members, it does not belong to any class of objects. No matter how many objects are generated by the class, there is only one static data member in the program, which is shared and accessed by all objects of the class, that is, all objects operate on the same static data member.
  2. Static data members need to allocate space when defining, so they cannot be defined in class declarations. Static data members must be declared inside the class and initialized outside the class. Because static data members do not belong to any object, they only belong to classes. Therefore, all operations on static data members (except declarations) must use the class name as the scope. And static data members follow public, protected and private access rules like ordinary data members.
  3. Static data members are stored in the global data area, but static member variables do not occupy the size of the class. They are created and initialized at compile time

5. Static member function

  • Refers to the member function of static modifier class.

  • Like static data members, static member functions do not belong to any object. They serve the whole class, and all objects of this class share the same function. Therefore, it is different from ordinary member functions. Because the ordinary member function is a specific object belonging to a class, the ordinary member function will have a hidden this pointer to operate the resources owned by the object. However, static member functions do not belong to any object. Therefore, static member functions do not have this pointer, and they cannot access any non static member functions and non static data members belonging to class objects. That is, static member functions can only access static member functions and static data members. The method of use is as follows:
    1. Using object access
    2. Access using the class name. Static member functions, like static data members, must be declared inside the class and defined outside the class, and cannot be defined with the static keyword.

  • One reason for using a static member function is that it can be used to process static data members before creating any object, which is not implemented by ordinary member functions.

  • Static member functions cannot be declared const because static members are not part of any object

  • Static member functions cannot be declared as virtual functions

quote

  • The reference must be initialized, and once defined, the reference cannot change the point. The reference must refer to legal memory space.
    But const int & ref = 10 can be compiled because the compiler explains: int temp = 10; const int &ref = temp;
    The difference between pointer and reference
  • The essence of reference: pointer constant (pointing to non modifiable, content modifiable)

The difference between reference and pointer

  • A pointer is an entity and a reference is an alias for a block of memory.
  • Autoincrement operators have different meanings: pointer + + refers to address autoincrement, and reference + + refers to value autoincrement.
  • The pointer can change the point, and the reference can only be initialized once at definition time, and then immutable
  • Reference cannot be null, pointer can be null
  • Sizeof (Reference) gets the size of the pointed variable, while sizeof (pointer) gets the size of the pointer itself, generally accounting for 4 bytes.
  • References cannot be used with const (i.e. no & const), but pointers can (i.e. * const)
  • Pointers can have multiple levels of pointers, while references have only one level
  • Try not to return the reference of the memory allocated by new inside the function, which is easy to cause memory leakage, as shown in the following example:
string& foo() {
    string* ptr = new string("123");
    return *ptr;
}
string temp = foo();  //The memory generated by new cannot be released 
//The above sentence can be handled as follows, but it will be troublesome
string& tmp = foo();
string str = tmp;
delete &tmp;

Lvalue, rvalue, lvalue reference, rvalue reference

Lvalue: a variable that can appear either to the left or to the right of the equal sign. That is, the lvalue is an addressable variable with persistence and can be modified
Right value: can only appear in variables to the right of the equal sign. That is, the right value is generally a non addressable constant or an unnamed temporary object created during the evaluation of an expression, which is transient. The right value cannot be modified
Lvalue reference: refers to an object;
Right value reference: it refers to the reference that must be bound to the right value. In C++11, the right value reference can realize "mobile semantics" and obtain the right value reference through & &.
One of the functions of R-value reference: move semantics (std::move), which is used to reduce the development of temporary resources

class Person {
public:
	//Default constructor 
	Person() {
		m_Age = 0;
		m_Height = nullptr;
	}

	//copy constructor 
	Person(const Person& p) {
		m_Age = p.m_Age;
		m_Height = new int(*p.m_Height);  //Deep copy to prevent shallow copy
		//Cout < < the address of the copy constructor application is: < < m_ Height << endl;
		cout << "Copy constructor called" << endl;
	}

	//move constructor 
	Person(Person&& p) noexcept {
		m_Age = p.m_Age;
		m_Height = p.m_Height;
		p.m_Height = nullptr;
		cout << "The move constructor was called" << endl;
	}

	//Assignment Operators 
	Person& operator=(Person& p) {
		m_Age = p.m_Age;
		m_Height = new int(*p.m_Height);
		//Cout < < the address of the assignment operator application is: < < m_ Height << endl;
		return *this;
	}
	//Move assignment operator
	Person& operator=(Person&& p) noexcept {
		//First, self-test and release their own resources
		if (this != &p) {
			delete m_Height;
		}
		
		m_Age = p.m_Age;			//Take over p's resources
		m_Height = p.m_Height;

		p.m_Height = nullptr;      //Empty p's resource
	}

	//Parameterized constructor
	Person(int age, int height) {
		m_Age = age;
		m_Height = new int(height);
		//Cout < < the address of the parameterized constructor application is: < < m_ Height << endl;
		cout << "A parameterized constructor was called" << endl;
	}

	//Destructor
	~Person() {
		if (m_Height != nullptr) {
			delete m_Height;
			m_Height = nullptr;
		}
	}

	int m_Age;
	int* m_Height;
};


int main(){
	//Traditional lvalue reference
    int a = 10;
    int& b = a;  // Define an lvalue reference variable
    b = 20;      // Modify the value of reference memory through lvalue reference

    //The following line of code cannot be compiled because the number to the right of the equal sign cannot get the address
    int &var = 10;  

    //The above line of code can be changed into the following constant references for the reasons mentioned above
    const int& var = 10; 
    
    //However, changing to constant reference cannot modify the value of var, so you need to use right value reference to solve the problem
    //The following line of code can be compiled
    int&& var = 10;

    //And the right value reference can also change the value
    var = 1; 

	vector<Person> vec;
	Person p1(20, 160);

	vec.push_back(p1);			//p1 will call the assignment constructor when passing in the parameter, which will be copied once, and then destroyed immediately
	vec.push_back(std::move(p1));	//p1 will be converted to an R-value, so the move constructor will be called instead of the copy constructor,
									//Improve efficiency	
 }

The second function of R-value reference: perfect forwarding
Perfect forwarding means that the function template can "Perfectly" forward its parameters to other functions called internally. The so-called perfection means that it can not only accurately forward the value of the parameter, but also ensure that the left and right value attributes of the forwarded parameter remain unchanged.
Look at the following code

template<typename T>
void f(T&& x){ 
    cout << ++x; 
}
f(2); // 3

The function f accepts an R-value x, but when the statement f(2) passes 2 in, it can increment 2 automatically. This means that the right value refers to the left value itself, that is, the left value of X. Then there will be problems when passing parameters. Look at the following code

template<typename T>
void g(T&& x) {
    cout << "Right value" << endl;
}

template<typename T>
void g(T& x) {
    cout << "Lvalue" << endl;
}

template<typename T>
void f(T&& x) {
    cout << "f rvalue reference " << endl;
    g(x);
}
template<typename T>
void f(T& x) {
    cout << "f lvalue reference " << endl;
    g(x);
}

The results are as follows:

There are three functions in total. The f function calls the g function. The g function is to output the left value if the incoming parameter is a left value, and the right value if the incoming parameter is a right value. However, since the parameters of the g function are passed in through the f function (that is, X is first passed in through the external f function, and then passed to g by the f function), X has become an lvalue after the first parameter transfer (which can be compared with the first example), g(x) will always only output lvalues (that is, it will always match the second function template), which obviously does not match the result we want. Then we can write:

template<typename T>
void f(T&& x) {
    cout << "f rvalue reference " << endl;
    g(std::forward<T>(x));
}
template<typename T>
void f(T& x) {
    cout << "f lvalue reference " << endl;
    g(std::forward<T>(x));
}

At this time, the output results are as follows:

In line with our expectations. The std::forward function maintains the reference type of x.
Then, in combination with mobile semantics, imagine an example: you need to assign a value (or copy) to the parameter passed in by the function, but if you do not maintain the nature of the parameter, although you pass in an R-value outside in order to reduce the overhead, it will become an l-value as soon as you enter the function. for instance:
Back to the previous example of mobile semantics, the previous code remains unchanged, and we add a function

void fun(Person&& p) {
	Person p2(p);
}
int main(int argc, char* argv[])
{
	Person p1(20, 160);
	fun(std::move(p1));
}

The results are as follows:

We constructed the P1 object in the main function. The original meaning of the fun() function is to use the move constructor to reduce the number of copies. However, we can see that although we used the right value in the main function, the copy constructor is still used in the fun function. We make the following modifications:

Person p2(std::forward<Person>(p));

The results are as follows:

We can see that it meets our expectations

new delete and malloc free

Same point: both can apply for and release space from the heap
difference:

  1. new and delete are operators, while malloc is a function
  2. Malloc is only responsible for opening up space. new not only has the function of malloc, but also can initialize data
  3. malloc failed to open up memory and returned nullptr pointer; new throws bad_alloc type exception
  4. Free will only free space, while delete will first call the destructor of the class to free space.

Keywords: C++ Back-end

Added by pedrobcabral on Tue, 11 Jan 2022 15:27:47 +0200