2022's wish: resist the pressure of C generation.

This article has written that as a person who is used to Python, there are some uncomfortable places and differences in some details in learning C + +.

Of course, the most internal difficulties are related to the basis of C + +. Various writing methods of the same thing and mistakes that are easy to make accidentally are listed. Some simple things such as how to write if and what is while are omitted.

If you don't know how to use the pointer, or if you are distressed by the array, pointer and reference of function parameters, I'm confident to explain it clearly in this article. In addition, the later knowledge will be interspersed with the review of the previous difficulties.

1, Basic part

There are some side dishes in the front, and some related contents of python will be omitted later.  

1. Output stream

We all know that in C + +, float type has 7 significant digits and double type has 15-16 significant digits. But for this kind of decimal output, without any setting, we will only see 6 significant digits, that is, the significant digits before and after the decimal point add up to 6, 4 digits before the decimal point, and 2 digits after the decimal point.

cout << 3.1415926535 << endl; // 3.14159

cout << 31.415926535 << endl; // 31.4159

cout << 314159.26535 << endl; // 3114159

If there are more than 6 digits before the decimal point, it will be expressed by scientific counting method.

cout<< 3141592.6535 <<endl;// 3.14159e+06

Python is much better for this

print(3.1415926535897) # 3.1415926535897

That day, a younger student asked me a C language question. Although it was an old question, I still had to have a long memory.

2. Type and size

We all know that most languages, such as int type, take up 4 bytes, while python's int type takes up 24 + 4 bytes, and the other 24 come from its internal encapsulation (we'll talk about list array later). Of course, you are like the data types in some scientific computing libraries of anaconda. In order to calculate efficiently, MinGW is given to you. There are clearly big and small.

cout << sizeof(100) << endl; //4
a = int()
print(a.__sizeof__()) # 24

b = int(100) # I.e. b=100
print(b.__size0f__()) # 28

3. Division

For calculation, who is free to take 0 as divisor or modulus will report errors in both languages.

For C + +, integer divided by integer or integer, integer / floating point divided by floating point may be integer or floating point;

cout<< 3/2 << endl;      // 1
cout<< 5/2.5 << endl;    // 2
cout<< 5/2.2 << endl;    // 2.27273

cout<< 0.5/0.25 <<endl;  // 2
cout<< 0.5/0.22 <<endl;  // 2.27273

cout<<0.5/5<<endl;        // 0.1

Even if you use your own defined variables to receive

    float a1 = 5 / 2.5;
    int a2 = 5 / 2.5;
    float d1 = 2.5 / 5;
    int d2 = 2.5 / 5;
    cout << a1 << endl; // 2
    cout << a2 << endl; // 2
    cout << d1 << endl; // 0.5
    cout << d2 << endl; // 0
float a = 0.5f //double float

For Python, note that it comes after Python 3. 2 or 3 / 2 = 1. For integer division, Python is represented by / /.

print(3 / 2)  # 1.5
print(5 / 2.5)  # 2.0
print(0.5 / 0.25)  # 2.0
print(0.5 / 0.22)  # 2.272727272727273

3. Characters and strings

For c + +, use single quotation marks for characters and double quotation marks for strings. In fact, single, double and three quotation marks in python are OK. I thought this was enough. Interestingly, when learning ES6 of JS, I also learned that backquotes can also be used to wrap strings.

char a = 'Ha';
char name1[] = "Uh, uh";  //   Double quotation mark
string name2 = "Hello"; //   #include <string>  #include "string"
cout << name1<< "," <<name2 << endl;

4. For tabs

cout << "aaa\tbbbbb" << endl;
cout << "a\tbbbbb" << endl;
cout << "aaaaaa\tbbbbb" << endl;

/*
aaa     bbbbb
a       bbbbb
aaaaaa  bbbbb
*/
print("aaa\tbbbbb")
print("a\tbbbbb")
print("aaaaaa\tbbbbb")

'''
aaa	bbbbb
a	bbbbb
aaaaaa	bbbbb
'''

No surprise, eight and four.

I'm even more surprised that what I see on the computer side is different from that on the mobile side (from iPhone 13 users).

5. For input

We enter a space, which means that we have entered the value.  

First, if the type you define to receive does not match the type you enter, it will not receive.

int d;
float e;
cout<<d<<", "<<e<<endl;
cout<<"Please enter an integer and a floating point:"<<endl; //Too much input will affect the next time
cin>>d>>e;
cout<<d<<", "<<e<<endl;

string f;
cout<<"Enter a string:"<<endl;
cin>>f;
cout<<"ok, "<<f<<endl; //If there is a space, don't continue

/*
0, 9.80909e-44
 Please enter an integer and a floating point:
aa bb 123 33
0, 9.80909e-44
 Enter a string:
ok, 
*/

If you input more than you receive, the first time is OK, but the extra part will affect the second output.

int d;
float e;
cout<<"Please enter an integer and a floating point:"<<endl; //Too much input will affect the next time
cin>>d>>e;
cout<<d<<", "<<e<<endl;

string f;
cout<<"Enter a string:"<<endl;
cin>>f;
cout<<"ok, "<<f<<endl; //If there is a space, don't continue

/*
Please enter an integer and a floating point:
33 5.5 aaa
33, 5.5
 Enter a string:
ok, aaa
*/
// Obviously, I printed out aaa before entering it for the second time

So don't embarrass yourself.  

For Python, it doesn't matter what you enter. It can be processed by subsequent split, or map split can receive multiple values at the beginning.

a = input('Quick input:')
print(a)

'''
Quick input: asd qwe2.1m 3 \n xxx
 asd qwe2.1m 3 \n xxx
'''

6. Cast type

The premise is that it can be converted, "11" can be converted into number 11, "abc" cannot be converted into number. Cast type conversion can include type conversion and binary conversion.

C + + adds (type) before the conversion

python is direct, such as int (element). If you don't know about binary conversion, you can check it. It's not difficult.

7. About ASSIC code

char a='a';
cout << (int) a << endl; // a  
cout<< (char)65 <<endl;  // A
print(ord('a'), chr(97)) # 97 a

8. For Boolean operations

In C + + language, true returns 1 and false returns 0. The truth of python may return the values of 1, 2, 3, 4 and so on.

int g1 = 0 && 3; // 0
int g2 = 2 && 9; // 1
int h1 = 0||5; // 1
int h2 = 5||0; // 1
int h3 = 0||0; // 0
int t = 0||NULL; // 0
print(2 and 4)  # 4
print(3 and 0)  # 0
print(8 or 2)  # 8
print(0 or None)  # None

Interesting little cover up:

print('False' and '0')

# The result returns 0, but this 0 is the string 0, the formula is true, and the strings on both sides of and are not empty.

print(type('False' and '0')) # class str

9. Ternary operator

int a = 20, b = 30, c;
c = a > b ? a : b;
cout << c << endl; //30
a = 10 if 10>20 else 20
print(a) # 20

I feel bad that C + + can't write derivation like python.

a = [i for i in range(5)]
print(a)  # [0, 1, 2, 3, 4]

Exchange

a = 1
b = 2
a,b=b,a
print(a,b) # 2 1

10. Circulation

C + + does not support for else and while else.

python does not support switch and does not have do while. There is no goto, but it can be imported from a third-party library.

*11. C + + array and Py list

C + +: each element in the array is of the same data type and consists of consecutive memory locations.

Py: elements can be of any type, and the address of each element can be stored in continuous memory.

For C + +:

// Three ways to define one-dimensional array
    int arr1[5]; //Assignment: arr1[0]=1 arr1[0]=2 arr1[0]=3 arr1[0]=4 arr1[0]=5
    int arr2[5] = {1,2,3,4,5}; 
    int arr3[] = {1,2,3,4,5};
// Type array name [length] = {value}  
/*
For the first type, the direct output result without assignment is relatively random, which may be 0 or other numbers.
For the second, if the definition length is greater than the assigned value, the exceeding range is 0
 The third method automatically defines the length according to the assigned value.
In addition, if arr1[6],arr2[99],arr3[999] are out of range, the result is random.
*/

Array name: 1. It can count the length of elements in memory; 2. It can the first address of the array in memory.

    int arr1[5]; //Assignment: arr1[0]=1 arr1[1]=2 arr1[2]=3 arr1[3]=4 arr1[4]=5
    int arr2[8] = {1,2,3,4,5};
    int arr3[] = {1,2,3,4,5};
    cout<< sizeof(arr1)<< " "<< sizeof(arr2)<<' '<<  sizeof(arr3)<<endl;
    cout<<"arr2 The number of array elements is: "<< sizeof(arr2)/ sizeof(int)<<endl;

    cout<< arr1<< " "<< arr2<<' '<<  arr3<<endl;
    cout<< &arr1[0]<< " "<< &arr2[0]<<' '<<  &arr3[0]<<endl;
/*
20 32 20
arr2 Number of array elements: 8
0x61fe00 0x61fde0 0x61fdc0
0x61fe00 0x61fde0 0x61fdc0
*/

For Python:

lst = [1, 2, 3.5, 'nihao', ['ha-ha'], {"xx": "oo"}]
print(lst) # [1, 2, 3.5, 'nihao', ['ha ha'], {'xx': 'oo'}]
# It is relatively free and can be written freely. Print (list name) refers to all contents in the list.
# By the way, py is out of range and will report an error if it is different from C + +

We know that the output address in C + + is hexadecimal, while in Python it is hexadecimal.

a = [1, 2, 3.5, 'nihao', ['ha-ha'], {"xx": "oo"}]
print(id(1)) # 140724378797744
print(id(a), id(a[0]))  # 1590331192512 140724378797744

C + + array requires uniform element types, such as int, which accounts for 4 bytes. Then its next element, such as a[0] and a[1], is 4 bytes short. It also has a specified length and is still a continuous space, so it is more basic.

In Python, various types are encapsulated in the form of objects, and the list can store multiple elements, which makes the length uncertain. Therefore, python selects a continuous memory, which stores the addresses of elements in different regions. In fact, the list of Python also needs to specify the initial length. These internal C languages and help us complete it.

Python garbage collection mechanism_ suic009 blog - CSDN blog

C + + cannot slice directly.

C + + can't copy arrays (the same memory address) directly through the equals sign like Python's list. Different need lst copy(),tup.deepcopy().

*12. Two dimensional array

We won't talk about python. It's meaningless. Further research also involves data pool, deep and shallow copy, etc. Mainly look at C + +:

Definition method:

//Which one do you like and which one do you use
    int arr1[2][3];
    int arr2[2][3] = {{1, 2, 3},{4, 5, 6}};
    int arr3[2][3] = {1, 2, 3, 4, 5, 6};
    int arr4[][3] = {1, 2, 3, 4, 5, 6};
double arr2[2][3] = {{1, 2, 3},{4, 5, 6}};
cout<<arr2[0][0]<<' '<<arr2[0][1]<<endl; // The result is still 1 2

Array name:

arr[0] arr[0] [0] addresses are the same. arr[0] is actually a one-dimensional array.

cout<<arr3<<' '<< arr3[0] << ' '<<&arr3[0][0] <<endl; // 0x61fdc0 0x61fdc0 0x61fdc0
cout<<"The number of elements is:"<< sizeof(arr3)/ sizeof(int)<<endl; // 6
cout<<"The number of rows in the array is:"<< sizeof(arr3)/ sizeof(arr3[0])<<endl; // 2
cout<<"The number of array columns is:"<< sizeof(arr3[0])/ sizeof(arr3[0][0])<<endl; // 3

*13. Function

In C + +, you can understand this:

For return 13: we usually define variable assignment: int a = 13; This time, the return is obviously not limited by int, so you should tell it the return value type when declaring the function. Write void if you don't write return. Just remember not to receive it. Just remember to bring int, float, etc.

Note: it's not just int float void. It depends on your return value. If it is an object, it should be declared with a class.  

int foo(){
    float a = 1.12;
    return a;
}

int main() {
    cout<<foo()<<endl;
    return 0;

}
// Result output 1.12
// If you change float foo to int foo, 1 is output

Note:

  • Receive the return value of the function with whatever function is declared
  • The function declaration and the return value of the function should follow the method of declaring variables.

If I declare the return value of a function that I want to receive with an object, Person p = foo(); The foo should be declared as follows: person foo() {return P}. Want to declare pointer int *p = foo(); Then int * foo() {return & A}. I want to quote int & A = foo(); Then {int & foo(){return a}

Value transfer:

As you can see below, when the formal parameter changes, it will not affect the actual parameter. a and b above open up new memory storage 1 and 2

(the transfer of array pointers and so on will be later. Here we will look at the value transfer first.)   

void foo(int a,int b){
    cout<<&a<<' '<<&b<<endl;
    a = 100;
}

int main() {
    int a=1,b=2;
    cout<<&a<<' '<<&b<<endl;
    foo(a,b);
    cout<<a<<endl;
    return 0;
}

/*
0x61fe1c 0x61fe18
0x61fdf0 0x61fdf8
1
*/

In python, although the address is the same here, the current value will not change. I have links on the optimization knowledge involved. You can have a look if you are interested.

def foo1(a,b):
    print(id(a),id(b))
    a = 100

def foo2():
    a = 3
    b = 4
    print(id(a),id(b))
    foo1(a,b)
    print(a,b)

foo2()
'''
140724378797808 140724378797840
140724378797808 140724378797840
3 4
'''

Function declaration:

int foo(int a,int b);

Python does not require a function declaration.

def foo1():
    foo2()
    pass

# foo1() is executed from top to bottom. At this time, foo2() has not been seen and an error is reported.

def foo2():
    print('ok')
    pass

foo1() # ok

C + + sub file preparation:

C + + libraries are like Python libraries / modules / packages.  

So split files are like Python importing its own modules, which can write classes, functions, global variables, etc.

1. Create a suffix of h header file
 2. Create a suffix of cpp source file
 3. Write the function declaration in the header file
 4. Write the function definition in the source file

I wrote it here in clone, but it's actually the same in VS. I marked everything that should be added.

# foo.h 

# foo.cpp

In main CPP import just written

The operation results are as follows:

 

PS: Clone novice user's Guide. You can understand it by reading English.

*14. Pointer

Description, definition and use of pointers

As mentioned earlier, for a variable, such as int a =10; Then the memory space 0x0000 to 0x0004 is used to store it. In fact, we didn't know its address when we defined it, so we named it a. A's address &a is 0x0000.

But when we know its address or want to operate its address, we can do it with a pointer.

As you can see, just as variable a holds 10, our pointer variable p holds the address of a data.  

We define a variable p of type int * to store & A.  

To distinguish , p, * p, int *

be careful:

           int *p; In this way: int * is a data type (int *)p, which defines a pointer variable p, and the 64 bit operating system p occupies 8 bytes.

           int *p=&a; Look at it this way: (int *) (P = & A); That is, assign & A to variable p and declare it as a pointer variable with int *.

    cout<<sizeof(int *)<<endl;    // 8
    cout<<sizeof(float *)<<endl;  // 8
    cout<<sizeof(double *)<<endl; // 8

For * p, look at * and p separately. The p variable holds the address, * points to this address (that is, get the value of this address).

* p is the result of this address and the saved data.

        

If it is array arr, write int *p=arr; Instead of int * P = & arr; Although the ARR and & arr from cout are equal, the address of the address is obviously wrong, but you can write int * P = & arr [0].

int main() {
    int a = 10;
    int *p;
    p = &a;
    cout<<"a Your address is:"<<&a<<endl;
    cout<<"p The value of is:"<<p<<endl;
    cout<<"*p For:"<<*p<<endl;
}

/*
a The address of is 0x61fe14
p The value of is: 0x61fe14
*p Is: 10
*/

In order to deepen our understanding, we can also regard * p as a whole. In fact, * p is 10 at this time. We can:

    int c = *p;
    cout<<c<<endl; // 10

This step is to get it according to the address * in p. at this time, * p is 10, and 10 is assigned to c as a variable. It's not difficult.

After knowing the above, the pointer can be defined directly, so you can understand:

int *p = &a; // (int *)(p=&a);

However, it should be noted that this can only be written in the declaration, because int * and p = & A are separate. Never write * p = & a when you use it again or change the value later. Is this to change the value of * p to & A, that is, the address of a?

Looking back, we can see that p is equal to & A and * p is equal to A. We can certainly change the array of a memory with a. We * p and a point to the same memory, so the assignment modification of * p will also affect a. In addition, the first step of a we learned is actually the same as the * step of * p, which takes the address to get the value.

    int a = 10;
    int *p = &a;
    *p = 99;
    cout<<"a For:"<<a<<" *p For:"<<*p<<endl; // a is: 99 *p is: 99

int a tells the system to store a in a space of 4 bytes. int * makes p occupy 4 bytes in 32-bit (x86) operating system and 8 bytes in 64 bit (x64) operating system.

    int a = 10;
    int *p = &a;    
    cout<<sizeof(p)<<endl;   // 64 bit operating system, p accounting for 8 bytes
    cout<<sizeof(*p)<<endl;  // 10 is int type, accounting for 4 bytes

Null pointer

Pointer variable pointer to memory space number 0 is called null pointer. 0x0000.

Generally used to initialize pointer variables. Remember that the memory of null pointers is inaccessible.

    int *p = NULL;
    cout<<"p For:"<<p<<" *p For:"<<*p<<endl; // p is: 0 *p is:
// The output above is like that. I didn't write less
// Process finished with exit code -1073741819 (0xC0000005)
// Yes, of course

The number 0-255 is occupied by the system, and the access will be wrong.

Field pointer

We gave p the address int * P = & A. If we write this, we will report an error (except int *p=0x0000): int *p=0x1010. The program cannot be executed. This is to make P point to this hexadecimal number. If we cast: int *p=(int *)0x1010

    int *p = (int *)0x1100;
    cout<<"p For:"<<p<<" *p For:"<<*p<<"End of output"<<endl; // p is: 0 *p is:
    //p is: 0x1100 *p is:
    //Process finished with exit code -1073741819 (0xC0000005)

This scribbled address can't be accessed without applying for it.

const modifier pointer

  • const modifier pointer
  • const modifier constant
  • const modifies both pointers and constants

In fact, it's OK to see who const modifies. It's good to understand p, * p and int * above, and understand what is what.  

const modifier pointer. The value pointed to by the pointer cannot be changed, but the pointer can be changed.

    int a=10;
    int b = 20;
    const int *p = &a;
// At this point, * p=99 doesn't work, but p = & B can be emphasized again. Don't write it as * P = & B

const modifier constant: the value pointed to by the pointer can be changed, but the pointer cannot be changed.

    int a=10;
    int b = 20;
    int * const p = &a;  // int const *p = &a;  No error is reported, but it's better to int * together..
// At this time, p cannot be changed, that is, the pointer cannot be changed, p = & B is wrong, * p=99 is OK.

const modifies both pointers and constants

Then it can't change.

Pointers and arrays

If you understand the above knowledge, it will be easy to understand here

    int arr[5]={1,2,3,4,5};
    int *p=arr; // Int * P = & arr [0] is the same, but int * P = & arr is wrong 
    cout<<"The first data is:"<<*p<<endl; // The first data is: 1

Let's go through:

    for(int i=0;i<5;i++){
        cout<<"The first"<<i+1<<"The first data is:"<<*p<<endl;
        p+=1; // Move back four bytes
    }

/*
The first data is: 1
 The second data is: 2
 The third data is: 3
 The fourth data is: 4
 The fifth data is: 5
*/

Pointers and functions

As we know above, through value passing, the change of formal parameters cannot affect the actual parameters, because it opens up its own space.  

void swap(int a,int b ){
    int tem = a;
    a = b;
    b = tem;
    cout<<a<<' '<<b<<endl; // 2 1
}

int main() {
    int a = 1;
    int b =2;
    swap(a,b);
    cout<<a<<' '<<b<<endl; // 1 2
}

However, if the formal parameters and arguments share the same block address, the change of the formal parameters will inevitably lead to the change of the arguments because the directions of the variables are the same:

void swap(int *a,int *b ){ 
    int tem = *a;
    *a = *b;
    *b = tem;
    cout<<*a<<' '<<*b<<endl; // 2 1
}

int main() {
    int a = 1;
    int b =2;
    swap(&a,&b);
    cout<<a<<' '<<b<<endl; // 2 1
}

Note: this step is equivalent to: int * a = & A; Note: the two A's before and after are not the same thing. There are two spaces after the formal parameter. The address of argument a is stored in formal parameter a* A points to this value.

int *foo(){
    int a = 1;
    // int arr[5];
    return &a;
    // return arr;
}

This is correct but unnecessary. The stack area is cleaned up and can only be called once in main. The references learned later are also not recommended:

(stack area and reference are written later, no hurry)

Pointer functions and arrays

void details(int arr[]) { // *arr
    cout<<arr<<" "<<arr[0]<<endl; // 0x61fe00 1

}

int main() {
    int arr[5]={1,2,3,4,5};
    cout<<arr<<" "<<arr[0]<<endl; // 0x61fe00 1
    details(arr); // Remember not to write & arr here. After that, the array will write the name and the value will write & variable.
}

When the formal parameter receives the function, either write arr [] or * arr, which are the same. Writing arr will report an error. How can ordinary variables receive addresses.

Therefore, in C + +, the default values of int, float and other types are passed. If you want to change, you can pass in the address and receive it with a pointer. When you pass an array type, you have to receive it with a pointer, that is, it will change the original content.

In python

1. In the process of function parameter transfer, some basic data types, such as int (integer), float (floating point), str (string), etc., are value transfer. When modifying the data of the above data types within the function, the original value will not be changed.

2. For list, dict and tuple, the address is passed. The original data value will be changed when the function operates on the above data types.

Small case

Write a function function in C + + to realize bubble sorting. The array is required to change the original address rather than receiving the return value.

#include <iostream>
using namespace std;
void swap(int *a,int *b ){
    int tem = *a;
    *a = *b;
    *b = tem;
}

void bulleSort(int *arr,int length ){ // arr[]
    int flage;
    for(int i=0;i<length-1;i++){
        flage = true;
        for(int j=0;j<length-i-1;j++){
            if(arr[j]>arr[j+1]){
                swap(&arr[j],&arr[j+1]);
                flage = false;
            }
        }
        if(flage){
            return; // Bubble optimization, if there is no transposition, it will be terminated.
        }
    }
}

int main() {
    int arr[10] = {1,5,3,6,6,8,3,2,9,2};
    int length = sizeof(arr)/sizeof(int);
    bulleSort(arr,length); // &arr[0]
    for(int i=0;i<length;i++){
        cout<<arr[i]<<endl;
    }

}

15. Structure

Structure is a user-defined data type, which allows different data to be stored.

Syntax: struct structure name {structure member};

There are three ways to create variables through structures:

  • struct structure name variable name
  • struct structure name variable name = {member 1 value, member 2 value...}
  • Create variables as you define the structure.
struct Student{
    string name;
    int age;
    int score;
}s3;

int main() {
    // Method 1
    struct Student s1;
    s1.name = "Li Si";
    s1.age = 50;
    s1.score = 100;
    cout<<s1.name<<" "<<s1.age<<" "<<s1.score<<endl;
    
    // Method 2
    struct Student s2={"Zhang San",18,100};
    cout<<s2.name<<" "<<s2.age<<" "<<s2.score<<endl;

    // Method 3 is defined at the end above
    s3.name = "Wang Wu";
    s3.age = 10;
    s3.score = 20;
    cout<<s3.name<<" "<<s3.age<<" "<<s3.score<<endl;
}

Structure array

We just defined one student by one. In fact, we want to put the information of multiple students together,

Syntax: struct structure name array name [number] = {}, {}, {}...}

struct Student{
    string name;
    int age;
    int score;
};

int main() {

    struct Student arr[5]={
            {"Zhang San",18,100},
            {"Li Si",50,50},
            {"Wang Wu",12,30}
    }; //If the number of elements beyond the range is accessed, such as arr[3], the string will not be displayed, and the number is 0 
    arr[1].name = "Li Shishi";
    cout<<arr[1].name<<"The score is:"<<arr[1].score<<endl;
}

Structure pointer

Through the operator - > you can access the structure properties through the structure pointer, and so can the class objects learned later.

Remember that when declaring the structure pointer, it is (struct) structure name * P = & element / array name =, that is, declare the pointer variable of xx structure type.

int main() {
    struct Student s={"Zhang San",18,100}; // Student s = {"Zhang San", 18100};
    struct Student *p = &s; //  Student *p = &s;
    cout<<p->name<<"The score is:"<<p->score<<endl; // Zhang San's score is 100
}

If you write p.name, it will automatically convert you to p - > name. If you understand the pointer (* P) Score is OK.

Structure nesting

struct Student{
    string name;
    int age;
    int score;
};
struct Teacher{
    int id;
    string name;
    int age;
    struct Student stu;
};
int main() {
    struct Student s1={"Zhang San",18,95};
    struct Teacher t = {001,"Miss Li",80,s1};
    t.stu.name = "Zhang Sanwan";
    cout<<t.name<<"Students"<<t.stu.name<<"The score is:"<<t.stu.score<<endl; 
    // Mr. Li's student Zhang Sanwan's score is: 95
}
struct Student{
    string name;
    int age;
    int score;
};
struct Teacher{
    int id;
    string name;
    int age;
    struct Student arr[5];
};
int main() {
    struct Student s1={"Zhang San",18,95};
    struct Student s2={"Zhang Si",19,97};
    struct Student s3={"Zhang Wu",20,100};
    struct Teacher t = {001,"Miss Li",80,s1,s2,s3};
    for(int i=0;i<3;i++){
        cout<<t.name<<"Students"<<t.arr[i].name<<"The score is:"<<t.arr[i].score<<endl;
    }
    /*
        Miss Li's student Zhang San's score is 95
        Miss Li's student Zhang Si's score is 97
        Miss Li's student Zhang Wu's score is 100
     */
}

Of course, you can also assign a value to t.arr[i] or t.arr[i] through the for loop Assignment of name, etc.

struct Student{
    string name;
    int age;
    int score;
};
struct Class{
    int id;
    struct Student arr[5];
};
struct Teacher{
    int id;
    string name;
    int age;
    struct Class c;
};
int main() {
    
    struct Student s1={"Zhang San",18,95};
    struct Student s2={"Zhang Si",19,97};
    struct Student s3={"Zhang Wu",20,100};
    struct Class c1={001,s1,s2,s3};
    struct Teacher t = {001,"Miss Li",80,c1};
    for(int i=0;i<3;i++){
        cout<<t.name<<"of"<<t.c.id<<"class"<<"student"<<t.c.arr[i].name<<"The score is:"<<t.c.arr[i].score<<endl;
    }
    /*
        Miss Li's class 1 student Zhang San's score is 95
        Miss Li's class 1 student Zhang Si's score is 97
        Miss Li's class 1 student Zhang Wu's score is 100
     */
}

Generally, structure pointer is used to receive structure array:

struct Student{
    string name;
    int age;
    int score;
};
struct Class{
    int id;
    struct Student *stu;
};

int main() {
    struct Student s[3]={
            {"Zhang Wu",20,100},
            {"Zhang Si",19,97},
            {"Zhang Wu",20,100}
            };
    struct Class c1={001,s};
    for(int i=0;i<3;i++){
        cout<<c1.id<<"Class"<<"student"<<c1.stu[i].name<<"The score is:"<<c1.stu[i].score<<endl;
    }
    /*
        1 Zhang Wu, a student in class, got a score of 100
        1 Zhang Si, a student in class, scored 97
        1 Zhang Wu, a student in class, got a score of 100
     */
}

In this way, only the size of a pointer variable is opened, and the things stored in other people's home address can save space, isn't it.

Structure as function parameter

pass by value

struct Student{
    string name;
    int age;
    int score;
};
void details(Student stu){ // struct Student stu
    cout<< stu.name<<","<<stu.age<<","<<stu.score<<endl;
    stu.name="xxxxxx";
}

// pass by value
int main() {
    struct Student s1={"Zhang Wu",20,100};
    details(s1); // Zhang Wu, 20100
}

Address delivery saves space

void details(Student *stu){
    // You can only use arrows at this time
    cout<< stu->name<<","<<stu->age<<","<<stu->score<<endl;
    // cout << (*stu). name << "," << (*stu). age << "," << (*stu). score << endl;  That's OK
    stu->name="xxxxxx";
}

// Address delivery
int main() {
    struct Student s1={"Zhang Wu",20,100};
    details(&s1);
    cout<< s1.name<<","<<s1.age<<","<<s1.score<<endl; // xxxxxx,20,100
}

Address passing array (structure array is also an array, but the internal elements are different)

void details(Student *stu) { // The pointer points to the first of the array
    for (int i = 0; i < 3; i++) {
        // You can't use arrows at this time
        cout << stu[i].name << "," << stu[i].age << "," << stu[i].score << endl;
        stu[i].name = "xxxxxx";
    }
}

int main() {
    struct Student s[3] = {
            {"Zhang Wu", 20, 100},
            {"Zhang Si", 19, 97},
            {"Zhang Wu", 20, 100}
    };
    details(s); // Zhang Wu, 20100 emphasize again that although the values of S and & S cout are the same, you can't write & s here
    for(int i = 0; i < 3; i++) {
        cout << s[i].name << "," << s[i].age << "," << s[i].score << endl;
    }
}
/*
Zhang Wu, 20100
 Zhang Si, 19,97
 Zhang Wu, 20100
xxxxxx,20,100
xxxxxx,19,97
xxxxxx,20,100
*/

Structure as return value

struct Student {
	int id;
	string name;
};

Student foo() {
	Student s = { 99,"xx" };
	return s;
}

int main() {
	Student p = foo();
	cout << p.name << endl;
}

Pointer reference is OK if you like, but it's not necessary. It can only be used once without other declarations. Obviously, it's not good. I'll practice when I talk about class objects later. I'll skip it here first.  

const application scenario in structure

Changing the formal parameter in the function to a pointer can reduce the memory space and will not copy a new copy. (the pointer only takes up 8 bytes. If the parameter Student stu is received again, it is necessary to open up a space of the same size as the structure.) In this case, the so-called address passing, the parameter change will change the argument.

In order to prevent misoperation, a const can be added. That is, the const modifier pointer we talked about earlier (the value pointed to cannot be changed).

It's red at the sight.  

Of course, that's not right

Of course, that's all

You should understand what the const modifier pointer in the array modifies. The first two are the values corresponding to the address, and the last one is the address.  

2, C + + Advanced

1. Memory partition model

During the execution of C + + program, the general direction of memory is divided into four areas:

  • Code area: it stores the binary code of the function body and is managed by the operating system (before the program runs)
  • Global area: store global variables, static variables and constants (before the program runs)
  • Stack area: automatically allocated and released by the compiler to store the parameter values and local variables of the function (after the program runs)
  • Heap area: it is allocated and released by the programmer. If the programmer does not release it, it will be recycled by the operating system at the end of the program. (after the program runs)

The meaning of four memory areas: the data stored in different areas give us different life cycles and give us greater flexibility in programming.

Before running the program

After the program is compiled, an exe executable program is generated. Before the program is executed, it is divided into two areas:

Code area:

Store machine instructions executed by cpu

The code area is shared. The purpose is to have only one code in memory for the frequently executed program.

The code area is read-only because it prevents the program from accidentally modifying its instructions.

Global area:

Global and static variables are stored here.

The global area also contains a constant area, where string constants and other constants are also stored.

The data in this area is released by the operating system after the program is completed.

By printing the address, it can be found that the address of local variable and local constant are similar;

The addresses of global variables, global constants, static variables and string constants are similar.

After the program runs

Stack area:

  • It is automatically allocated and released by the compiler to store the parameter values and local variables of the function.
  • Note: do not return the address of local variables. The data opened up in the stack area is automatically released by the compiler

Note that the return address should be declared as a pointer type when declared.   

int* func() {
	int a = 10;
	return &a;
}

int main() {
	int* p = func();
	cout << *p << endl; //10
	cout << *p << endl; //2025425288
}

As we know above, the compiler automatically allocates and releases the stack data. Finally, we return the address of a. at this time, the func function ends and the data in it is released. When we go through * p to get it, the block of memory does not have access rights or access is out of code.

The reason why the first tender is more successful is that at this time, the compiler makes a reservation for us, fearing that we may misoperate. But not the second time. (can static int a = 10;)

Stacking area:

  • A programmer allocates the release. If the programmer does not release it, the operating system will recycle it after the program ends
  • In C + +, new is mainly used to open up memory in the heap

Note that the return address should be declared as a pointer type when declared.  

int* func() {
//New returns the pointer of this data type, so if the pointer is used to receive new, float * is used to receive it
	int *a = new int(10); 
	return a; // a stores the address of the heap data, which is returned to * p of the main function to receive.
}

int main() {
	int* p = func();  // The int *p=a here is equivalent to the previous int a=10; int *p=&a;
	cout << *p << endl; // 10
	cout << *p << endl; // 10
	delete p;
	cout << *p << endl; // Throw exception
}
int* func() {
	int *arr = new int[10]; //Array brackets
	arr[0] = 99;
	return arr; // arr is the first address of the array, which is returned to * p of the main function to receive.
}

int main() {
	int* p = func();
	cout << p[0] << endl; // 99
	cout << p[0] << endl; // 99
	delete[] p; // Free up all space in the array
}

In this way, there are many ways to define variables and arrays. int *arr = new int[10]; Where arr is not the array name, but also its address.

Here, the knowledge of new and delete will be used later in the destructor.  

2. Reference

int main() {
	int a = 10;
	int& b = a; // int * const b = &a;
	cout << b << endl; // 10
	b = 99;  // *b = 99;
	cout <<a<<" "<< b << endl; // 99 99
}

How to understand this? The address where 10 was stored was called a in the past, and it will also be called b in the future. That is, at this time, a and b are the names of variables, 10.  

Note that if int is written, B = & A; An error will be reported (how can you assign a hexadecimal number to a variable of type int?), Don't compare with the pointer I learned earlier. Int * b = & A; Confused.

int main() {
	int a = 10;
	int *b = &a;
	cout << b << endl; // 00AFF804
	*b = 99;
	cout <<a<<" "<< *b << endl; // 99 99
}
  1. (at the beginning of definition) references must be initialized. (define and const are the same, but const int &a can be used as function parameters)
  2. It cannot be changed after reference.
int main() {
	int a = 10,c=100;
	int &b = a;
	b = c; // It is equivalent to executing a=100 or b=100
	cout << b << endl; // 100 this is an assignment, not a reference change.
	b = 99;
	cout <<a<<" "<< b << endl; // 99 99
}

You can use a reference instead of a pointer to receive

void swap1(int& a, int& b) {
	int tem = a;
	a = b;
	b = tem;
}
void swap2(int* a, int* b) {
	int tem = *a;
	*a = *b;
	*b = tem;
}

int main() {
	int a = 3;
	int b = 5;
	swap1(a, b);
	cout <<a<<" "<< b << endl; // 5 3
	swap2(&a, &b);
	cout << a << " " << b << endl; // 3 5
}

Reference function return value

  1. Do not return a reference to a local variable
  2. Functions can be lvalues
int& foo1() {
	int a = 10; // The local variable stack area is released after this function is completed
	return a;
}
int& foo2() {
	static int a = 99; // Static variables are released after the global area program ends
	return a;
}
int main() {
	int& a = foo1();
	cout << a << endl; // 10 compiler reserved
	cout << a << endl; // 2038794632 memory has been released

	int& b = foo2();
	cout << b << endl; // 99
	cout << b << endl; // 99 
	foo2() = 10000; // In fact, the assignment is a=10000;
	cout << b << endl; // 10000 
}

Attention

int& foo2() {
	static int a = 99; // Static variables are released after the global area program ends
	return a;
}

For a function with a & declaration name like this, the returned content is still a, and the result of foo2() is still 99, but at this time, you can't use a variable of type int & to receive it, that is, int & B = foo2() is wrong, that is, int & B = 99 is wrong, but const int & B = foo2() is OK. So if I want to directly int & B = foo2(), I'll add &, or int b=foo2() is the simplest function to receive the return value.

const decoration to prevent misoperation

// int a = 99;
// int &b = a; // sure

// int &b = 5;  You can't write that
const int& b = 10; // Can not be modified
// const int *p = 5;  	 int* const p = 10;  may not
void foo1(const int &a) {  // Add const to prevent careless changes in future operations
	//Receiving with a instead of & A is equivalent to a = 1000; The formal parameter value was passed, not referenced
	cout << a << endl; 
}

int main() {
	int a = 10;
	foo1(a);
}

3. Advanced functions

Default parameters

If the function declaration has default parameters, the function implementation cannot have default parameters. There can only be one of them.

void foo1(int a = 1, int b = 2);

void foo1(int a=1,int b =2) {
	cout << a <<" "<<b << endl;
}

int main() {
	int a = 10,b=20;
	foo1();
}

be

Function placeholder parameter

// The booth parameters at the current stage are not available to us, and will be updated in the future.
// The placeholder parameter also has the default parameter void foo1(int a,int=10) {}
void foo1(int a,int) {
	cout << a << endl;
}

int main() {
	int a = 10,b=20;
	foo1(a,b); // At this time, two messages must be transmitted
}

function overloading

Function names can be the same to improve reusability

void foo(int a) {
	cout <<"Overloaded function 1  "<< a << endl;
}
void foo(int a,int b) {
	cout << "Overloaded function 2  " << a <<" "<< b << endl;
}
void foo(float a, float b) {
	cout << "Overloaded function 3  " << a << " " << b << endl;
}
void foo(int a, float b) {
	cout << "Overloaded function 4  " << a << " " << b << endl;
}
int main() {
	int a = 10,b=20;
	float c = 1.2, d = 3.14;
	foo(a);
	foo(a,b);
	foo(c,d);
	foo(a,d);
}

/*
Overloaded function 1 10
 Overloaded function 2 10 20
 Overloaded function 3 1.2 3.14
 Overloaded function 4 10 3.14
*/

Note: the return type and return value of a function cannot be used as an overload condition. Just look at the parameters.

Function overload considerations

  1. Quote as a note
  2. Function overload with default value

Quote as a note

void foo(int &a) {
	cout <<"Overloaded function 1  "<< a << endl;
}
void foo(const int &a) {
	cout << "Overloaded function 2  " << a << endl;
}
int main() {
	int a = 10;
	foo(a); // Overloaded function 1 10
}

In this case, function 1 will be followed, no matter who is in front of 1 and 2.

void foo(int &a) {
	cout <<"Overloaded function 1  "<< a << endl;
}
void foo(const int &a) {
	cout << "Overloaded function 2  " << a << endl;
}
int main() {
	int a = 10;
	foo(10); // Overloaded function 2 10
}

In this case, it is directly transmitted to 10 and function 2. Because it's illegal to go to function 1.

Function overload with default value

void foo(int a) {
	cout <<"Overloaded function 1  "<< a << endl;
}
void foo(int a=99) {
	cout << "Overloaded function 2  " << a << endl;
}
int main() {
	int a = 10;
	foo(a);
}

In this way, no error can be reported

void foo(int a, int b = 20) {
	cout <<"Overloaded function 1  "<< a << endl;
}
void foo(int a=99) {
	cout << "Overloaded function 2  " << a << endl;
}
int main() {
	int a = 10;
	foo(a);
    // foo(30,40) is OK
}

No, it's wrong, because you can go. Try to avoid putting.

4. Classes and objects

python object oriented_ suic009 blog - CSDN blog

Three characteristics of C + + object-oriented: encapsulation, inheritance and polymorphism.

Syntax class name {access rights: attribute / behavior};

encapsulation

Design a circle class to display the perimeter.

#include<iostream>
#include<string.h>
using namespace std;
#define PI 3.1415926

class Circle {
public:
	int r;
	float calculate() {
		return 2 * PI * r;
	}
};
int main() {
	Circle c;
	c.r = 1;
	cout << c.calculate() << endl;// 6.28319
}

Internal assignment / modification

Design a student class (small pit)

#include<iostream>
#include<string.h>
using namespace std;
#define PI 3.1415926

class Student {
public:
	int id;
	string name;
	void setS(int id1,string name1) { // Note that you can't write the same name here. C + + doesn't know, Py knows.
		id = id1;
		name = name1;
	}
	void show() {
		cout << id << " " << name << endl;
	}
};
int main() {
	Student s1;
	s1.id = 1;
	s1.name = "Zhang San";
	s1.show(); // 1 Zhang San

	Student s2;
	s2.setS(2, "Li Si"); // If name = id = name, the following code will be output
	s2.show(); // 2 Li Si
}

Public, private and protection rights

public: accessible within class, accessible outside class, accessible in subclass

private , accessible in class , not accessible outside class , not accessible in subclass

protected , accessible within the class , not accessible outside the class , accessible by subclasses

class and struct

  • class the default permission is private.
  • The default permission of struct is public permission.

Privatization of member properties

This is also necessary. We can control which can be read and which can be written by ourselves.

For some write permissions, we can verify them to prevent them from exceeding the valid range.  

include<iostream>
#include<string.h>
using namespace std;

class People {
	int id;		 // read-only
	string name; // Readable and writable
	int age;	 // Readable and writable
public:
	People(int i=000, string n="No real name", int a=18) { // Later, it will be written for fun.
		id = i;
		name = n;
		age = a;
	}
	void show() {
		cout << id <<" "<< name <<" "<< age << endl;
	}
	int show_id() {
		return id;
	}
	string show_name() {
		return name;
	}
	int show_age() {
		return age;
	}
	void set_name(string n) {
		name = n;
	}
	void set_age(int a) {
		if(a<0 || a>150){
			cout << "- - gun - -" << endl;
			return;
		}
		age = a;
	}
};
int main() {
	People p1(001,"Zhang San",20);
	People p2(002,"Li Si");
	p1.show();
	p2.set_age(200);
	p2.show();
}
/*
1 Zhang San 20
- - gun - -
2 Li Si 18
*/

C + + cannot print cout < < p < < endl directly;  

Print address "cout < < (int *) & P < < endl;  

Object initialization and cleanup

C + + uses constructors and destructors to solve the initialization and cleaning problems of objects.

Write your own compiler, even if you don't call them. There is also a copy constructor, which is also the default. In other words, there are three default constructors, destructors and copy constructors added by the C + + compiler itself.

Constructor

The main function is to assign values to the member attributes of the object when creating the object. The constructor is automatically called by the compiler without manual call.

  1. The constructor does not return a value and does not write void
  2. The function name is the same as the class name
  3. Constructors can have arguments, so overloading can occur
  4. When the program creates an object, it will automatically call the constructor without manual call, and it will only be called once

Destructor

The main function is to automatically call the system and perform some cleaning work before object destruction.

  1. The destructor does not return a value or write void
  2. The function name is the same as the class name, and the name is preceded by~
  3. Destructors cannot have parameters, so overloading cannot occur
  4. The program will automatically call the destructor before the object is destroyed. There is no need to call it manually, and it will only be called once
#include<iostream>
#include<string.h>
using namespace std;

class People {
public:
	People() {
		cout << "Hello" << endl;
	}
	~People()
	{
		cout << "bye-bye" << endl;
	}
};

// Construction and deconstruction must be implemented. If we don't write, the compiler will provide a construction and Deconstruction of empty implementation
int main() {
	People p1; // The data on the stack will be released after the main function is executed
}
/*
Hello
 bye-bye
*/

Classification and calling of constructors

Two classification methods:

  • According to parameters, it can be divided into parametric structure and nonparametric structure
  • By type, it can be divided into ordinary structure and copy structure
	People(const People &p) { // Reference receiving cannot change itself, so add const
		// This p is another human object
        num = p.num;
		cout << "copy constructor " << endl;
	}

Note: the constructor (copy) can receive parameters with people &p, and People p is wrong (do you write your own constructor to construct yourself?).

In other functions, People p can be value passing.

Call constructor

Method 1: Bracket Method

#include<iostream>
#include<string.h>
using namespace std;

class People {
public:
    int num;
	People() {
		cout << "Normal: nonparametric structure/Default construction" << endl;
	}
	People(int a) {
		cout << "General: parametric structure" << endl;
	}
	People(const People &p) { // You can't change it
		// This p is another human object
        num = p.num;
		cout << "copy constructor " << endl;
	}

	~People()
	{
		cout << "bye-bye" << endl;
	}
};


int main() {
	People p1;		// Default constructor 
	People p2(99);  // Parameterized constructor
	People p3(p2);	// Copy constructor at this time, p3's name, age, etc. are the same as p1
}
/*
Normal: parameterless construction / default construction
 General: parametric structure
 copy constructor 
bye-bye
 bye-bye
 bye-bye
*/

Note: do not add () when calling the default constructor. It's hard to change python when it's written too much.

Because of the following line of code, the compiler will consider it a function declaration.

People p();	// There will be no error or output object or initialization of the declared object

Method 2: display method:

int main() {
	People p1;				 // Default constructor 
	People p2 = People(99);  // Parameterized constructor
	People p3 = People(p2);	 // copy constructor 
}

be careful:

  •   People(99); Anonymous object after the current program ends, the system will immediately recycle the anonymous object.
  • Do not initialize anonymous objects with copy constructor. People(99) === People p3; Object redefinition.

Method 3: implicit conversion method:

int main() {
	People p1;			// Default constructor 
	People p2 = 99;		// Parameterized constructor
	People p3 = p2 ;	// copy constructor 
}

Three cases of copying constructor calls

In C + +, there are usually three situations when copying constructor calls

  • Initialize a new object with an already created object
  • Pass values to function parameters in the way of value transfer (People p receiving parameter value transfer is equivalent to assigning values to formal parameters; ps copying constructors in any form can only be received as people &p)
  • Return the local object by value (assign value when containing and receiving return p return value);

Case 1:

int main() {
    People p1;            // Default constructor 
    People p2(p1) ;      // copy constructor 
}

Case 2: the function is received with People p

#include<iostream>
#include<string.h>
using namespace std;

class People {
	
public:
	int num;
	People() {
		cout << "Normal: nonparametric structure/Default construction" << endl;
	}
	People(int a) {
		num = a;
		cout << "General: parametric structure" << endl;
	}
	People(const People &p) { 
		num = p.num;
		cout << "copy constructor " << endl;
	}

	~People()
	{
		cout << "bye-bye" << endl;
	}
};

void foo(People p) { // pass by value
	cout << p.num << endl;
	p.num = 0;
	cout << p.num << endl;
}

int main() {
	People p1(99);
	cout << p1.num << endl;
	foo(p1);
	cout << p1.num << endl;
}

/*
General: parametric structure
99
 copy constructor 
99
0
 bye-bye
99
 bye-bye
*/

Case 3: assignment when containing and receiving return p return value;

#include<iostream>
#include<string.h>
using namespace std;

class People {

public:
	int num;
	People() {
		cout << "Normal: nonparametric structure/Default construction" << endl;
	}
	People(int a) {
		num = a;
		cout << "General: parametric structure" << endl;
	}
	People(const People& p) {
		num = p.num;
		cout << "copy constructor " << endl;
	}
	~People()
	{
		cout << "bye-bye" << endl;
	}
};

People foo() { // Note that People declares functions more than int float
	People p(99);
	return p;
}

int main() {
	foo(); // Anonymous object direct release (direct destruct)
	cout << "ok" << endl;
	//cout << p.num << endl;
}
/*
General: parametric structure
 copy constructor 
bye-bye
 bye-bye
ok
*/

Copy constructor executed before release

#include<iostream>
#include<string.h>
using namespace std;

class People {
	
public:
	int num;
	People() {
		cout << "Normal: nonparametric structure/Default construction" << endl;
	}
	People(int a) {
		num = a;
		cout << "General: parametric structure" << endl;
	}
	People(const People &p) { 
		num = p.num;
		cout << "copy constructor " << endl;
	}
	~People()
	{
		cout << "bye-bye" << endl;
	}
};

People foo() { 
	People p(99);
	return p;
}

int main() {
	People p = foo(); // foo() first copies the constructor to generate an anonymous object
	cout << p.num << endl;
}

/*
General: parametric structure
 copy constructor 
bye-bye
99
 bye-bye
*/

People foo() { 
	People p(99);
	return p;
}

int main() {
	People p = foo(); // Direct release of anonymous objects
	cout << "ok" << endl;
	//cout << p.num << endl;
}
/*
General: parametric structure
 copy constructor 
bye-bye
ok
 bye-bye
*/

Don't rush to play again:

(value transfer above)

Reference return

People &foo() {
	People p(99);
	return p;
}

int main() {
	People &p = foo();
    // Of course, if People p = foo(); The copy constructor is called, which is the same as People foo() when declaring the function
	cout << p.num << endl;
}

/*
General: parametric structure
 bye-bye
99
*/

Pointer

People *foo() {
	People p(99);
	return &p;  // This can only &p show that the name of an object is an address, unlike an array
}

int main() {
	People *p = foo();
	cout << (*p).num << endl; // Parentheses should be added here, otherwise the error report will be executed in different order
    // cout << p->num << endl;  Normally, it should be written like this
}

/*
General: parametric structure
 bye-bye
99
*/

We can see that there are three cases for case 3:

  • The first: People p = foo(); Use p to receive an object P `, which is equivalent to People p = p `; Execute copy constructor.
  • Second: people &p = foo (); This is a reference, equivalent to people & P = P `; Alias this and do not execute the copy constructor.
  • Third: People *p = foo(); After all, there are many knowledge points involved. The above comments are written, and the copy constructor is not executed.

2. The three methods do not assign values between objects and do not execute copy constructors.

However, it should be noted that for cases 2 and 3 of case 3, I can only print them once, because I have said many times that they are cleaned up when they are used up in the stack area, the obtained address has not changed, and the value in it has disappeared.

People* foo() {
	People p(99);
	cout << (int*)&p << endl;
	cout << (int)&p << endl;
	return &p;
}

int main() {
	People* p = foo();
	cout << (int*)&*p << endl;
	cout << (int)&p << endl;
}

/*
General: parametric structure
00BBFAC8
12319432
 bye-bye
00BBFAC8
12319672
*/

Of course, you can also use new or static, but I also wrote it when learning pointers and references, which is not advocated.

So return an object structure or something, because it may need to be used. Don't be fussy. Just return a value directly. Pass it to the function as a parameter. You can consider pointer reference to save space. You can consider adding const to prevent it from changing.

If you think of all this, it means you didn't learn in vain.

Call rules for constructors

By default, the C + + compiler adds at least three functions to a class

Default constructor (no parameters, empty function body)
Default destructor (no parameters, empty function body)
The default copy function constructor copies the values of all attributes


Constructor calling rules are as follows:

  • If the user defines a parametric constructor, C + + no longer provides a default parameterless construct, but a default copy construct is provided
  • If you define a copy constructor, C + + will not provide another constructor
     

Namely:

If the user provides parameters, the compiler will not provide no parameters, but will provide copies;

If the user provides a copy, the compiler will not provide any constructor.

Deep copy and shallow copy

  • Shallow copy: a simple assignment copy operation.
  • Deep copy: reapply space in the heap area for copy operation.
#include<iostream>
using namespace std;
class Person
{
public:
	int m_Age;
	int* m_Height;

	Person()
	{
		cout << "Person Default constructor call for" << endl;
	}
	Person(int age,int height)
	{
		m_Height = new int(height); //Cut the height into the pile area
		m_Age = age;
		cout << "Person Parameterized constructor call" << endl;
	}
    Person(const Person& p)
	{
		cout << "Person Copy constructor call" << endl;
		m_Age = p.m_Age;
		m_Height = p.m_Height; //The compiler implements this line of code by default
		
	}
	~Person()
	{
		//Release the data opened up in the heap area
		if (m_Height !=NULL)
		{
			delete m_Height;
			m_Height = NULL;
		}
		cout << "Person Destructor call" << endl;
	}

};
void foo()
{
	Person p1(18,166);
	cout << p1.m_Age<<"  " << *p1.m_Height << endl;
	Person p2(p1);
	cout << p2.m_Age<<"  " <<*p2.m_Height<< endl;
}
int main(void)
{
	foo();
	return 0;
}

In this case, an error will be reported and the memory will be released repeatedly.

Solution: the shallow copy provided by the compiler doesn't work. We need to open up a new space for our own deep copy:

    Person(const Person& p)
	{
		cout << "Person Copy constructor call" << endl;
		m_Age = p.m_Age;
		//m_Height = p.m_Height; // The compiler implements this line of code by default
        m_Height = new int(*p.m_Height);
	}

Results:

/*
Person Parameterized constructor call
18  166
Person Copy constructor call
18  166
Person Destructor call
Person Destructor call
*/

That is, if an attribute is opened in the heap area, you must provide a copy constructor to open up a new space to prevent the problems caused by shallow copy when the destructor releases memory.

Initialization list

Just find out.  

effect:

C + + provides initialization list syntax to initialize objects.

Syntax:

Constructor (): Property 1 (value 1), property 2 (value 2)... {}

Example:

#include<iostream>
using namespace std;
class Person
{
public:
	//Traditional assignment operation
	/*Person(int a, int b, int c)
	{
		m_A = a;
		m_B = b;
		m_C = c;
	}*/
	//Initialization list initialization properties
	Person(int a,int b,int c) :m_A(a), m_B(b), m_C(c){}
	int m_A;
	int m_B;
	int m_C;
};
void test()
{
	//Person p(10,20,30);
	Person p(30,20,10);
	cout << p.m_A << endl;
	cout << p.m_B << endl;
	cout << p.m_C << endl;
}
int main(void)
{
	test();
	system("pause");
	return 0;

}

Class object as class member

A member of a class in C + + can be an object of another class, which we call an object member.

For example:
Class B has object A as A member and A as an object member.

Then, when creating A pair of B, A is constructed first. When the objects of other classes are members of this class, the objects of other classes are constructed first, and then itself.

Destructors are the opposite of constructors. The destructor of its own class is performed first, and then that of other classes.

#include<iostream>
#include<string.h>
using namespace std;
class Student{
public:
	int id;
	string name;
	Student() {};
	Student(int i, string n) {
		id = i;
		name = n;
	}
};
class Teacher {
public:
	int id;
	string name;
	Student s;
	Teacher(int i, string n, Student stu) {
		id = i;
		name = n;
		s = stu;
	}

};

int main(void)
{
	Student s(001, "Zhang San");
	Teacher t(001, "Miss Li", s);
	cout << t.s.name << endl; // Zhang San
	return 0;
}

Teacher(int i, string n, Student stu) requires students to have a default parameterless constructor. Because I wrote parameterless constructor, the system will no longer provide parameterless constructor, so I need to add one manually. Of course, if Teacher(int i, string n, Student stu = Student(007, "Zhang San sss")) makes a default, it's not necessary.

#include<iostream>
#include<string.h>
using namespace std;
class Student{
public:
	int id;
	string name;
	Student() {};
	Student(int i, string n) {
		id = i;
		name = n;
	}
};
class Teacher {
public:
	int id;
	string name;
	Student s;
	Teacher(int i, string n, Student stu = Student (001, "Zhang San")) { 
		id = i;
		name = n;
		s = stu;
	}

};

int main(void)
{
	Student s(001, "Zhang San");
	Teacher t1(001, "Miss Li", s);
	Teacher t2(001, "Miss Wang");
	cout << t1.s.name << endl; // Zhang San
	cout << t2.s.name << endl; // Zhang San
	return 0;
}

At this time, when Teacher(int i, string n, Student stu = Student (001, "Zhang San") {receives, Student stu(001, "Zhang San") cannot be written, but Student stu = Student (001, "Zhang San").

Of course, you can also use implicit conversion, such as assigning a string to an object to instantiate it.

Static member

Static members are called static members by adding the keyword static in front of member variables and member functions.

Static members are divided into:

  • Static member variable
    • All objects share the same data
    • Allocate memory at compile time
    • In class declaration, out of class initialization
  • Static member function
    • All object members share the same function
    • Static member functions can only access static member variables, and non static members cannot.
class Person {
public:
	static void func() {
		cout << "ststic function call" << endl;
	}
};

int main(void)
{
    // Access by object
	Person p; 
	p.func(); // ststic function call

    // Access through class
	Person::func();  // ststic function call 
	// Access by class name indicates that it does not belong to an object. Refer to Python class members.
	return 0;
}

For static member variables, declaration inside the class and initialization outside the class. Non static can be initialized in class;

#include<iostream>
#include<string.h>
using namespace std;
class Person {
public:
	int b = 20; // It is equivalent to the default value. If you are willing to copy the structure, reassign it, and change it if you are willing to change it.
	static int a;
    // static int a = 100;  Float red
	static void func() {
		a = 99;
	}
};
int Person::a=20; // If it is not written, the following p.a causes an error.
int main(void)
{
	Person p; 
	cout << p.b << endl; // 1
	return 0;
}

Static member functions access non static member variables

class Person {
public:
	static int a; // Inside class declaration and outside class initialization
	static void func() {
		// x = 0;  Non static member variables cannot be accessed
		a = 99; // Static member functions can access static member variables
		cout << "ststic function call" << endl;
	}
	void xx() {
		a = 33;
	}
};
int Person:: a = 100;// Inside class declaration and outside class initialization
int main(void)
{
	Person p; 
	cout << p.a << endl; // 100
	p.func(); 
	cout << p.a << endl; // 99
	p.xx();
	cout << p.a << endl; // 33
	return 0;
}

Why can static member functions only access static member variables and non static members cannot?

As we know above, the static method is a class member, and non static members can only be assigned or called when instantiating objects.

If you have P1 and P2 objects, call this static function and let him change the non static member variables. He doesn't know whose non static variables to change. Person::func();   p1.func(); p2.func();

Of course, if the static member function is private, it cannot be accessed outside the class.

C + + object model

In C + +, member variables and member functions in a class are stored separately,

Only non static member variables belong to the object of the class.

(only the size of non static member variables is included in the size of the class, and nothing else.)

The size of C + + empty object is 1 to distinguish the occupied positions of different classes in memory.

Each empty object should also have a unique byte.  

#include<iostream>
#include<string.h>
using namespace std;
class Person {

};
int main(void)
{
	Person p; 
	cout << sizeof(p) << endl; // 1
	return 0;
}

When there are only non static variables

class Person {
	int a;
	float b;
};
int main(void)
{
	Person p; 
	cout << sizeof(p) << endl; // 4+4=8
	return 0;
}

When there are only non static functions

class Person {
	void func() {
		int a = 99;
	}
	int func1() {
		int b = 20;
		return 10;
	}
};
int main(void)
{
	Person p; 
	cout << sizeof(p) << endl; // 1
	return 0;
}

Have static members

class Person {
	static int a;

	static void func() {
		a = 99;
	}
};
int Person::a=20;
int main(void)
{
	Person p; 
	cout << sizeof(p) << endl; // 1
	return 0;
}

That is, in C + +, the member variables and member functions in the class are stored separately, and only non static member variables belong to the object of the class.

this pointer


As we know above, C + + member variables and member functions are stored separately

Each non static member function will only produce a function instance, that is, multiple objects of the same type will share a piece of code.

So the question is: how does this piece of code distinguish which object calls itself?

C + + solves the above problems by providing special object pointer and this pointer. (the this pointer here can refer to JavaScript.)

The this pointer points to the object to which the called member function belongs. This points to whoever adjusted it.

this pointer is a pointer implicit in each non static member function.

Purpose of this pointer

When a formal parameter has the same name as a member variable, it can be distinguished by this pointer (which is why I say that it can't assign the same name as python)
Return the object itself in the non static member function of the class. You can use return *this
Resolve name conflicts

#include<iostream>
#include<string.h>
using namespace std;
class Person {
public:
	int age;
	Person(int age) {
		this->age = age;
	}

	Person(const Person &p) {
		age = p.age;
		cout << "copy constructor " << endl;
	}

	Person func() {
		return *this;
	}
};

int main(void)
{
	Person p(99); // Person p cannot be written here; The default constructor is no longer available.
	cout << p.age << endl; // 99

	Person p1 = p.func(); // Review: the copy constructor is actually called here
	cout << p1.age << endl; // 99
	return 0;
}

 cout << this  << endl; Is an address * this is the called object

	Person(int age) {
		this->age = age;
		// *this->age = age;  Float red
	}

The essence of this pointer is a pointer constant, and the pointer cannot be modified;

Review: copy constructor (see which step is called)

	Person func() {
		return *this;
	}
};

int main(void)
{
	Person p(99); 
	cout << p.age << endl; // 99
	p.func();  // Call the copy constructor to generate the anonymous object here and release it directly
	return 0;
}
	Person& func() {
		return *this;
	}
};

int main(void)
{
	Person p(99); 
	cout << p.age << endl; 
	p.func();  // Do not call copy constructor
	return 0;
}
	Person& func() {
		return *this;
	}
};

int main(void)
{
	Person p(99); 
	cout << p.age << endl; 

	Person p1 = p.func(); 
	cout << p1.age << endl;  // Calling the copy constructor does not receive with & matching the function at this time
	return 0;
}
	Person& func() {
		return *this;
	}
};

int main(void)
{
	Person p(99);
	cout << p.age << endl;

	Person &p1 = p.func();
	cout << p1.age << endl;  // Do not call copy constructor
	return 0;
}
	Person func() {
		return *this;
	}
};

int main(void)
{
	Person p(99); 
	cout << p.age << endl; 

	Person p1 = p.func();  // The copy constructor is called only once when the anonymous object is assigned to p1
	cout << p1.age << endl; 
	return 0;
}

Null pointer calls member function

#include<iostream>
using namespace std;
class Person
{
public:
	void ShowClassName()
	{
		cout << "this is Person class" << endl;
	}
	void ShowPersonAge()
	{
	
		//Improve the robustness, and return empty ones directly to prevent code crash
		if (this == NULL)
		{
			return;
		}
		//The reason for the error is that the passed in pointer is NULL - it is made out of nothing. Use a NULL pointer to access the properties inside 
		cout << this->m_Age << endl;
	}
	int m_Age;
};
void test()
{
	Person* p = NULL;
	p->ShowClassName();
	p->ShowPersonAge();
}
int main(void)
{
	test();
	system("pause");
	return 0;
}

const modifier member function

Constant function:

  • After adding const to the member function, we call this function a constant function
  • Constant functions cannot modify member properties
  • After the keyword mutable is added to the member attribute declaration, it can still be modified in the constant function

Constant object:

  • const calls an object a constant object before declaring it.
  • A constant object can only call a constant function.
#include<iostream>
using namespace std;

class Person{
public:
	//The essence of this pointer is a pointer constant, and the pointer cannot be modified
	//It is equivalent to Person *const this;
	//Add const after the member function to modify the point of this, and the value pointed by the pointer cannot be modified
	void showPerson() const//Adding const is not allowed to modify const Person *const this;
	{
		this->m_b = 100;
		//this = NULL;tbhis pointer cannot be modified
	}
	int m_a;
	mutable int m_b;//For special variables modified with mutable, this value can be modified even in constant functions and objects

	void func()
	{
		m_a = 100;//It can be modified in ordinary member functions
	}
};
void test()
{
	Person P;
	P.showPerson();
}
//Constant object
void test1()
{
	const Person p;//Add const before the object to become a constant object
	//p.m_a = 100;   Unalterable
	p.m_b = 100;
	//Constant objects can only call constant functions 
	p.showPerson();
	//p.func(); Constant objects cannot call ordinary member functions because ordinary member functions can modify properties.
	
}
int main(void)
{
	test();
	system("pause");
	return 0;
}

Friends

In the program, some private properties also want to be accessed by some special functions or classes outside the class, so friend technology needs to be used.

The purpose of a friend is to let a function or class access private elements in another class.

friend keyword

Three implementations of friends

  • Global function as friend
  • Class as friend
  • Member function as friend

Global function as friend

#include<iostream>
#include<string>
using namespace std;
class Building 
{
	//The goodgay global function is a good friend of the Building class and can access private members.
	friend void goodgay(Building* building);
public:
	Building()
	{
		m_SittingRoom = "a living room";
		m_BedRoom = "bedroom";
	}
public:
	string m_SittingRoom;
private:
	string m_BedRoom;
};

//Global function
void goodgay(Building* building) // Isn't this to save space
{
	cout << "Good friends global function is accessing your" << building->m_SittingRoom << endl;
	
	cout << "Good friends global function is accessing your" << building->m_BedRoom << endl;
}
void test()
{
	Building building;
	goodgay(&building);
}
int main(void)
{
	test();
	system("pause");
	return 0;
}

Class as friend

#include<iostream>
#include<string>
using namespace std;
//Let's make a statement first
class Building;

class GoodGay
{
public:
	GoodGay();
public:
	void visit();//Visit functions to access properties in Building
	Building* building;
};


class Building
{
	//GoodGay is a good friend of the Building class and can access its private properties
	friend class GoodGay;
public:
	Building();
public:
	string m_SittingRoom;
private:
	string m_BedRoom;
};
//Write member functions outside the class
Building::Building()
{
	m_SittingRoom = "a living room";
	m_BedRoom = "bedroom";
}
GoodGay::GoodGay()
{
	//Create a Building object
	building = new Building;
}
void GoodGay::visit()
{
	cout << "Good friends are visiting your" << building->m_SittingRoom << endl;
	cout << "Good friends are visiting your" << building->m_BedRoom << endl;
}

void test()
{
	GoodGay gy;
	gy.visit();
}
int main(void)
{
	test();
	system("pause");
	return 0;
}

Member function as friend

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

class Building;
class GoodGay
{
public:
	GoodGay();
	void visit();//You can access private members in Building
	void visit1();//Private members in Building cannot be accessed
	Building* builidng;	
};
class Building
{
	//Tell the compiler that the visit member function in GoodGay class, as a good friend of this class, can access private functions
	friend void GoodGay::visit();
public:
	Building(); 
public:
	string m_SittingRoom;
private:
	string m_BedRoom;
};

Building::Building()
{
	m_SittingRoom = "a living room";
	m_BedRoom = "bedroom";
}

GoodGay::GoodGay()
{

	builidng = new Building;
}
void GoodGay::visit()
{
	cout << "visit Visiting" << builidng->m_SittingRoom << endl;
	cout << "visit Visiting" << builidng->m_BedRoom << endl;
}
void GoodGay::visit1()
{
	cout << "visit1 Visiting" << builidng->m_SittingRoom << endl;

}
void test()
{
	GoodGay gg;
	gg.visit();
	gg.visit1();
}
int main(void)
{
	test();
	system("pause");
	return 0;
}

inherit

class Person {
	// Parent class
};
class boy :public Person {
	// Subclass
};

public: accessible within class, accessible outside class, accessible in subclass

private , accessible in class , not accessible outside class , not accessible in subclass

protected , accessible within the class , not accessible outside the class , accessible by subclasses

All non static member properties in the parent class will be inherited by the child class.

The private member attributes in the parent class are hidden by the compiler, so they cannot be accessed, but they are indeed inherited

The order of construction and Deconstruction in inheritance

After the subclass inherits from the parent class, the constructor of the parent class will also be called when the subclass is created.

In inheritance, the constructor of the parent class is called first, and then the constructor of the child class is called. The deconstruction order is opposite to that of the construction.

Static members with the same name are handled in the same way as non static members, except that there are two ways to access them (through object and class names).  

Access with the same name: parent plus scope

//How members with the same name are handled
void test01()
{
	Son son;
	cout <<son.m_A<< endl;
	//If you want to access a member with the same name in the parent class through a subclass object, you need to add a scope.
	cout <<son.Base::m_A<< endl;
}
//Processing method of member function with the same name
void test02()
{
	Son son1; 
	son1.func();//son
	son1.Base::func();//father
	//If a member function with the same name as the parent class appears in the subclass
	//A member with the same name in a subclass hides all member functions with the same name in the parent class
	//If you want to access the member function with the same name hidden in the parent class, you need to add scope
	son1.Base::func(10);
}
int main(void)
{
	test02();
	system("pause");
	return 0;
}

polymorphic

There are two kinds of polymorphism

  • Static polymorphism: function overloading and operator overloading belong to static polymorphism and reuse function names
  • Dynamic polymorphism: derived classes and virtual functions implement runtime polymorphism

The difference between static polymorphism and dynamic polymorphism

  • Statically polymorphic function address early binding - the function address is determined in the compilation stage
  • Dynamic polymorphic function address late binding - determine the function address at the run-time
#include<iostream>
using namespace std;
class Animal
{
public:
	//In addition, virtual becomes a virtual function to realize late address binding
	virtual void speak()
	{
		cout << "Animals are talking" << endl;
	}
};

class Cat :public Animal
{
public:
	void speak()
	{
		cout << "The kitten is talking" << endl;
	}
};

class Dog : public Animal
{
public:
	void speak()
	{
		cout << "The dog is talking" << endl;
	}
};

//The address is bound early, and the function address is determined at the compilation stage
//If you want the cat to talk, the address of this function cannot be bound in advance and needs to be bound at the runtime

//Dynamic multiple satisfying conditions
/*
1.There is an inheritance relationship
2.Subclass overrides the virtual function of the parent class
*/
//Rewriting requirements: the return value type of the function is exactly the same as the parameter list of the function name 
//Use of dynamic polymorphism
/*
The pointer or reference of the parent class points to the object of the child class / / Animal & Animal = cat;
*/

void doSpeak(Animal& animal)//Animal &animal = cat;
{
	animal.speak();
}
void test01()
{
	Cat cat;
	doSpeak(cat);
	Dog dog;
	doSpeak(dog);
}
int main(void)
{
	test01();
	system("pause");
	return 0;
}

If I don't add virtual, the result of cout is that animals are talking

principle

Let's print the size of the Animal instantiated object:

int main(void)
{
	Animal a;
	cout << sizeof(a) << endl; // 4
	return 0;
}

The output result is 4. Of course, if you choose x64 architecture, the result should be 8.

We know that the normal size of an empty object is 1, and here 4 or 8 is actually the size of a pointer. It has more than one vfptr, virtual function (table) pointer.

In short, when a subclass inherits the parent class, it inherits all of the parent class. Therefore, because the parent class writes virtual and has multiple vfptr pointers, the pointer points to itself when it inherits, such as cat and dog. Therefore, when void dospeak (Animal & animal) receives a cat or dog object, the pointer will point to itself instead of the parent animal.

Pure virtual functions and abstract classes


In polymorphism, the implementation of virtual functions in the parent class collection is meaningless, which is mainly the content rewritten by calling subclasses. Therefore, virtual functions can be changed to pure virtual functions.

Pure virtual function syntax: virtual return value type function name (parameter list) = 0;

When there are pure virtual functions in a class, this class is also called an abstract class.

Abstract class features:

  • Cannot instantiate object
  • Subclasses must override pure virtual functions in abstract classes, otherwise they also belong to abstract classes
     
#include<iostream>
using namespace std;
class Animal
{
public:
	//In addition, virtual becomes a virtual function to realize late address binding
	virtual void speak() = 0;
};

class Cat :public Animal
{
public:
	virtual void speak() // Add this
	{
		cout << "The kitten is talking" << endl;
	}
};

class Dog : public Animal
{
public:
	virtual void speak() // Add this
	{
		cout << "The dog is talking" << endl;
	}
};


void doSpeak(Animal& animal)
{
	animal.speak();
}
void test01()
{
	Cat cat;
	doSpeak(cat);
	Dog dog;
	doSpeak(dog);
}
int main(void)
{
	test01();
	return 0;
}

At this time, if anima executes; An error is reported. The abstract class cannot instantiate the object.

Virtual destruct and pure virtual destruct


Problem: when polymorphism is used, if an attribute in a subclass is opened to the heap, the parent class pointer cannot call the destructor code of the subclass when it is released

Solution: change the destructor in the parent class to virtual destructor or pure virtual destructor

Virtual and pure deconstruction:

  • It can solve the problem of releasing subclass objects from parent class pointers,
  • All need to have a specific function implementation

The difference between virtual deconstruction and pure fiction:

  • If it is a pure virtual destructor, this class belongs to an abstract class and cannot instantiate an object


Virtual deconstruction grammar;  

virtual ~ class name () {}
Pure virtual destructor syntax:

virtual ~ class name () = 0// statement
Class name:: ~ class name () {}

#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
	Animal()
	{
		cout << "Animal Constructor call for" << endl;
	}
	//Using virtual destructor can solve the problem that the parent class pointer releases the object unclearly
	/*virtual ~Animal()
	{
		cout << "Animal Destructor call "< < endl;
	}*/
	//Pure virtual destructors need to be declared and implemented
	//With pure virtual destructor, this class is also an abstract class and cannot instantiate objects	
	virtual ~Animal() = 0;

	virtual void speak() = 0;
};
//pure virtual destructor 
Animal::~Animal()
{
	cout << "Animal Pure destructor call" << endl;
}
class Cat :public Animal
{
public:
	Cat(string name)
	{
		m_Name = new string(name);
	}
	virtual void speak()
	{
		cout << "Cat Constructor call for" << endl;
		cout << *m_Name << "The kitten is talking" << endl;
	}
	~Cat()
	{
		if (m_Name != NULL)
		{
			cout << "Cat Destructor call" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}
	string* m_Name;
};
void test01()
{
	Animal* animal = new Cat("Tom");
	/*
	When the pointer of the parent class is destructed, the destructor in the child class will not be called,
	As a result, if subclasses have heap attributes, memory leaks will occur.
	Solution: change the destructor of the parent class to virtual destructor
	*/
    animal->speak();
	delete animal;  // You can't print this destructor
}
int main(void)
{
	test01();
	return 0;
}
/*
Animal Constructor call for
Cat Constructor call for
Tom The kitten is talking
Cat Destructor call
Animal Pure destructor call
*/

Here we have string * m in cats_ Name received the new string(name), so it needs to be released. Normally, the destructor of the subclass can go, but at this time, when the object is declared, the parent class pointer is in the heap area, and deleting it will not affect the subclass itself.

ps: the following virtual destructs can be released normally without writing

void test01()
{
	Cat c("Tom");
	Animal &animal = c; // This way of writing animal & Animal = cat ("Tom") reports an error
	animal.speak();
/*  
    Cat c("Tom");
	Animal* animal = &c; 
	animal->speak(); 
*/

}
int main(void)
{
	test01();
	return 0;
}

Question: Animal * animal = & Cat ("Tom"); Is that ok? Answer No.

There is this in this foreign subclass, and the parent class pointer cannot point to itself.  

Keywords: Python C++

Added by chadtimothy23 on Thu, 17 Feb 2022 02:39:38 +0200