c++ 11特性

c++ 11特性:

Type Inference 类型推导

  1. auto
  2. decltype:
1
2
3
4
int a;
std::string b;
decltype(a) i;
decltype(b) s;

这个语法可以应用于复用匿名的struct或union

1
2
3
4
5
6
7
8
9
10
struct { // 匿名
int x, y;
} temp_position;

union { // 匿名
int *foo;
} temp_union;

decltype(temp_position) pos;
decltype(temp_union) uni;

需要注意,auto和decltype都是编译时推导变量的类型,而不能在运行时推导变量类型,而且其推导有一定的限制。例如

1
2
3
4
auto i; // Compile error
int foo(auto a, auto b); // Compile error, but it is valid in C++14
int bar(auto a = 1, auto b = 1); // 虽然我们可能觉得编译器可以推导
// 但这也是不合法的表达

Return Value Tracking(追踪返回类型)

追踪返回类型的函数和普通函数的声明最大区别在于返回类型的后置。如

1
2
int func(char* a, int b);
auto func(char* a, int b) -> int;

而追踪返回类型还可以让我们在写返回类型时,不写明作用域

1
2
3
4
5
6
class Foo {
struct InnerType { int i; }
InnerType GetInner();
InnerType it;
}
auto OuterType::GetInner() -> InnerType { return it; }

上面体现的效果并不是特别的明显,那么下面的例子呢?

1
2
3
4
5
// 有的时候,你会发现这是面试题
// ——《Understanding C++11 Analysis
// and Application of New Features》
int (*(*pf()) ()) () { return nullptr; }
auto pf1() -> auto (*) () -> int (*) () { return nullptr; }

上述两个函数的作用是完全一样的,但是可读性却完全不同
这其实是一个返回函数指针的函数,而这个指针指向着返回int*的函数

auto的使用细则:auto*, auto&, const auto, volatile auto…

Rvalue Reference 右值引用

通常,在C++中,我们称可以使用取地址运算符(&)的值为左值,而不是左值的值就是右值

1
2
3
4
5
6
7
8
9
#include <iostream>
int main() {
s int a = 1, b = 3, c = 0;
c = a + b;
std::cout << &c << std::endl; // Valid, c is an Lvalue
// std::cout << &(a + b) << std::endl; // Invalid, (a + b) is an Rvalue
std::cout << &a << " " << &b << std::endl; // Valid, a and b are Lvalue
return 0;
}

右值引用,就是使右值也拥有一个名字,如果它是一个临时变量(如函数的返回值),右值引用还能给它“续命”。

基本语法 value_type && val = temp_val;

1
2
3
4
5
6
7
8
int foo() {
return 1;
}

int main() {
int && a = foo(); // foo()的返回值不会立即销毁
return 0;
}

所谓移动语义,就是从将要销毁的临时变量那里把资源移动过来。这样就节省了分配空间和复制值的开销。这就是移动语义的核心。

Lambda 匿名函数

Lambda Functional Programming

基本语法:

1
2
3
4
5
6
7
[capture] (parameters) mutable -> return_type { statement }
[capture]: 捕捉列表。能够捕捉上下文的变量供lambda函数使用。
(parameters): 参数列表。
mutable: 默认情况下,lambda函数总是一个const函数,mutable可以取
消其常量性。
->return_type: 返回类型。
statement: 函数体。可以使用捕获的变量
1
2
3
4
5
6
7
8
9
10
11
12
// example
int main ( ) {
int a = 2 ;
// catch a
auto pow = [ a ] ( int times ) - > int {
int ret = 1 ;
while ( times -- ) ret * = a ; // use a
return ret ;
} ;
std :: cout << pow ( 3 ) << std :: endl ; // 8
return 0 ;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
std::list<int> li = { 1,2,3,4,5,6,7,8 };
auto printList = [](std::list<int>& li) {
for(auto it = li.begin(); it != li.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
};
printList(li);
// remove all even numbers from the list
li.remove_if([&](int x) -> bool {
if(x % 2 == 0) {
return true;
}
return false;
});
printList(li);
1
2
3
4
5
 std :: array < int , 10 > s = { 5 , 7 , 4 , 2 , 8 , 6 , 1 , 9 , 0 , 3 } ;
// sort using a lambda expression
std :: sort ( s. begin ( ) , s. end ( ) , [ ] ( int a, int b ) {
return b < a ;
} ) ;

Closure 函数闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fromto = [ ] ( auto start, auto finish ) {
return [ = ] ( ) mutable {
if ( start < finish )
return start ++ ;
else
throw std :: runtime_error ( "Complete" ) ;
} ;
} ;
auto range = fromto ( 0 , 10 ) ;

std :: cout << range ( ) << std :: endl ;
std :: cout << range ( ) << std :: endl ;
std :: cout << range ( ) << std :: endl ;
std :: cout << range ( ) << std :: endl ;

Initializer List

1
2
3
4
5
6
7
8
9
10
11
struct s {
int x;
std::string std;
std::vector<int> vec;
};

s func (){
return {1, "abcde", {2,4,6,8,10}};
}

s s1{1, "abcde", {2,4,6,8,10}};

Smart Pointer 智能指针

auto_ptr(C++98):

auto_ptr以对象的方式管理堆分配的内存,并在适当的时间(比如auto_ptr析构时),释放获得的堆内存。这种管理方式只要程序员将new返回的指针作为auto_ptr的初始值即可,不需要显式调用delete。比如:

auto_ptr(new int);

不过auto_ptr有一些缺点,如拷贝时返回一个左值,不能调用delete[]等,在C++11中被废弃。

unique_ptr

unique_ptr如其命名,表示它管理一个只属于它的内存,它不能被复制,可以被移动。

1
2
3
4
5
6
7
8
9
10
11
using namespace std;
unique_ptr<int> up1(new int(11));
unique_ptr<int> up2 = up1; // 编译错误,复制
unique_ptr<int> up3 = move(up1) // std::move会使up1清空,
// 这句的语义是将up1移动给up3
cout << *up1 << endl; // 错误,up1已经不拥有该内存
cout << *up3 << endl; // 11
up3.reset(); // reset使指针置空并释放内存
up1.reset(); // 不会导致内存错误
cout << *up3 << endl; // 错误,内存已释放
// 即使不调用reset,unique_ptr对象在析构时也会释放拥有的内存

shared_ptr

shared_ptr也如其命名,表示多个智能指针可以拥有同一个堆内存。在实现上使用了引用计数,当一个shared_ptr失效,内存也不会被释放,而是引用计数减小。当引用计数归零的时候,shared_ptr才会真正释放堆内存的空间。

weak_ptr

weak_ptr用于指向一个shared_ptr,但它实际上不拥有这个内存。其lock()方法可返回一个shared_ptr,而当所指对象已经失效时,可以返回nullptr。用这个可以验证shared_ptr的有效性。

1
2
3
4
5
6
7
8
9
10
11
12
using namespace std;
shared_ptr<int> sp1(new int(22));
shared_ptr<int> sp2 = sp1; // 和sp1共享一个堆内存
weak_ptr<int> wp = sp1; // 指向sp1所指对象
cout << *sp1 << endl; // 22
cout << *sp2 << endl; // 22
sp1.reset(); // 引用计数-1,但不释放内存
if (wp.lock() != nullptr) cout << "valid" << endl; //valid
shared_ptr<int> sp3 = wp.lock(); // sp3指向与sp1相同的对象
sp2.reset();
sp3.reset(); // 所指对象被释放
if (wp.lock() == nullptr) cout << "invalid" << endl; // invalid

Range Base Loop

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <vector>
int main() {
vector<int> a {1,2,3,4,5,6,7,8};
for(auto i : a) {
std::cout << i << " ";
}
std::cout << endl;
return 0;
}
Donate? comment?