c++11之新特性

统一初始化

  • c++扩大了用{}的使用范围,使其可用于内置类型和自定义类型

缩窄

  • 初始化列表语法可防止缩窄,即禁止将数值赋给无法存储它的数值变量; 在编译器会报错

std::initializer_list

  • C++11提供了模板类initializer_list,可将其用作构造函数的参数

code

#include <iostream>
#include <cstring>
#include <memory>
#include <vector>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")
class MyClass
{
private:
    int m_a;
    double m_b;

public:
    MyClass() = default;
    MyClass(int a, double b) : m_a(a), m_b(b) {}
};
int main()
{
    // 统一初始化
    {
        int x = {5};                     // 可添加等号=
        short y[5] = {1, 2, 3, 4, 5};    // 不添加等号
        int *p = new int[4]{1, 2, 3, 4}; // 用于new
        MyClass a1(1, 3);                // old style
        MyClass a2{1, 2};                // c++11
        MyClass a3 = {1, 2};             // c++11
    }
    // 防止缩窄
    {
        // char c1 = 1.23e27;               // 超过范围,double to char,undefined behavior; 编译期warning
        // char c2 = 459585821;             // 超过范围,int to char ,undefined behavior; 编译期warning
        char c3 = static_cast<char>(1.23e27); // 编译期无warning
        // char c4 = {128};                      // 编译器error:初始化列表语法可防止"缩窄",即禁止将数值赋给无法存储它的数值变量。
    }
    // c++11 提供了模版类 initializer_list;STL容器提供了将initializer_list作为参数的构造函数
    {
        vector<int> a(10);      // uninitialized vector with 10 elements
        vector<int> b{10};      // initializer-list,b has 1 elemnt set to 10;
        vector<int> c{1, 2, 3}; // 3 elements, vector(initializer_list<value_type> __il);
    }
    return 0;
}

返回顶部

声明

  • C++11提供了多种简化声明的功能,尤其在使用模板时

auto

  • C++11将其用于实现自动类型推断;这要求进行显式初始化,让编译器能够将变量的类型设置为初始值的类型,可以简化代码

decltype

  • 关键字decltype将变量的类型声明为表达式指定的类型,在定义模版的时候特别有用

返回类型后置

  • C++11新增了一种函数声明语法:在函数名和参数列表后面(而不是前面)指定返回类型

模版别名:using=

  • using = 和typedef区别
    • 都可以创建类型别名
    • using=可以用于模版部分具体化,但是typedef不能

nullptr

  • 空指针是不会指向有效数据的指针
  • 以前c++源码中使用0表示这种指针,但内部表示可能不同;使得0即可表示指针常量,又可表示整型常量
  • 现阶段:如果试图将nullptr传递给int形参的函数,编译器将此视为错误
  • 建议:出于清晰和安全考虑,空指针请使用nullptr—如果您的编译器支持它
    返回顶部

code

#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
#include <array>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")
class MyClass
{
private:
    int m_a;
    double m_b;

public:
    MyClass() = default;
    MyClass(int a, double b) : m_a(a), m_b(b) {}
};
template <typename T, typename U>
void mul(T a, U b)
{
    decltype(a * b) tu;
    tu = a * b;
    cout << tu << '\t' << typeid(tu).name() << endl;
}

// 返回类型后置
template <typename T, typename U>
auto myAdd(T a, U b) -> decltype(a + b)
{
    return a + b;
}

// using = 用于模版取别名
template <typename T>
using arr10 = array<T, 10>; // 不能在函数中声明,模板声明只能在为命名空间或类作用域声明出现
void testAuto()
{
    auto a = 5;   // int
    auto b = 5.0; //double

    vector<int> c{1, 2, 3};
    auto it = c.begin(); // it is vector<int>::iterator

    auto f = [](int a) -> int {
        return a + 2;
    }; // 通过f简化代码
    // cout << typeid(f).name() << endl;
}
void testDecltype()
{
    const int x = 5;
    decltype(x) y = 6; //获取x的类型作为y的类型声明
    cout << typeid(y).name() << endl;
    // decltype 用于模版
    mul<char, short>(char(1), short(2));
}
void testRearDecl()
{
    auto a = myAdd<int, double>(int(5), double(10.0));
    cout << a << '\t' << typeid(a).name() << endl;
}
void testUsing()
{
    // typedef vector<int> vi;
    using vi = vector<int>;
    vi vec{1, 2, 3};

    arr10<int> b = {1, 2, 3};
    for_each(b.begin(), b.end(), [](const int &n) { cout << n << '\t'; });
    cout << endl;
}
void print(int *p)
{
    cout << p << '\t' << (nullptr == p ? -1 : *p) << endl;
}
void testNullptr()
{
    // int a = nullptr; // error 不能用nullptr去初始化int,;nullptr是指针类型,不能转换为整型类型
    int *p = nullptr;
    cout << p << '\t' << typeid(p).name() << endl;
    print(nullptr);
    print(0); // C++11仍允许使用0来表示空指针,因此表达式nullptr==0为true,但使用nullptr而不是0提供了更高的类型安全
}
int main()
{
    // auto
    testAuto();
    // decltype
    testDecltype();
    // 返回类型后置
    testRearDecl();
    // 模版别名:using=
    testUsing();
    // nullptr
    testNullptr();
    return 0;
}

智能指针

c++11之智能指针

返回顶部

异常规范方面的修改

  • noexcept 告诉编译器,函数中不会发生异常,有利于编译器对程序作更多的优化
  • 通过noexcept 代替throw()
  • noexcept 默认为noexcept(true)
  • 析构函数不能抛出异常:Effective C++ 里面有一条”别让异常逃离析构函数“,大意说是Don’t do that, otherwise the behavior is undefined;其它异常抛出时在栈解退阶段 无法捕捉析构里的异常
  • 构造函数抛出异常注意内存泄漏
  • tips: 析构函数/移动构造/移动赋值函数加上 noexcept

code

#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
#include <array>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")

void print(int a) throw() //C++11之前
{
    cout << a << endl;
}
void print2(int a) noexcept // C++11之后
{
    cout << a << endl;
}
template <typename T>
void mySwap(T &a, T &b) noexcept(noexcept(a.swap(b))) //有条件的noexcecpt:如果操作a.swap(b)不发生异常,那么函数swap(Type& a, Type& b)一定不发生异常
{
    a.swap(b);
}
class Base
{
public:
    void fun() { throw logic_error("1"); }
    ~Base() noexcept(false)
    {
        cout << "destruct base" << endl;
        throw logic_error("2");
    }
};

void testDestructExcept()
{
    try
    {
        Base base;
        // base.fun(); // 无法捕捉~Base() 里面的异常
    }
    catch (exception &e)
    {
        cout << e.what() << endl;
    }
    catch (...)
    {
        cout << "get the catch" << endl;
    }
}
int main()
{
    // print(12);
    // print2(34);
    testDestructExcept();
    return 0;
}

返回顶部

作用域内枚举

  • C++11新增了一种枚举。这种枚举使用class或struct定义,以免发生名称冲突

code

#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
#include <array>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")
enum old
{
    NUM1,
    NUM2,
    NUM3
};
enum class new1
{
    N1,
    N2,
    N3
};
enum struct new2
{
    N1,
    N2,
    N3
};
int main()
{
    cout << NUM2 << '\t' << int(new1::N2) << '\t' << int(new2::N2) << '\t' << endl;
    return 0;
}

返回顶部

对类的修改

显式转换运算符

  • C++引入了关键字explicit,以禁止单参数构造函数导致的自动转换;防止自动类型转换可能导致意外转换的问题

类内成员初始化

  • 通过使用类内初始化,可避免在构造函数中编写重复的代码,从而降低了程序员的工作量、厌倦情绪和出错的机会

返回顶部

模版和STL方面的修改

基于范围的for循环

  • 形式:for(auto i:std::vector{1,2,3}){}

新的STL容器

  • C++11新增了STL容器forward_list、unordered_map、unordered_multimap、unordered_set和unordered_multiset,以及模版array

新的STL方法

  • C++11新增了STL方法cbegin()和cend()

valarray升级

  • 模板valarray独立于STL开发的,C++11添加了两个函数(begin()和end()),它们都接受valarray作为参数

code

#include <iostream>
#include <algorithm>
#include <valarray>
using namespace std;

int main()
{
    valarray<int> f(5);
    valarray<int> a(f[slice(0, 4, 1)]);
    // for (auto it = begin(a); it != end(a); ++it)
    // {
    //     cout << *it << '\t';
    // }
    for (auto it : a)
    {
        cout << it << '\t';
    }
    cout << endl;
    return 0;
}

摒弃export

  • C++98新增了关键字export,旨在提供一种途径,让程序员能够将模板定义放在接口文件和实现文件中,其中前者包含原型和模板声明,而后者包含模板函数和方法的定义。实践证明这不现实,因此C++11终止了这种用法,但仍保留了关键字export,供以后使用

尖括号

  • old-- std::vector<std::list > v1;// 两个>中间多个空格
  • new–std::vector<std::list> v1;// 两个>中间可以不要空格

返回顶部

右值引用和移动语义

右值引用

  • 作用:可关联到右值,即可出现在赋值表达式右边,但不能对其应用地址运算符
  • 形式:int&& a = 10;
  • 包括:
    • 字面常量
    • x+y 等表达式和返回值的函数

移动语义

  • 作用:减少资源的拷贝,只改变指向,不拷贝资源
  • 学习移动语义的作用:对大多数程序员来说,右值引用带来的主要好处并非是让他们能够编写使用右值引用的代码,而是能够使用利用右值引用实现移动语义的库代码。例如,STL类现在都有复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符
  • 通过右值引用实现,编写移动(构造/赋值)函数

code

#include <iostream>
using namespace std;
// #define cout (cout << "line " << __LINE__ << " - ")
class Useless
{
private:
    int n;
    char *pc;
    static int ct;
    void ShowObject() const;

public:
    Useless();
    explicit Useless(int k);
    explicit Useless(int k, char ch);
    Useless(const Useless &f);            // regular copy constructor
    Useless(Useless &&f);                 // move constructor
    Useless &operator=(const Useless &f); // copy assigment
    Useless &operator=(Useless &&f);      // move assigment
    ~Useless();
    Useless operator+(const Useless &f) const;
    void ShowData() const;
};
int Useless::ct = 0;
Useless::Useless()
{
    ++ct;
    n = 0;
    pc = nullptr;
    cout << "default constructor called; number of objects: " << ct << endl;
    ShowObject();
}
Useless::Useless(int k) : n(k)
{
    ++ct;
    cout << "int constructor called; number of objects: " << ct << endl;
    pc = new char[n];
    ShowObject();
}
Useless::Useless(int k, char ch) : n(k)
{
    ++ct;
    cout << "int,char constructor called; number of objects: " << ct << endl;
    pc = new char[n];
    for (int i = 0; i < n; i++)
        pc[i] = ch;
    ShowObject();
}
Useless::Useless(const Useless &f) : n(f.n)
{
    ++ct;
    cout << "copy const called; number of objects: " << ct << endl;
    pc = new char[n];
    for (int i = 0; i < n; i++)
        pc[i] = f.pc[i];
    ShowObject();
}
Useless::Useless(Useless &&f) : n(f.n)
{
    ++ct;
    cout << "move constructor called;number of objects: " << ct << endl;
    pc = f.pc;
    f.pc = nullptr;
    f.n = 0;
    ShowObject();
}
Useless &Useless::operator=(const Useless &f)
{
    cout << "copy operator= called;number of objects: " << ct << endl;
    if (this == &f)
        return *this;
    delete[] pc;
    n = f.n;
    pc = new char[n];
    for (int i = 0; i < n; i++)
        pc[i] = f.pc[i];
    return *this;
}
Useless &Useless::operator=(Useless &&f)
{
    cout << "move operator= called;number of objects: " << ct << endl;
    if (this == &f)
        return *this;
    delete[] pc;
    n = f.n;
    pc = f.pc;
    f.n = 0;
    f.pc = nullptr;
    return *this;
}
Useless::~Useless()
{
    cout << "destructor called; objects left: " << --ct << endl;
    cout << "delete object:" << endl;
    ShowObject();
    delete[] pc;
}
Useless Useless::operator+(const Useless &f) const
{
    cout << "Enterint operator+()" << endl;

    Useless temp = Useless(n + f.n);
    for (int i = 0; i < n; i++)
        temp.pc[i] = pc[i];
    for (int i = n; i < temp.n; i++)
        temp.pc[i] = f.pc[i - n];
    cout << "tempt object: " << endl;
    cout << "Leaving operator + ()" << endl;
    // return std::move(temp); // 通过std::move一定会调用移动构造
    return temp;
}
void Useless::ShowObject() const
{
    cout << "Number of elements: " << n << '\t';
    cout << "Data address: " << (void *)pc << endl;
}
void Useless::ShowData() const
{
    if (0 == n)
        cout << "(object empty)";
    else
        for (int i = 0; i < n; i++)
            cout << pc[i];
    cout << endl;
}
int main()
{
    Useless one(10, 'x');
    Useless two = one; // calls copy constructor
    Useless three(20, 'o');

    //calls operator +(),move constructor
    // MVC c++ 2010会调用移动构造
    // > g++ 4.5.0 -std=c++11 /新版本的clang 不会调用移动构造
    // 编译器推断出对象four是operator+()所做工作的受益人,因此将operator+()创建的对象转到four的名下,进行了优化
    Useless four = one + three; // c++98/03会调用赋值构造,如果实参为右值,const引用形参将指向一个临时变量
    cout << "object one: ";
    one.ShowData();
    cout << "object two: ";
    two.ShowData();
    cout << "object three:";
    three.ShowData();
    cout << "object four:";
    four.ShowData();
    four = three;            // call copy operator=
    four = std::move(three); // call move operator=
    // four = static_cast<Useless &&>(three); // call move operator=
    return 0;
}

返回顶部

新的类功能

特殊的成员函数

  • c++11新增了两个函数:移动构造函数和移动赋值运算符
  • tips
    • 如果您提供了析构函数、复制构造函数或复制赋值运算符,编译器将不会自动提供移动构造函数和移动赋值运算符
    • 您提供了移动构造函数,因此编译器不会自动创建默认的构造函数、复制构造函数和复制赋值构造函数

默认的方法和禁用的方法

  • default:显式地声明这些方法的默认版本,关键字default只能用于6个特殊成员函数
  • delete:用于禁止编译器使用特定方法,delete可用于任何成员函数

委托构造函数

  • 为让编码工作更简单、更可靠,C++11允许您在一个构造函数的定义中使用另一个构造函数。这被称为委托,因为构造函数暂时将创建对象的工作委托给另一个构造函数
  • example
#include <iostream>
#include <string>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")
class MyClass
{
private:
    int m_a = 0;
    double m_b = 0.0;
    string m_c;

public:
    MyClass(int a, double b, string c) : m_a(a), m_b(b), m_c(c)
    {
    }
    MyClass(string c) : MyClass(2, 0.0, c) // 委托构造函数:将创建对象的工作委托给另一个构造函数
    {
    }
    void show()
    {
        cout << m_a << '\t' << m_b << '\t' << m_c << endl;
    }
};

int main()
{
    MyClass a("hello");
    a.show();
    return 0;
}

继承构造函数

  • 为进一步简化编码工作,C++11提供了一种让派生类能够继承基类构造函数的机制
  • code
#include <iostream>
#include <string>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")
class MyClass
{
private:
    int m_a = 0;
    double m_b = 0.0;
    string m_c;

public:
    MyClass(int a, double b, string c) : m_a(a), m_b(b), m_c(c)
    {
    }
    MyClass(double b) : MyClass(1, b, string()) // 委托构造函数:将创建对象的工作委托给另一个构造函数
    {
    }
    MyClass(string c) : MyClass(2, 0.0, c) // 委托构造函数:将创建对象的工作委托给另一个构造函数
    {
    }
    void show()
    {
        cout << m_a << '\t' << m_b << '\t' << m_c << endl;
    }
};
class MyClassInherit : public MyClass
{
private:
    int m_j = 0;

public:
    using MyClass::MyClass; // 让派生类继承基类的所有构造函数(默认构造函数、复制构造函数和移动构造函数除外)

    MyClassInherit(int a) : MyClass(2 * a, 0.0, string()), m_j(a)
    {
    }
    MyClassInherit(string a) : MyClass(0, 0.0, a), m_j(2)
    {
    }
    void show()
    {
        cout << m_j << endl;
        MyClass::show();
    }
};

int main()
{
    MyClassInherit a("hello"); // call MyClassInherit(string a)
    a.show();
    MyClassInherit b(double(5.5)); // call MyClass(double b)
    b.show();
    return 0;
}

管理虚方法:override和final

  • override:指出您要覆盖一个虚函数:将其放在参数列表后面。如果声明与基类方法不匹配,编译器将视为错误
  • final:禁止派生类覆盖特定的虚方法

返回顶部

lambda函数

  • 主要目的:让您能够将类似于函数的表达式用作接受函数指针或函数符的函数的参数
  • 优点
    • 距离:定义和使用距离可以很近(因为不能在函数内部定义其他函数,因此函数的定义可能离使用它的地方很远。函数符是不错的选择,因为可在函数内部定义类(包含函数符类),因此定义离使用地点可以很近)
    • 简洁:函数符代码比函数和lambda代码更繁琐,函数和lambda的简洁程度相当
    • 效率:取决于编译器使用什么类型来跟踪lambda(函数指针方法阻止了内联,因为编译器传统上不会内联其地址被获取的函数,因为函数地址的概念意味着非内联函数。而函数符和lambda通常不会阻止内联)
    • 功能:lambda有一些额外的功能。具体地说,lambad可访问作用域内的任何动态变量
  • 形式:[capture](parameters)->return-type{body}

返回顶部

包装器/适配器

bind

  • bind:函数适配器,通过一个可调用对象生成新的可调用对象
  • 其中模板bind可替代以前的bind1st和bind2nd,但更灵活
  • code
#include <iostream>
#include <functional>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")
void func(int a, int b, int c)
{
    cout << a << '\t' << b << '\t' << c << endl;
}
class MyClass
{
public:
    void show(int a, int b, int c)
    {
        cout << "MyClass::show:" << '\t' << a << '\t' << b << '\t' << c << endl;
    }
};

// 函数对象
class MyClass2
{
public:
    MyClass2() = default;
    MyClass2(int a) : m_a(a) {}
    int operator()(int a)
    {
        return m_a * a;
    }

private:
    int m_a = 10;
};
int main()
{
    // bind 返回值也是一个函数对象
    auto f1 = bind(func, placeholders::_2, 15, placeholders::_1); // 普通函数/函数指针
    f1(12, 20);
    MyClass a;
    auto f2 = bind(&MyClass::show, &a, placeholders::_1, 2, placeholders::_2); // 类成员函数
    f2(1, 3);

    MyClass2 b(20);
    cout << b(10) << endl;
    return 0;
}

mem_fn

  • 把成员函数转为函数对象,使用对象指针或对象(引用)进行绑定
  • 替换mem_fun/mem_fun_ref
  • code
#include <iostream>
#include <functional>
#include <vector>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")

class MyClass
{
public:
    void show()
    {
        cout << "MyClass::show no param" << endl;
    }
    void print(int a, int b, int c)
    {
        cout << "MyClass::show:" << '\t' << a << '\t' << b << '\t' << c << endl;
    }
};

int main()
{
    // bind 返回值也是一个函数对象
    // vector<MyClass *> vec{new MyClass(), new MyClass(), new MyClass()};
    auto f = mem_fn(&MyClass::show);
    f(MyClass());

    vector<MyClass> vec{MyClass(), MyClass(), MyClass()};
    for_each(vec.begin(), vec.end(), mem_fn(&MyClass::show));
    // for_each(vec.begin(), vec.end(), mem_fn(&MyClass::print, 1, 2, 3));// error:不能接受额外参数,不方便
    for_each(vec.begin(), vec.end(), bind(&MyClass::print, placeholders::_1, 1, 2, 3));
    return 0;
}

reference_wrapper

function

  • std::function是一个函数包装器模版,可以包装任何类型的可调用对象(普通函数,函数对象,lambda,函数指针,类成员函数指针)
  • code
#include <iostream>
#include <functional>
#include <vector>
using namespace std;
// #define cout (cout << __DATE__ << ' ' << __TIME__ << '\t' << __FILE__ << ":" << __LINE__ << " - ")
template <typename T, typename F>
T use_f(T v, F f)
{
    static int cnt = 0;
    cnt++;
    cout << "cnt = " << cnt << '\t' << "&cnt = " << &cnt << endl;
    return f(v);
}
class FuncOj1
{
private:
    double m_a = 0.0;

public:
    explicit FuncOj1(double a) : m_a(a) {}
    double operator()(double p) { return m_a * p; }
};
class FuncOj2
{
private:
    double m_a = 0.0;

public:
    explicit FuncOj2(double a) : m_a(a) {}
    double operator()(double p) { return m_a * p; }
};
double f1(double x) { return 2.0 * x; }
double f2(double x) { return x * x; }

void Test1()
{
    cout << '\t' << __FUNCTION__ << '\t' << endl;
    double a = 3.14;
    // 通过function使use_f只实例化5次
    // 可调用的类型如此丰富,导致模版的效率很低
    cout << use_f(a, f1) << endl;
    cout << use_f(a, f2) << endl;
    cout << use_f(a, FuncOj1(2.0)) << endl;
    cout << use_f(a, FuncOj2(2.0)) << endl;
    cout << use_f(a, [](double x) { return 2.0 * x; }) << endl;
    cout << use_f(a, [](double x) { return x * x; }) << endl;
}
void Test2()
{
    cout << '\t' << __FUNCTION__ << '\t' << endl;
    double a = 3.14;
    typedef function<double(double)> fd_d;
    // 通过function使use_f只实例化一次
    cout << use_f(a, fd_d(f1)) << endl;
    cout << use_f(a, fd_d(f2)) << endl;
    cout << use_f(a, fd_d(FuncOj1(2.0))) << endl;
    cout << use_f(a, fd_d(FuncOj2(2.0))) << endl;
    cout << use_f(a, fd_d([](double x) { return 2.0 * x; })) << endl;
    cout << use_f(a, fd_d([](double x) { return x * x; })) << endl;
}
int main()
{
    Test1();
    Test2();
    return 0;
}

返回顶部

可变参数模版

  • 一个可变参数模板(variadictemplate)就是一个接受可变数目参数的模板函数或模板类
  • 可变数目的参数被称为参数包(parameterpacket)
    • 模板参数包(templateparameterpacket),表示零个或多个模板参数
    • 函数参数包(functionparameterpacket),表示零个或多个函数参
  • sizeof…运算符:获取参数包中包含多少元素
  • 非可变参数模板比可变参数模板更特例化,因此编译器选择非可变参数版本

包扩展

  • 当…扩展一个包时,我们还要提供用于每个扩展元素的模式(pattern)
  • 扩展一个包就是将它分解为构成的元素,对每个元素应用模式,获得扩展后的列表;扩展中的模式会独立地应用于包中的每个元素

转发参数包

  • tips:在新标准下,我们可以组合使用可变参数模板与forward机制来编写函数,实现将其实参不变地传递给其他函数
  • code
template <typename... Args>
void func(Args &&... args) // 将Args扩展为一个右值引用的列表
{
    work(forward<Args>(args)...); // work的实参既扩展了Args又扩展了args
}

函数可变参数模版 code

#include <iostream>
#include <sstream>
#include <string>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")
// 函数模版
template <typename... Args>
void func(const Args &... rest)
{
    // sizeof...获取参数包元素的个数
    cout << sizeof...(Args) << endl; // 类型参数的数目
    cout << sizeof...(rest) << endl; // 函数参数的数目
}

template <typename T>
string string_rep(const T &t)
{
    ostringstream ret;
    ret << t << '\t';
    return ret.str();
}

// 非可变参数版本myPrint,用于终止递归并打印最后一个元素,在可变参数版本的myPrint定义之前声明
template <typename T>
ostream &myPrint(ostream &os, const T &t)
{
    return os << t << endl;
}

// 可变参数版本myPrint,用于递归调用
template <typename T, typename... Args>
ostream &myPrint(ostream &os, const T &t, const Args &... rest) // 模版参数包被扩展,const Args& 应用与每个元素,通过...来触发
{
    os << t << "\t";
    return myPrint(os, rest...); // 参数包扩展,通过扩展为myPrint调用生成实参列表
    // return myprint(os,string_rep(args_1),string_rep(args_2),string_rep(args_n))
    // return myPrint(os, string_rep(rest)...); // 增加包扩展模式,表示我们希望对函数参数包rest中的每个元素调用string_rep
}

int main()
{
    int a = 1;
    double b = 2.0;
    string c = "hello";
    func(a, a, b, c);
    func(a, c);

    myPrint(cout, a, b, c);
    myPrint(cout, a, b);
    myPrint(cout, a); // 调用非可变参数版本的myPrint,非可变参数模板比可变参数模板更特例化,因此编译器选择非可变参数版本

    return 0;
}

类可变参数模版 code

#include <iostream>
#include <type_traits>
#include <memory>
using namespace std;
#define cout (cout << "line " << __LINE__ << " - ")
// 可变参数类模版的声明
template <typename... Types>
struct Sum;

// 可变参数的类模版的定义,告诉编译器如何递归展开参数包
template <typename First, typename... Rest>
struct Sum<First, Rest...>
{
    enum
    {
        value = Sum<First>::value + Sum<Rest...>::value
    };
};

// 特化的递归终止类
template <typename Last>
struct Sum<Last>
{
    enum
    {
        value = sizeof(Last)
    };
};

int main()
{
    Sum<int, double, string> aa;
    cout << sizeof(int) << '\t' << sizeof(double) << '\t' << sizeof(string) << endl;
    cout << aa.value << endl;

    return 0;
}

返回顶部

other

并行编程

  • thread_local:关键字thread_local将变量声明为静态存储,其持续性与特定线程相关;即定义这种变量的线程过期时,变量也将过期;每 一个线程都拥有一个独立的变量实例
  • code
#include <iostream>
#include <thread>
using namespace std;

thread_local int g_n = 1;

void f()
{
    g_n++;
    printf("id=%d, n=%d\n", std::this_thread::get_id(), g_n);
    // cout << "id=" << std::this_thread::get_id() << ", n=" << g_n << endl;
}

void foo()
{
    thread_local int i = 0;
    printf("id=%d, n=%d\n", std::this_thread::get_id(), i);
    i++;
    // cout << "id=" << std::this_thread::get_id() << ", n=" << i++ << endl;
}

void f2()
{
    foo();
    foo();
}

int main()
{
    g_n++; //修改操作并不影响g_n在线程t2和t3中的初始值(值为1)
    f();
    std::thread t1(f);
    std::thread t2(f);
    t1.join();
    t2.join();
    f();
    f2();
    std::thread t4(f2);
    std::thread t5(f2);
    t4.join();
    t5.join();
    return 0;
}

新增的库

  • random:头文件random支持的可扩展随机数库提供了大量比rand()复杂的随机数工具
  • chrono:头文件chrono提供了处理时间间隔的途径
  • tuple:头文件tuple支持模板tuple
  • ratio:头文件ratio支持的编译阶段有理数算术库让您能够准确地表示任何有理数,其分子和分母可用最宽的整型表示。它还支持对这些有理数进行算术运算
  • regex:正则表达式库

低级编程

  • 变化之一是放松了POD(PlainOldData)的要求
  • 允许共用体的成员有构造函数和析构函数,这让共用体更灵活;但保留了其他一些限制,如成员不能有虚函数
  • C++11解决了内存对齐问题; 要控制对齐方式,可使用说明符alignas
  • constexpr机制让编译器能够在编译阶段计算结果为常量的表达式,让const变量可存储在只读内存中,这对嵌入式编程来说很有用(在运行阶段初始化的变量存储在随机访问内存中)

杂项

  • C++11提供了一种创建用户自定义字面量的机制:字面量运算符(literaloperator);如11000b–2进制字面量
  • C++提供了调试工具assert;C++11新增了关键字static_assert,可用于在编译阶段对断言进行测试。这样做的主要目的在于,对于在编译阶段(而不是运行阶段)实例化的模板,调试起来将更简单
  • C++11加强了对元编程(metaprogramming)的支持

返回顶部

语言变化

  • Boost库成了C++编程的重要部分,给C++11带来了深远影响;该计划的基本理念是,创建一个充当开放论坛的网站,让人发布免费的C++库
  • TR1(TechnicalReport1)是C++标准委员会的部分成员发起的一个项目,它是一个库扩展选集,这些扩展与C++98标准兼容,但不是必不可少的。这些扩展是下一个C++标准的候选内容;在TR1中,Boost库占了很大一部分

返回顶部

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值