CMU-15-445——P0

本身是实现一个Trie树,本身难度不大,主要是很久没写C++了,重拾之后,发现了很多有意思的东西。

什么是lvalue和rvalue?

lvalue就是能取到地址的变量。而rvalue是临时变量。例如i是lvalue,而99是rvalue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

void f(int& i) { std::cout << "lvalue ref: " << i << "\n"; }
void f(int&& i) { std::cout << "rvalue ref: " << i << "\n"; }

int main()
{
int i = 77;
f(i); // lvalue ref called
f(99); // rvalue ref called

f(std::move(i)); // rvalue ref called

return 0;
}

再看个例子:getValue函数的返回值是rvalue

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

int getValue ()
{
int ii = 10;
return ii;
}

int main()
{
std::cout << getValue();
return 0;
}
1
2
const int& val = getValue(); // OK
int& val = getValue(); // NOT OK

而现在可以通过rvalue引用(mutable)来引用该变量,甚至可以修改:

1
2
const int&& val = getValue(); // OK
int&& val = getValue(); // OK

除此之外,可以通过overload来判断lvalue还是rvalue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

using namespace std;

void printReference (int& value)
{
cout << "lvalue: value = " << value << endl;
}

void printReference (int&& value)
{
cout << "rvalue: value = " << value << endl;
}

int getValue ()
{
int temp_ii = 99;
return temp_ii;
}

int main()
{
int ii = 11;
printReference(ii);
printReference(getValue()); // printReference(99);
return 0;
}

总结:

  1. C++11引入int &&a来声明rvalue引用
  2. rvalue绑定的通常会是临时对象

什么是std::move,作用是什么?

参考C++ Tutorial: C++11/C++14 5. rvalue Reference and Move Semantics - 2017 (bogotobogo.com),move()的作用是将lvalue转化为rvalue。

在C++11之前,在以传值的方式传参的时候,发生了很多implicit copy。现在我们可以通过overloading来区分传入的参数的permanent(lvalue)还是temporary(rvalue),从而避免copy。

当传入的参数是rvalue的时候,我们知道其很快就销毁了,所以我们不需要重新申请空间然后拷贝,我们只需要用rvalue的空间就行。

Move Constructor

1
2
3
4
5
A(A&& other) noexcept    // C++11 - specifying non-exception throwing functions
{
mData = other.mData; // shallow copy or referential copy
other.mData = nullptr;
}

Move Assignment Operator

1
2
3
4
5
6
A& operator=(A&& other) noexcept
{
mData = other.mData;
other.mData = nullptr;
return *this;
}

比起Move Constructor,赋值Operator由于本来就存在值,因此多了一步,一共四步:

  1. 释放当前自己拥有的资源
  2. 将other的资源拿过来(而非copy)
  3. 将other的资源清空
  4. 返回*this

智能指针

自动释放空间的指针,参考smart pointers - cppreference.com,例如:

1
2
3
4
5
6
7
8
9
10
void my_func()
{
int* valuePtr = new int(15);
int x = 45;
// ...
if (x == 45)
return; // here we have a memory leak, valuePtr is not deleted
// ...
delete valuePtr;
}

我们需要在return之前delete,不然会有内存泄露。而使用智能指针后,就没有这个问题了:

1
2
3
4
5
6
7
8
9
void my_func()
{
std::unique_ptr<int> valuePtr(new int(15));
int x = 45;
// ...
if (x == 45)
return; // no memory leak anymore!
// ...
}

unique_ptr

被其管理的对象只能有这一个指针指着。该指针无了之后,对象就可以被回收了。智能指针的使用与普通指针相同。创建与清空不同:

1
2
3
4
// 创建
std::unique_ptr<int> valuePtr(new int(47));
// 清空
valuePtr.reset();

unique_ptr支持move,但不支持copy,因为copy后就不再只有唯一指针指着了对象了。例如:

1
2
std::unique_ptr<int> valuePtr(new int(15));
std::unique_ptr<int> valuePtrNow(std::move(valuePtr));

shared_ptr

支持多个指针指着,当计数器为0时,自动delete。


CMU-15-445——P0
https://fffffaraway.github.io/2022/11/12/CMU-15-445-P0/
Author
Song Wei
Posted on
November 12, 2022
Licensed under