Open DaddyTrap opened 8 years ago
Lambda expression is a really new part in C++11. It is said that this feature is inspired by Functional Programming. This expression support Anonymous function(匿名函数). Let's see the expression.
With this, we can simply declare a function and use it.
// declare
auto func = [a](int b) -> int {
return a + b;
};
// use it
cout << func(10) << endl;
capture means this function can catch some variables including global variables and use them in the function body before it. capture有翻译为“捕获”,指可以捕获上文的一些变量在函数体中使用,包括全局变量。 parameters are the parameters, the same as what we usually use. parameter 参数,就是平常用的参数。 return_type is the type that the function return, same 返回类型,一样的 function_body, same 函数体,一样……
Then let's concentrate on capture.....
#include <iostream>
using std::cout;
using std::endl;
int global = 3;
int main() {
int temp = 1;
auto func1 = [global, temp]() -> int {
return global + temp;
};
cout << func1() << endl;
return 0;
}
output:
4
This is a simple example. The function "catch" global
and temp
and return their sum.
When we try catching the variable after the lambda expression, it can't find it.
#include <iostream>
using std::cout;
using std::endl;
int global = 3;
int main() {
auto func1 = [global, temp]() -> int {
return global + temp;
};
int temp = 1; // put after the lambda expression
cout << func1() << endl;
return 0;
}
lambda.cpp:6:25: error: ‘temp’ was not declared in this scope
auto func1 = [global, temp]() -> int {
^
lambda.cpp: In lambda function:
lambda.cpp:7:21: error: ‘temp’ is not captured
return global + temp;
^
If you want to catch all the variable by value, you can use =
.
#include <iostream>
using std::cout;
using std::endl;
int global = 3;
int main() {
int temp = 1;
auto func1 = [=]() -> int { // change the variable names to "="
return global + temp;
};
cout << func1() << endl;
return 0;
}
output:
4
If you want to catch them by reference, use &
.
#include <iostream>
using std::cout;
using std::endl;
int global = 3;
int main() {
int temp = 1;
auto func1 = [&]() -> int {
temp = 2;
return global + temp;
};
cout << func1() << endl;
cout << "temp: " << temp << endl;
return 0;
}
output:
5
temp: 2
First we have to understand what is L-value(左值) and R-value(右值). Generally, L-value is the value on the left of an assignment character "=", R-value is the value on the right. But this statement is not very reasonable.
L-values have storage addresses that are programmatically accessible to the running program (e.g., via some address-of operator like "&" in C/C++) R-values can be l-values (see below) or non-l-values—a term only used to distinguish from l-values
Simply speaking, when "&" can be used to get a value's address, then this value is a l-value. R-value can be a l-value or not. A temporary value is exactly a r-value. For example, a return value from a function.
The explanation about r-value is above. Next, let's go to the main topic, r-value reference. When we pass values to a function, for example
int sum(int a, int b) {
return a + b;
}
int main() {
cout << sum(3, 4) << endl;
return 0;
}
3
and 4
will be copied.
Though copying an int
is not very difficult, but when copying an object, it is an awesome job.
Why not use the allocated resource? Doing so, we can reduce the times to allocate new memory. Then we come to move semantic.
Move means the L-value "steal" the R-value. Well, even if it was not stolen, its value will be lost because R-value is a temporary value. So, using move semantic can prevent its lost and give it to L-value. In this way we construct the value only once.
Let's make a class MyString
to use it.
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
using std::cout;
using std::endl;
struct MyString {
char *s;
size_t len;
MyString() {
s = NULL;
len = 0;
}
~MyString() {
if (s) delete[] s;
}
MyString(const MyString &f) {
cout << "Copy constructor" << endl;
len = f.len;
s = f.s;
cout << "new space was allocated" << endl;
s = new char[len + 1];
memcpy(s, f.s, len);
s[len] = '\0';
}
MyString(const char *s) {
cout << "Const char* constructor" << endl;
len = strlen(s);
this->s = new char[len + 1];
memcpy(this->s, s, len);
this->s[len] = '\0';
}
MyString &operator=(const MyString &f) {
cout << "Copy Assignment" << endl;
if (s) delete[] s;
len = f.len;
s = f.s;
cout << "new space was allocated" << endl;
s = new char[len + 1];
memcpy(s, f.s, len);
s[len] = '\0';
return *this;
}
};
int main() {
MyString s2;
s2 = MyString("Test");
std::vector<MyString> v;
v.push_back(MyString("Test"));
return 0;
}
With this code, we get
Const char* constructor
Copy Assignment
new space was allocated
Const char* constructor
Copy constructor
new space was allocated
Using move semantic
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
using std::cout;
using std::endl;
struct MyString {
char *s;
size_t len;
MyString() {
s = NULL;
len = 0;
}
MyString(MyString &&f) { // here different
cout << "Move constructor" << endl;
len = f.len;
cout << "nothing new was allocated" << endl;
s = f.s;
f.s = NULL;
f.s = 0;
}
~MyString() {
if (s) delete[] s;
}
MyString(const char *s) {
cout << "Const char* constructor" << endl;
len = strlen(s);
this->s = new char[len + 1];
memcpy(this->s, s, len);
this->s[len] = '\0';
}
MyString &operator=(MyString &&f) { // here different
cout << "Move Assignment" << endl;
if (s) delete[] s;
len = f.len;
cout << "nothing new was allocated" << endl;
s = f.s;
f.s = NULL;
f.s = 0;
return *this;
}
};
int main() {
MyString s2;
s2 = MyString("Test");
std::vector<MyString> v;
v.push_back(MyString("Test"));
return 0;
}
Then we get
Const char* constructor
Move Assignment
nothing new was allocated
Const char* constructor
Move constructor
nothing new was allocated
When moved, no new memory will be allocated. So our program can be more efficient.
In addition, usually we should declare both copy constructor and move constructor. Because when we have to copy something, we can't just move it.
unique_ptr
unique_ptr
means unique pointer. That is, the pointer is the unique one to manage an object and it is responsible to delete the object when the pointer itself is deleted. Yes, it deletes the object in its destructor.
It is useful because we can get no memory errors when using it properly. Next, let's see how we can use it.
#include <iostream>
#include <memory>
using std::unique_ptr;
int main() {
unique_ptr<int> p(new int(10));
std::cout << "\n";
return 0;
}
No delete
!
==11764== Memcheck, a memory error detector
==11764== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==11764== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==11764== Command: ./a.out
==11764==
==11764==
==11764== HEAP SUMMARY:
==11764== in use at exit: 72,704 bytes in 1 blocks
==11764== total heap usage: 2 allocs, 1 frees, 72,708 bytes allocated
==11764==
==11764== LEAK SUMMARY:
==11764== definitely lost: 0 bytes in 0 blocks
==11764== indirectly lost: 0 bytes in 0 blocks
==11764== possibly lost: 0 bytes in 0 blocks
==11764== still reachable: 72,704 bytes in 1 blocks
==11764== suppressed: 0 bytes in 0 blocks
==11764== Rerun with --leak-check=full to see details of leaked memory
==11764==
==11764== For counts of detected and suppressed errors, rerun with: -v
==11764== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
BUT no error!
And you can use it to manage array!
#include <iostream>
#include <memory>
using std::unique_ptr;
int main() {
unique_ptr<int> p(new int[3]{1, 2, 3});
std::cout << "\n";
p.reset();
return 0;
}
No delete!
==11778== Memcheck, a memory error detector
==11778== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==11778== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==11778== Command: ./a.out
==11778==
==11778== Mismatched free() / delete / delete []
==11778== at 0x4C2C2BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11778== by 0x400D03: std::default_delete<int>::operator()(int*) const (in /home/daddytrap/Homework/test/a.out)
==11778== by 0x400C69: std::unique_ptr<int, std::default_delete<int> >::reset(int*) (in /home/daddytrap/Homework/test/a.out)
==11778== by 0x40096B: main (in /home/daddytrap/Homework/test/a.out)
==11778== Address 0x5a37c80 is 0 bytes inside a block of size 12 alloc'd
==11778== at 0x4C2B800: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11778== by 0x40091F: main (in /home/daddytrap/Homework/test/a.out)
==11778==
==11778==
==11778== HEAP SUMMARY:
==11778== in use at exit: 72,704 bytes in 1 blocks
==11778== total heap usage: 2 allocs, 1 frees, 72,716 bytes allocated
==11778==
==11778== LEAK SUMMARY:
==11778== definitely lost: 0 bytes in 0 blocks
==11778== indirectly lost: 0 bytes in 0 blocks
==11778== possibly lost: 0 bytes in 0 blocks
==11778== still reachable: 72,704 bytes in 1 blocks
==11778== suppressed: 0 bytes in 0 blocks
==11778== Rerun with --leak-check=full to see details of leaked memory
==11778==
==11778== For counts of detected and suppressed errors, rerun with: -v
==11778== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
No err....... What the.......?????
Yep, errors occur. Search unique_ptr
in cplusplus
non-specialized
template <class T, class D = default_delete> class unique_ptr; array specialization
template <class T, class D> class unique_ptr<T[],D>;
Now I see. When I want to use it to manage array, I should use unique_ptr<T[]>
If not, when the pointer want to delete the object, it uses delete
but not delete[]
, and you can use operator[]
to access members in the array. (see detail in cplusplus)
The correct code is following.
#include <iostream>
#include <memory>
using std::unique_ptr;
int main() {
unique_ptr<int[]> p(new int[3]{1, 2, 3});
std::cout << "\n";
p.reset();
return 0;
}
Chapter 1:
auto
auto
is not a new key word in C++, but in C++11 it has a new feature (and the feature in C++98 was disabled). Usingauto
, for example,auto i = 1
, means we let the compiler judge the type ofi
.auto
will be treated asint
here. And you can writeauto a = getX()
, then you can easily get the X without thinking more about its type. You can remember your code, but maybe you will forget others' code. So, it is convenient.Somebody thinks it makes no sense to add this feature, but it is really a delicious sugar!(Syntactic sugar I mean) For instance, please see the following code.
At the first time I compile it, I got some compile errors, such as
Finally I found that I didn't give the template to
vector
. But if I useauto
, it won't annoy me. And when we use some libraries, for example, cocos2d-x. It use factory mode to produce instance, soauto
is used generally.The final and correct code: