这是Laurent Kneip教授的CS133的第7讲,上课的时候听的迷迷糊糊的,但是后来写作业的时候觉得这些内容非常重要但是不容易理解,所以写一点总结。
motivation
我们想要让一个class来代替programmer来管理动态分配的memory,在constructor里面分配资源,在deconstructor里面de-allocate.
Smart pointer:
1 | template <class T> |
现在我们就可以愉快地用Smart pointer了,比如:
1 | void function() { |
Auto-pointer flaw
1 | int main() { |
similar problem is caused by this:
1 | void passbyvalue(Auto_ptr1<Resource> res) {} |
solutions to the Auto_ptr flaw
solution 1: prevent the copy constructor and assignment operator to be "available"
- method 1: explicitly declare copy constructor and make them private
1
2
3
4
5
6
7
8
9
10
11template <class T>
class Auto_ptr1 {
T* m_ptr;
public:
Auto_ptr1(T* ptr=nullptr) : m_ptr(ptr) {}
~Auto_ptr1() {delete m_ptr;}
// ...
private:
Auto_ptr(const Auto_ptr1 &);
Auto_ptr& operator= (const Auto_ptr1 &);
}problem: less efficient than the default constructor, member functions and friends can still call the private defined constructors, unclear
- method 2: the C++11 way
1
2
3
4
5
6
7
8
9
10template <class T>
class Auto_ptr1 {
T* m_ptr;
public:
Auto_ptr1(T* ptr=nullptr) : m_ptr(ptr) {}
~Auto_ptr1() {delete m_ptr;}
// ...
Auto_ptr(const Auto_ptr1 &) = delete;
Auto_ptr& operator= (const Auto_ptr1 &) = delete;
}pass by value is no longer available -> we can just pass reference
however, we can no longer do this:
1
2
3
4
5
6??? generateResource() {
Resource *r = new Resource;
return Auto_ptr1(r);
}
// can't return by reference because object will be destroyed
// can't return by value because copy-constructor is disabled
move semantics
我们不想copy value, just move ownership
1 | template <class T> |
- std::auto_ptr is implemented exactly like this in original C++98 standard
- problem occurs if pass by value
- removed since C++17
Lvalues and Rvalues
general rule: if you can take its address, it's an lvalue, else, it's an rvalue
some examples:
1 | template<typename T1, typename T2> |
lvalue: sizeDiff, c1, c2
rvalue: return value
1 | int *px; |
lvalue: px, v, s
rvalue: sizeDiff(v, s)
1 | std::ifstream myinput(std::string("something")); |
anonymous objects are rvalues
- Rvalues die at the end of an expression
- they can't be assigned to
but there is a special case:
1 | void printSomething(const std::string &str) { |
local const reference can be used to prolong the lifetime of temporary value and refers to it until the end of the containing scope(does not incur the cost of a copy-construction)
Rvalue references
1 | int x = 5; |
- rvalue reference allows us to
- extend the lifespan of the (rvalue) object by the lifespan of the rvalue reference
- modify the rvalue
move construction and move assignment
1 | template<class T> |
the usage:
1 | class Resource { |
output:
1 | resource acquired |
2 allocations to create an object, inefficient but very safe
- move construction/assignment
- role: move ownership from one obj to another
- use instead of copying to gain efficiency
- similar to regular copy constructor/assignment oprator
- take non-const rvalue reference instead of const lvalue reference
1 | template<class T> |
and now we run the program again:
1 | class Resource { |
output:
1 | resource acquired |
- key insights
- if an lvalue is used, like: a = b(we should make a deep copy and not alter b)
- if an rvalue is used, like: a = b + c(rvalue is about to be destroyed, it is reasonale to steal the ownership and avoid copying)
std::move
extension of move semantics to lvalues!
1 | template<class T> |
however doing 3 copies is unnecessary, we can do 3 moves instead
solution: cast the lvalues to rvalues with std::move()
1 | template<class T> |
what happens to the lvalue !?
一个小实验:
1 |
|
output:
1 | copying str |
objects that are being stolen from need to be left in a defined "null state"
they are not a temporary after all, and can be used again later