C++11新特性

  0、使用C++11新特性,编译时需指定-std=c++11。

  1、auto与decltype:自动类型推导。

  如auto x = 7;,则推导x是int类型的。auto在类型难以确切知道或难以表达时很有用:

template<class T, class U>
void compute(T t, U u)
{
    // x的类型在事先难以确切知道
    auto x = t * u;
    cout << sizeof(x) << endl;
}

int main()
{
    compute<int, int>(1, 1);    // 4
    compute<int, double>(1, 1.2);    // 8

    vector<int> vec = {1, 2, 3, 4};
    // “传统的”遍历方式
    // for (vector<int>::iterator iter = vec.begin(); iter != vec.end(); iter++)
    // 简洁写法
    // for (auto iter = vec.begin(); iter != vec.end(); iter++)
    // 这种情况下,还可以这样遍历
    // for (auto ele : vec)

    return 0;
}

  auto的另一个好处是,使用它定义的变量都必须初始化。

  decltype操作符用于取得操作数类型。如下例:

int main()
{
    int i = 0;
    decltype(i) j = i;  // &i != &j
    decltype((i)) k = i;  // k为int &类型。&i == &k

    int a[10] = {10};
    decltype(a) b;  // b为数组类型。sizeof(b) == 40

    int c[3] = {1, 2, 3};
//  decltype(c[1]) d; // d为引用类型。因为没有初始化而报错

    return 0;
}

 

  2、std::enable_if

template <bool Cond, class T = void> struct enable_if {};

  若Cond为true则enable_if::type被设置为T,否则enable_if::type未定义(会导致编译出错)。示例:

#include <type_traits>

// 用法一
template <class T>
typename std::enable_if<std::is_integral<T>::value, bool>::type // is_integral::value为true时enable_if::type被设置为bool
is_odd (T i) {return bool(i % 2);}

// 用法二
template <class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i % 2);}

int main()
{
    short int i = 1;
    std::cout << std::boolalpha;
    std::cout << "i is odd: " << is_odd(i) << std::endl;
    std::cout << "i is even: " << is_even(i) << std::endl;

    float f = 1.0;
    // std::cout << "f is odd: " << is_odd(f) << std::endl; // 编译出错

    return 0;
}

 

  3、lambda表达式

  1)简介:一种描述函数对象的机制。它可以访问它所在作用域内的局部变量(如第二个例子)。

  2)示例:

int main()
{
    vector<int> v = {50, -10, 20, -30};
    // 根据绝对值排序。第三个参数即为lambda表达式/函数
    sort(v.begin(), v.end(), [](int a, int b) { return abs(a) < abs(b); });

    return 0;
}
int main()
{
    vector<int> vec(8);
    int count = 0;
    // 最终vec的元素为0~7
    generate(vec.begin(), vec.end(), [&count]() { return count++; });

    return 0;
}

  []部分称为“捕获”(capture)列表。

  [&count]表示lambda表达式以引用方式“捕获”count,可以修改它。改成[]会报错,因为若没有“捕获”count则不能访问它;改成[count](值方式“捕获”)也会报错,因为此时count是只读的。另外,[]表示不“捕获”任何变量,[&]表示以引用方式“捕获”所有(可“捕获”的)变量,[=]表示以值方式“捕获”所有(可“捕获”的)变量。

 

  4、std::function和std::bind

  bind和function标准函数对象定义在<functional>中。它们被用于处理函数和函数参数。bind接受一个函数(或函数对象等)作为参数,并产生一个函数对象。如下面两个例子:

int f(int i, char c, double d) { /* ... */ }

int g(int) { /* */ }
double g(double) { /* */ }

int main()
{
    // _1是占位符。使用时要加上using namespace std::placeholders;
    // 参数的绑定也称为局部套用(Currying)
    auto ff = bind(f, _1, 'c', 1.2);
    // 相当于f(7, 'c', 1.2);
    int x = ff(7);

    // 参数“反转”
    auto frev = bind(f, _3, _2, _1);
    frev(1.2, 'c', 1);

    // bind重载函数时
    auto gg = bind((double(*)(double))g, _1);

    // 另一种形式:显式指定返回类型(在C++98中才能实现)
    auto f2 = bind<int>(f, 7, 'c', _1);

    return 0;
}
struct st
{
    int foo(int) { /* */ }
    int operator()(int x, int y) const { return x * y; };
};

int main()
{
    // 创建函数对象
    function<int(int x, int y)> f1 = st();  // 先创建st对象,再将其operator()函数赋值给f1。调用方式:f1(5, 3);

    vector<int> vec = {1, 3, 5, 7, 9};
    // 将函数对象作为参数。累乘。2是起始值,结果为1890
    cout << std::accumulate(vec.begin(), vec.end(), 2, f1) << endl;
    
    // 创建函数对象,“指向”成员函数,第一个参数为相应类对象的指针
    function<int(st *, int)> f2;
    f2 = &st::foo;
    
    st s;
    int v = f2(&s, 5);        // 调用s.foo(5)
    function<int(int)> ff = std::bind(f2, &s, _1);    // bind的返回值可以赋值给函数对象
    v = ff(5);                // 调用s.foo(5)
    
    return 0;
}

 

  5、tuple:把各种不同类型元素组合起来的一种类型。它是pair类型的泛化。在概念上类似于C的struct,但它通过元素在tuple中的序号访问元素,而不是通过元素名。需要include <tuple>并使用std命名空间。

int main()
{
    // 创建tuple
    tuple<const char *, int> tp1 = make_tuple("hello world", 11);
    // 获取元素
    cout << get<0>(tp1) << "\t" << get<1>(tp1) << endl;

    int i = 0;
    // tie:将多个变量“捆绑(tie)”成一个tuple 
// 如果只想获取tuple的部分元素,可以借助ignore
tie(std::ignore, i) = tp1; cout << i << endl; pair<const char *, float> p1 = make_pair("Hello C++11", 1.2f); // tuple_cat:连结多个tuple(或pair) tuple<const char *, int, const char *, float, int> tp2 = tuple_cat(tp1, p1, tie(i)); // tuple_size:获取tuple的元素个数(维数)
cout << tuple_size<decltype(tp2)>::value << endl;
return 0; }

 

  6、多线程相关

  1)std::thread

  2)std::atomic。需要包含<atomic>。另外,编译时需要加上-pthread选项

  atomic类型的对象“包含”了一个特定类型的值。atomic对象保证了从不同线程(同时)访问该值时不会引起数据竞争。以下是来自cplusplus的例子:

std::atomic<bool> ready(false);
std::atomic<bool> winner(false);

void count(int id)
{
    while (!ready) {}  // 等待开始“信号”
    for (int i = 0; i < 1000000; i++) {}   // 每个线程都会计数到100w
    // exchange():(原子地)读取并替换atomic对象包含的值,返回替换前的值
    // 如果某个线程的exchange操作返回false(在此之前还没有其他线程执行exchange操作),则它是第一个计数到100w的
    if (!winner.exchange(true)) 
    {
        std::cout << "thread #" << id << " won!\n";
    }
};

int main()
{
    std::vector<std::thread> threads;
    for (int i = 1; i <= 10; i++) 
        threads.push_back(std::thread(count, i));

    ready = true;  // 开始计数的“信号”
    for (auto &th : threads)
        th.join();

    return 0;
}

  其他一些常用的函数:

  store():修改atomic对象包含的值;load():读取atomic对象包含的值;fetch_add():(原子地)在atomic对象包含的值上做加法(read-modify-write),并返回操作前的值。

 

  7、unordered_map

  关联容器。和map类似,但其元素不是基于key或value排序,而是根据key的哈希值分配到内部哈希表的不同桶中。使用key访问元素的平均时间复杂度为常数。

#include <iostream>
#include <string>
#include <unordered_map>

int main()
{
    std::unordered_map<std::string, std::string> mymap = {
        {"house","maison"}, {"apple","pomme"}, {"tree","arbre"}, {"book","livre"},
        {"door","porte"}, {"grapefruit","pamplemousse"}
    };

    // 也可以这样遍历:for (auto &p : mymap)
    std::cout << "All elements: ";
    for (auto it = mymap.begin(); it != mymap.end(); it++)
        std::cout << "[" << it->first << ": " << it->second << "] ";
    std::cout << std::endl;

    // 此时mymap.bucket_count()为7
    for (unsigned i = 0; i < mymap.bucket_count(); i++)
    {
        std::cout << "bucket #" << i << " has " << mymap.bucket_size(i) << " elements, they are: ";
        // begin()和end()的另一种用法
        for (auto it = mymap.begin(i); it != mymap.end(i); it++)
        {
            std::cout << "[" << it -> first << ": " << it -> second << "] ";
        }
        std::cout << std::endl;
    }

    // 此时mymap.load_factor()为0.857143(即6/7)
    mymap.max_load_factor(0.5);
    // 由于此时load_factor > max_load_factor,故需要增加桶的数目,mymap.bucket_count()变为13

    std::unordered_map<std::string, std::string>::hasher fn = mymap.hash_function();
    // fn("this"): 2428694746313403453、fn("thin"): 18140520699013722149

    return 0;
}
[root@localhost ~]# ./a.out 
All elements: [grapefruit: pamplemousse] [door: porte] [tree: arbre] [book: livre] [apple: pomme] [house: maison]
bucket #0 has 1 elements, they are: [tree: arbre]
bucket #1 has 2 elements, they are: [book: livre] [apple: pomme]
bucket #2 has 1 elements, they are: [door: porte]
bucket #3 has 0 elements, they are:
bucket #4 has 1 elements, they are: [house: maison]
bucket #5 has 0 elements, they are:
bucket #6 has 1 elements, they are: [grapefruit: pamplemousse]

  常用函数说明:

  load_factor():返回unordered_map容器当前的负载因数(load factor)。负载因数=容器中元素的个数/桶的数目,即平均每个桶的元素个数。它会影响哈希表中发生冲突的可能性。

  max_load_factor():获取或设置load factor的最大值(默认为1.0)。

  bucket_count():返回容器中桶的数目。桶的数目直接影响容器哈希表的负载因数,因而也影响冲突的可能性。当负载因数大于max_load_factor时,容器会自动增加桶的数目(以减小负载因数),此过程会对元素重新计算哈希值(rehash)。

  bucket_size():返回桶中元素的个数(影响访问桶中特定元素的时间)。

  size():返回容器中元素的个数。

  bucket():通过key定位到元素在哪个桶中。

  hash_function():获取容器使用的哈希函数对象。

 

  8、final:用于修饰虚函数时,在派生类中将不可重写该函数;用于修饰类时,表示该类不可被继承。

struct Base
{
    virtual void foo();
};

struct A : Base
{
    void foo() final;  // 将A::foo()声明为final
    void bar() final;  // 出错:非虚函数不可声明为final
};

struct B final : A  // 将B声明为final
{
    void foo() override;  // 出错:因为A::foo()已经声明为final,不可被override
};

struct C : B  // 出错:B已经声明为final,不可被继承
{
};

 

 

  参考资料:

  http://www.stroustrup.com/C++11FAQ.html

  http://www.cplusplus.com

  http://en.cppreference.com/w/cpp/language/final

  http://segmentfault.com/a/1190000003004734?_ea=266751#articleHeader5

 

 

不断学习中。。。

posted on 2015-07-24 00:33  han'er  阅读(473)  评论(0编辑  收藏  举报

导航