C++ review notes - primer fifth edition

1. References and pointers

1. Quote

Generally, when initializing a variable, the initial value will be copied to the newly created object. However, when defining a reference, the program binds the reference to its initial value instead of copying the initial value to the reference. Once initialized, the reference remains bound to its initial value object. Because a reference cannot be rebound to another object, the reference must be initialized.
A reference is not an object. Instead, it is just another name for an existing object.
After defining a reference, all operations performed on it are performed on the object bound to it:

refval = 2;		// 把2赋给 refVal 指向的对象,此处即是赋给了ival
int ii = refVal;//与ii=ival执行结果一样

2.Pointer

​A pointer is a composite type that "points to" another type. Similar to references, pointers also provide indirect access to other objects. However, there are many differences between pointers and references. First, the pointer itself is an object, allowing assignment and copying of the pointer, and it can point to several different objects during the life cycle of the pointer. Second, pointers do not need to be assigned an initial value when they are defined. Like other built-in types, a pointer defined within a block scope will have an undefined value if it is not initialized.

2.1 Using pointers to access objects

If the pointer points to an object, the dereference operator (operator *) is allowed to access the object :

int ival = 42;
int *p = &ival;	//p存放着变量ival的地址,或者说p是指向变量ival的指针
cout << *p;		// 由符号*得到指针p所指的对象,输出 42

Dereferencing a pointer yields the object it refers to, so if you assign a value to the dereferenced result, you are actually assigning a value to the object pointed to by the pointer:

*p = 0;			// 由符号*得到指针p所指的对象,即可经由P为变量ival赋值
cout << *p;		//输出0如上述程序所示,为*p赋值实际上是为p所指的对象赋值。
<!--解引用操作仅适用于那些确实指向了某个对象的有效指针。-->

2.2 Changes in the value of the pointer or the value of the object pointed to by the pointer

​ Sometimes it is not easy to figure out whether an assignment statement changes the value of the pointer or the value of the object pointed to by the pointer. The best way is to remember that assignment always changes the object on the left side of the equal sign. When writing the following statement:

pi = &ival;  // pi 的值被改变,现在pi 指向了ival

​It means assigning a new value to pi, which means changing the address value stored in pi. On the contrary, if you write the following statement:

*pi = 0;		// ival的值被改变,指针pi并没有改变

​ Then *pi (that is, the object pointed to by the pointer pi) changes.

3. Assignment and pointers

Both pointers and references can provide indirect access to other objects. However, they are very different in specific implementation details. The most important point is that the reference itself is not an object . Once a reference is defined, it cannot be bound to another object . Every subsequent use of this reference accesses the object to which it was originally bound .
There is no such restriction between a pointer and the address it stores. Like any other variable (as long as it is not a reference),
assigning a value to a pointer means that it stores a new address and thus points to a new object :

int i =42;
int *pi =0;		//pi被初始化,但没有指向任何对象
int *pi2 = &i;	//pi2被初始化,存有i的地址
int *pi3;		//如果pi3定义于块内,则pi3的值是无法确定的

pi3= pi2;		//pi3和pi2指向同一个对象i
pi2 = 0;		//现在pi2不指向任何对象了

4. Reference to pointer

A reference itself is not an object, so a pointer to a reference cannot be defined. But pointers are objects, so there are
references to pointers:

int i=42;
int *pi ;		// p是一个int型指针
int *&r =pi;		//r是一个对指针p的引用

r = &i;			//r引用了一个指针,因此给上赋值&i就是令p指向i
*r =0;			//解引用r得到i,也就是p指向的对象,将i的值改为0

The easiest way to understand what the type of r is is to read the definition of r from right to left. The symbol closest to the variable name (in this case the symbol & for &r) has the most direct effect on the variable's type, so r is a reference. The rest of the declarator is used to determine what type r refers to. The symbol * in this example indicates that r refers to a pointer. Finally, the basic data type part of the declaration indicates that r refers to an int pointer.

2. String

1. How to initialize String object

string s1			//默认初始化,s1是一个字符串
string s2(s1)		//s2是s1的副本
string s2 == s1		//等价于上面
string s3("value")	//s3是字面值value的副本,除了最后一个空字符外
string s3 = "value"	//等价于上面
string s4(n,'c')	//n个连续的c组成的串

2. Operations on string objects

os<<s	//将s写到输出流os当中,返回os
is>>s	//从is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is, s)//从is中读取一行赋给s,返回is
s.empty()	//s为空返回true,否则返回false
s.size()	//返回s中字符的个数
s[n]		//返回s中第n个字符的引用,位置n从0计起
s1+s2	//返回s1和s2连接后的结果
sl=s2	//用s2的副本代替s1中原来的字符
s1==s2	//如果s1和s2中所含的字符完全一样,则它们相等;string对象的相
s1!=s2	//等性判断对字母的大小写敏感
<<=>>=	//利用字符在字典中的顺序进行比较,且对字母的大小写敏感

​ Chapter 1 has introduced the use of iostream in the standard library to read and write values ​​of built-in types such as int and double. Similarly, you can also use the IO operator to read and write string objects:

//注意:要想编译下面的代码还需要适当的#include语句和using声明
int main()
{
    
    
	string s;
    cin >> s;			//如果程序的输入是“     Hello World!     "
    cout << s << endl;	//则输出将是“Hello”,输出结果中没有任何空格。
    return 0;    
}

string s1, s2;		// 把第一个输入读到 s1 中,第二个输入读到 s2 中
cin >> s1 >> s2;	//输出两个 string对象
cout << s1 << s2 << endl;//输出HelloWorld! 

3. Use getline to read an entire line

​ Sometimes we want to retain the whitespace characters during input in the final string. In this case, we should use the getline function instead of the original >> operator. The parameters of the getline function are an input stream and a string object. The function reads content from the given input stream until it encounters a newline character (note that the newline character is also read in), and then stores the read content into Go to that string object (note that the newline character is not stored). getline ends the reading operation and returns the result as soon as it encounters a newline character, even if the input begins with a newline character. If the input really starts with a newline character, the result is an empty string.
Like the input operator, getline also returns its stream argument. So since the input operator can be used as a condition for judgment (Ctrl+D/Z), we can also use the result of getline as a condition. For example, you could rewrite your previous program so that it prints an entire line at a time, rather than one word per line:

int main()
{
    
    
	string line;	//每次读入一整行,直至到达文件末尾
	while (getline(cin, line))
		cout << line << endl;
	return 0;
}

Since line does not contain a newline character, we manually add the newline operator. As usual, use end1 to end the current line and flush the display buffer.

4. Add literal values ​​and strings

string s4 = s1 + ",";		//正确;把一个string对象和一个字面值相加
string s5 = "hello" + ",";	//错误;两个运算的对象都不是string
string s6 = s1 + "hello" + "," ;//正确;每个加法运算符都有一个string对象
string s7 = "hello" + "," + s1;	//错误;不能将两个字面值相加

5. Use a for loop to change the characters in the string

string s("Hello World!!!");//转换成大写形式。
for (auto &c :s)		// 对于s中的每个字符(注意:C是引用)
	c = toupper(c);		//c是一个引用,因此赋值语句将改变s中字符的值
	cout << s << endl;	

Performing iteration using subscripts
Another example is to capitalize the first word of s:


//依次处理s中的字符直至我们处理完全部字符或者遇到一个空白
for (decltype(s.size())index =0; index != s.size() && !isspace(s[index]); ++index)
	s[index] = toupper(s[index]);		//将当前字符改成大写形式
//index的类型是由 decltype关键字决定的,保证下标小于size()的值就可以了。

3. Vector

1. Define and initialize the vector object

vector<T> vl		//v1是一个空vector,它潜在的元素是T类型的,执行默认初始化
vector<T> v2(v1)	//v2中包含有v1所有元素的副本
vector<T> v2 = vl	//等价于v2(v1),v2中包含有v1所有元素的副本
vector<T> v3(n, val)//v3包含了n个重复的元素,每个元素的值都是val 
vector<T> v4(n)		//v4包含了n个重复地执行了值初始化的对象
vector<T> v5{
    
    a,b,c.}	//v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<T> v5 = {
    
    a,b, c.}	//等价于v5{a,b,c...}

4. Iterator

Operators for standard container iterators

*iter	返回迭代器iter所指元素的引用
iter->mem	解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem
++iter	令iter指示容器中的下一个元素
--iter  令iter指示容器中的上一个元素
iterl == iter2		判断两个迭代器是否相等(不相等),如果两个迭代器指示的是同一个元
iterl != iter2		素或者它们是同一个容器的尾后迭代器,则相等;反之,不相等

Similar to a pointer, the element it indicates can also be obtained by dereferencing an iterator. The iterator that performs dereference must be legal and indeed indicate an element. Attempting to dereference an illegal iterator or a trailing iterator is undefined behavior.

Read text text data:

// 依次输出 text 的每一行直至遇到第一个空白行为止
for (auto it = text.cbegin();
	it != text.cend() && !it->empty()++ it)
	cout << *it << endl;

Iterator operations

binary search method

auto mid = vi.begin() + vi.size()/2

5. Array

initialization

We can initialize such arrays with string literals. When using this method, be sure to note that there is a null character at the end of the string literal. This null character will also be copied to the character array like other characters in the string:

char al[l] = {
    
    'c','+'}		// 列表初始化,没有空字符
char a2[] = {
    
    'c','+','\0'}	//列表初始化,含有显式的空字符
char a3[] = "C++"			//自动添加表示字符串结束的空字符
const char a4[6] = "Daniel";	//错误:没有空间可存放空字符!

Although the string literal "Daniel" appears to be only 6 characters long, the size of the array must be at least 7, with 6 positions holding the contents of the literal and 1 position holding the terminating null character.

The contents of an array cannot be copied to other arrays as their initial values, nor can arrays be used to assign values ​​to other arrays:

int a[] = {
    
    1,2,3};	
int a2[] = a;	//err
a2 = a;			//err

Understand complex array declarations

int *ptrs[10];
int (*Parray)[10] = &arr;
int (&arrRef)[10] = arr;

By default, type modifiers are bound from right to left. For ptrs, it is relatively simple to understand its meaning from right to left (see Section 2.3.3, page 52): first know that we define an array of size 10, its name is ptrs, and then know that the array stores is a pointer to int.

But for Parray, it is not reasonable to understand from right to left. Because the dimensions of an array follow the declared name, it is much better to read from the inside out than from right to left in the case of arrays. The order from the inside out can help us better understand the meaning of Parray: first is the part enclosed by parentheses, *Parray means that Parray is a pointer, and then looking at the right side, we can know that Parray is a pointer to an array of size 10 , finally observe the left side and know that the elements in the array are ints. The final meaning is clear. Parray is a pointer, which points to an int array containing 10 elements. In the same way, (&arrRef) means that arrRef is a reference. The object it refers to is an array of size 10, and the type of the elements in the array is int.
*

Of course, there is no special limit on the number of modifiers:

int*(&arry)[10]-ptrs;// arry是数组的引用,该数组含有10个指针

Read the above statement in order from the inside out. First, you know that arry is a reference. Then, if you look at the right side, you will know that the object referenced by arry is an array of size 10. Finally, if you look at the left side, you will know that the element type of the array is a pointer to int. In this way, arry is a reference to an array containing 10 int pointers.
The best way to understand the meaning of an array declaration is to read it from the inside out, starting with the array name.

Arrays and pointers

The elements of the array are also objects. Use the subscript operator on the array to get the element at the specified position of the array. Therefore, like other objects, using the address operator on an array element will get a pointer to the element:

string nums[] =("one","two""three"}//数组的元素是string对象
string *p= &nums[0];				//p指向nums的第一个元素

//然而,数组还有一个特性:在很多用到数组名字的地方,编译器都会自动地将其替换为一个指向数组首元素的指针:
string *p2 = nums;//等价于p2 = &nums[0]
++p2;			//p2指向nums[2]

It can be seen from the above that in some cases, array operations are actually pointer operations. This conclusion has many hidden meanings. One meaning is that when using an array as the initial value of an auto variable, the inferred type is a pointer rather than an array ( avoidable by decltype ):

int ia[]=0,1,2,3,4,5,6,7,8,9}// ia是一个含有10个整数的数组
auto ia2(ia);					// ia2 是一个整型指针,指向ia的第一个元素
ia2 = 42;						// 错误:ia2是一个指针,不能用int 值给指针赋值

Pointers are also iterators

6. Function

1. Pass by reference and pass by value

Like other variables, the type of a formal parameter determines how the formal parameters and actual parameters interact. If the formal parameter is a reference type, it will be bound to the corresponding formal parameter; otherwise, the value of the actual parameter will be copied and assigned to the formal parameter. The formal parameter and the actual parameter are two independent objects.

2. Pass value parameters

pointer parameter

Pointers behave like other non-reference types. When a pointer copy operation is performed, the value of the pointer is copied. After copying, the two pointers are different pointers. Because a pointer allows us to access the object it points to indirectly, the value of the object it points to can be modified through a pointer:

int n = 0,i=42;
int *p= &n, *q= &i;	//p指向n;q指向主
*p=42;				//n的值改变;P不变
P=q;				//p现在指向了主;但是主和n的值都不变

Pointer parameters behave similarly:

//该函数接受一个指针,然后将指针所指的值置为0
void reset(int *ip)
{
    
    
    *ip=0;		//改变指针ip所指对象的值
    ip=0;		//只改变了ip的局部拷贝,实参未被改变
}

After calling the reset function, the edge pointed to by the actual parameter is set to 0, but the actual parameter itself does not change:

int i=42;
reset(&i);		//改变主的值而非土的地址
cout << "i" << i << endl;	//输出i=0
How to exchange the values ​​of two integers using pointer parameters
#include <iostream>
using namespace std;
int swap(int *a,int *b);
int main()
{
    
    
	int *p,*q;
	int min=10;
	int max=20;
	 p=&min;    
	 q=&max;    
	cout<<"交换前:a= "<<min<<",b= "<<max<<endl;
	swap(p,q);
	cout<<"交换后:a= "<<min<<",b= "<<max<<endl;
	return 0;
}
int swap(int *a,int *b)
{
    
    
	int c;
	 c=*a;
	*a=*b;
	*b=c;
}

[External link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-R6OUs0vx-1691733924503) (C:\Users\lijiale\AppData\Roaming\Typora\typora-user-images\ image-20230718110415925.png)]

p and q are the addresses of two numbers respectively. By taking the content of the address, the value of the number is obtained. When exchanging, the numbers pointed to by the pointers should also be exchanged. However, if it is written in the following form, it cannot be exchanged.

#include <iostream>
using namespace std;
int swap(int *a,int *b);
int main()
{
    
    
	int *p,*q;
	int min=10;
	int max=20;
	 p=&min;
	 q=&max;	
	cout<<"交换前:a= "<<min<<",b= "<<max<<endl;
	swap(p,q);
	cout<<"交换后:a= "<<min<<",b= "<<max<<endl;
	return 0;
}
int swap(int *a,int *b)
{
    
    
	int *c;
	 c=a;
	 a=b;
	 b=c;	 
}

This is because within the body of the swap function, only the values ​​of the two formal parameter pointers themselves are exchanged, and the actual parameters are not affected. At this time, if there is also a print statement in the formal parameter, you can clearly see that the pointer values ​​are indeed exchanged in the called function, but this is only valid in the local scope, and it will be invalid when returning to the main function after the call is completed.

3. Pass reference parameters

By using reference parameters, a function is allowed to change the value of one or more actual parameters.

Use references to avoid copies. It is best to use constant references when the function does not need to modify the value of the reference parameter.

bool isShorter(const string &s1,const string &s2)
{
	return s1.size() < s2.size();
}

Using formal parameters to implicitly return additional information

One way is to define a new data type that contains two members: position and quantity. There is another simpler method, we can pass an additional reference argument to the function to save the number of occurrences of the character:

//返回s中c第一次出现的位置索引
//引用形参occurs负责统计c出现的总次数
string::size _type find char(const string &s, char c,
							string::size _type &occurs)
{
    auto ret = s.size();	// 第一次出现的位置(如果有的话)
	occurs = 0;				//设置表示出现次数的形参的值
	for (decltype(ret) i = 0;!= S.size();++i) {
        if (s[il == c){
            if (ret ==  S.size())
				ret = i;	//记录c第一次出现的位置
            ++occurs;		//将出现的次数加1
        }
     }
return ret;			//出现次数通过occurs 隐式地返回
}

When we call the find_char function, we must pass in three actual parameters: a string object as the search range, the character to be found, and a size_type used to save the number of occurrences of the character.

4.Array parameters

int a[] = {0,1,2};	//含有3个整数的数组
int a2[]=a	;		//错误:不允许使用一个数组初始化另一个数组
a2 = a;				//错误:不能把一个数组直接赋值给另一个数组

So you cannot use array parameters by value.

But because arrays are converted into pointers, when we pass an array to a function, we actually pass a pointer to the first element of the array.

Although arrays cannot be passed by value, we can write formal parameters in an array-like form:

//尽管形式不同,但这三个print 函数是等价的
//每个函数都有一个const int*类型的形参
void print(const int*);	
void print(const int []);		//可以看出来,函数的意图是作用于一个数组
void print(const int[10]);		// 这里的维度表示我们期望数组含有多少元素,实际不一定
//尽管表现形式不同,但上面的三个函数是等价的:每个函数的唯一形参都是const int*类型的。当编译器处理对 print 函数的调用时,只检查传入的参数是否是const int*类型:
int i=0,j[2] ={0,1);
print(&i);					// 正确:&i的类型是int*
print(j);					// 正确:j转换成 int*并指向j[0]

If we pass an array to the print function, the actual parameter is automatically converted into a pointer to the first element of the array, and the size of the array has no effect on the function call.

5. Return array pointer

Because arrays cannot be copied, functions cannot return arrays. However, functions can return pointers or references to arrays (see 5. Complexity). Although syntactically speaking, it is cumbersome to define a function that returns a pointer or reference to an array, there are some ways to simplify this task, the most straightforward of which is to use type aliases:

typedef int arrT[10];// arrT是一个类型别名,它表示的类型是含有10个整数的数组
using arrT = int[10];// arrT的等价声明
arrT* func(int i);	//func返回一个指向含有10个整数的数组的指针

where arrT is an alias for an array of 10 integers. Since we cannot return an array, we define the return type as a pointer to an array. Therefore, the func function accepts an int argument and returns a pointer to an array containing 10 integers.

Declare a function that returns an array pointer

To declare func without using type aliases, we must keep in mind the dimensions of the array after the name being defined:

int arr[10];// arr是一个含有10个整数的数组
int *pl[10];//p1是一个含有10个指针的数组
int (*p2)[10] = &arr;//p2是一个指针,它指向含有10个整数的数组

As with these declarations, if we want to define a function that returns an array pointer, the dimensions of the array must follow the function name. However, the function's parameter list also follows the function name and the parameter list should precede the array dimensions. Therefore, the form of a function that returns an array pointer is as follows:

Type (*function (parameter _list) ) [dimension]
类似于其他数组的声明,Type 表示元素的类型,dimension 表示数组的大小。(*function(parameter _list))两端的括号必须存在,就像我们定义p2时两端必须有括号一样。如果没有这对括号,函数的返回类型将是指针的数组。
举个具体点的例子,下面这个func函数的声明没有使用类型别名:
int (*func(int i))[10];
可以按照以下的顺序来逐层理解该声明的含义:
func(int i)表示调用func函数时需要一个int类型的实参。
(*func(int i))意味着我们可以对函数调用的结果执行解引用操作。
(*func (int i))[10]表示解引用func的调用将得到一个大小是10的数组。
int (*func (int i))[10] 表示数组中的元素时int类型

Improvement: trailing return types

auto func(int i) -> int(*)[10];

6. Function overloading

Two functions are not allowed to have the same everything except their return type.

7. Category

If a const member function returns *this in the form of a reference, its return type will be a constant reference.

declaration, Type represents the type of element, and dimension represents the size of the array. (*function(parameter _list)) The brackets at both ends must exist, just like there must be brackets at both ends when we define p2. Without these parentheses, the return type of the function would be an array of pointers.
To give a specific example, the following func function declaration does not use a type alias:
int (*func(int i))[10];
You can understand the meaning of this declaration layer by layer in the following order:
func(int i) Indicates that an actual parameter of type int is required when calling the func function.
(*func(int i)) means that we can perform a dereference operation on the result of the function call.
(*func (int i))[10] means that the call to dereference func will get an array of size 10.
int (*func (int i))[10] represents the int type of the elements in the array


改进:尾置返回类型

```c++
auto func(int i) -> int(*)[10];

6. Function overloading

Two functions are not allowed to have the same everything except their return type.

If a const member function returns *this in the form of a reference, its return type will be a constant reference.

Because non-const versions of functions are not available for constant objects, const member functions can only be called on a constant object.

other

Rvalue references rvalue references

unnecessary copying = steal

rvalue:

//int实验
a + b = 42;//err
//string实验
string s1("Hello");
string s2("World");
s1 + s2 = s2;//pass,s1 = Hello,s2 = World
string() = "World";//pass 临时对象也是右值
//complex实验
complex<int> c1(3,8),c2(1,0);
c1 + c2 = complex<int>(4,9); //pass,c1:(3,8),c2:(1,0)
complex<int>() = complex<int>(4,9);//pass

No operations can be performed on rvalues:
Insert image description here

foo()返回的是值(int),在对右值进行取地址操作&foo(),是不允许的

usage:

对于insert操作,提供了两个函数:
1.copy
insert(...,&x)	//x是普通的值
2.move
insert(...,&&x) //x是右值(临时对象),此时取右值引用

Since the vector contains Mystring, class Mystring has two constructors: move is a shallow copy.
Insert image description here

Deep copy and shallow copy:

Insert image description here

Memory management

Insert image description here
Insert image description here
Insert image description here
Insert image description here

Variadic Templates

Example 1. Variable template parameters (recursive call)

Insert image description here

max function ratio size

Insert image description here

Handle print function

Example 2. Recursive inheritance

What is processed is type (type), using class template

[External link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-TEmUDRpJ-1691734666845) (C:\Users\lijiale\AppData\Roaming\Typora\typora-user-images\ image-20230713113744487.png)]

Example 3. Recursive compounding

[External link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-N4VzmZjn-1691734666846) (C:\Users\lijiale\AppData\Roaming\Typora\typora-user-images\ image-20230713115717968.png)]

Guess you like

Origin blog.csdn.net/Strive_LiJiaLe/article/details/132229951