C++11新特性

在vector中,emplace_back()成员函数的作用是在容器尾部插入一个对象,作用效果与push_back()一样,但是两者有略微差异,即emplace_back(args)中放入的对象的参数,而push_back(OBJ(args))中放入的是对象。即emplace_back()直接在容器中以传入的参数直接调用对象的构造函数构造新的对象,而push_back()中先调用对象的构造函数构造一个临时对象,再将临时对象拷贝到容器内存中。

一、std::result_of

template
class result_of
在编译时推导出一个可调用对象(std::function或重载operator())的返回值类型,主要用于模板编写。
Fn:调用对象
ArgTypes:参数列表
成员type:可调用对象的返回类型
std::result_of用法_帝江VII的博客-CSDN博客_std::result_of

std::function<int(int)> callback; ///std::function对象实例

template<typename Fn>
multimap<typename result_of<Fn(Person)>::type, Person> GroupBy(const vector<Person>& vt,const Fn& keySelect)
{
typedef typename result_of<Fn(Person)>::type key_type   ///模板中的应用实例

二、std::bind和std::function和lambda表达式

c++11新特性之std::function和lambda表达式 - 知乎 (zhihu.com)

1、std::bind

使用std::bind可将调用对象和参数一起绑定,绑定后结果用std::function保存,并延迟到任何需要的时候进行调用。
两大作用:
1)将可调用对象与参数一起绑定,提供给另一个std::function调用
2)将n元调用对象转成m元可调用对象,绑定一部分参数 (m

2、std::function

std::function是可调用对象的封装器,可以把std::function看作一个函数对象,用于表示函数这个抽象概念。若std::function不含目标,则称它为空,调用空的std::function的目标会抛出std::bad_function_call异常。
1).std::function可以绑定到全局函数/类静态成员函数(类静态成员函数与全局函数没有区别)。
2).绑定到类的非静态成员函数,则需要使用std::bind。

3、std::forward

有时候右值会转为左值,左值会转为右值
我们须要一种方法能依照參数原来的类型转发到还有一个函数中。这才完美,我们称之为完美转发。

函数模板 void G(A &&a)内部是无法知道形参对应的实参,到底是个普通变量,还是濒死的临时变量?

只能原封不动的完美的转发std::forward给函数模板 void G(A &&a)调用的下一层函数。

所以,std::forward转发问题针对的是模板函数。

using namespace std;

void F(int& a) {

}

void F(int&& a) {
    // do something

}

template<class A>
void G(A &&a) {
    F(std::forward<A>(a));
}

int main (int argc, char *argv[]) {
    int i = 2;
    G(i); //输出 int& version
    G(5);//输出 int && version
    return 0;
}

3、lambda表达式

定义一个匿名函数,可捕获一定范围的变量在函数内部使用
auto func = [capture] (params) opt -> ret { func_body; };

func:lambda表达式名
capture:捕获列表
params:参数表(可选)
opt:函数选项(可选)
ret:返回值(可选)
func_body:函数体

auto func1 = [](int a) -> int { return a + 1; };
auto func2 = [](int a) { return a + 2; };

[this] {    ///< 也是lambda表达式
      for(;;)
      {
      }
}

capture
[] 不捕获任何变量
[&] 引用捕获,捕获外部作用域所有变量,在函数体内使用引用
[=]值捕获,捕获外部作用域所有变量,在函数内使用副本。
[=, &a]值捕获外部作用域所有变量,按引用捕获a变量
[a]只值捕获a变量,不捕获其它变量
[this]捕获当前类中的this指针

#include 
#include 
#include 
using namespace std;
using namespace placeholders;

void test_func(int a)
{

}

void test_func2(int a, int b)
{

}

class TEST_T
{
    public:
        int func(int a, int b);
};

int TEST_T::func(int a, int b)
{

    return 0;
}

int main(int argc, char** argv)
{
    /// 绑定普通函数
    auto test_func5 = std::bind(test_func, 5);
    test_func5();

    auto test_func6 = std::bind(test_func2, _1, 1);
    test_func6(1);

    /// 绑定类成员函数
    TEST_T obj_test;
    auto test_func7 = std::bind(&TEST_T::func, &obj_test, 2, _1);
    test_func7(5);


    // function存储自由函数,
    std::function<void(int)> fun_std_1 = test_func;    
    fun_std_1(-9);

    /// 存储std_bind调用的结果
    std::function<void(int)> fun_std_2 = std::bind(test_func2, _1, 1);
    fun_std_2(2);

    /// lambda表达式示例
    int a = 0;
    auto f1 = [=]()->int{return a;};


    auto f2 = [&]()->int{a++;};
    f2();


    return 0;
}

三、C++11多线程

1、std::thread

1)构造

default (1) thread() noexcept; ///noexcept不抛出异常,减少捕获异常的额外代码
initialization (2) template explicit thread (Fn&& fn, Args&&… args);
copy [deleted] (3) thread (const thread&) = delete;
move (4) thread (thread&& x) noexcept;

(1). 默认构造函数,创建一个空的 thread 执行对象。
(2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4). move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.

#include 
#include 
#include 
#include 
#include 
#include 

void f1(int n)
{
    for (int i = 0; i < 5; ++i) {

        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

void f2(int& n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 2 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

int main()
{
    int n = 0;
    std::thread t1; // t1 is not a thread
    std::thread t2(f1, n + 1); // pass by value
    std::thread t3(f2, std::ref(n)); // pass by reference
    std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
    t2.join();
    t4.join();

}

2)其他成员函数

get_id 获取线程 ID。
joinable 检查线程是否可被 join。
join Join 线程。
detach Detach 线程
swap Swap 线程 。
native_handle返回 native handle。
hardware_concurrency [static]检测硬件并发特性。

2、std::future

std::future 类模板,提供访问异步操作结果的机制,轻松从异步任务中返回结果。

3、std::packed_task

包装一个可调用的对象,并且允许异步获取该可调用对象产生的结果,将其包装的可调用对象的执行结果传递给一个 std::future 对象
C++11 并发指南四( 详解二 std::packaged_task 介绍) - Haippy - 博客园 (cnblogs.com)

#include    // std::thread
#include    // std::packaged_task, std::future
#include  // std::cout

int sum(int a, int b) {
    return a + b;
}

int main() {
    std::packaged_task<int(int,int)> task(sum);
    std::future<int> future = task.get_future();

    // std::promise一样,std::packaged_task支持move,但不支持拷贝
    // std::thread的第一个参数不止是函数,还可以是一个可调用对象,即支持operator()(Args...)操作
    std::thread t(std::move(task), 1, 2);
    // 等待异步计算结果


    t.join();
    return 0;
}
/// 输出: 1 + 2 => 3

四、左值和右值

左值:能出现在等号左边,也能出现在等号右边的变量。可寻址的变量,有持久性。
右值:只能出现在等号右边的变量。不可寻址的常量,临时对象,短暂性。
左值可以修改。
int value = 3 ///< value为左值,3为右值

左值引用:引用一个对象
右值引用:必须绑定到右值的引用,可以通过&&获得右值引用

class demo
{
public:
    // 构造
    demo() {cout << "demo()" << endl;}
    // 析构
    ~demo() { cout << "~demo()" << endl;}
    // 拷贝构造
    demo(const demo&src) {cout << "demo(const demo&)" << endl;}
    // 赋值重载
    demo& operator=(const demo&src) {cout << "operator=" << endl;}
private:
    int *m;
};

如果此时赋值重载中有大量的数据传递,则会使用值传递,在这里可以改写如下:

// 移动拷贝构造(带右值引用)
demo(demo &&src) {cout << "demo(const demo&)" << endl;}
// 移动赋值重载
demo& operator=(demo&&src) {cout << "operator=" << endl;}

/// 将临时量资源的控制权转交给当前对象,临时对象便不再持有资源
/// 没有发生任何内存开辟和数据拷贝

1、标准库std::move函数

作用:实际内部执行static_cast(lvalue),仅仅是对象所有权和状态的改变,并没有发生任何拷贝。在这一过程中,move唯一的功能是将一个左值转换为一个右值引用,使我们通过右值引用使用这个对象,从而实现移动构造。

C++11提供的move方法会将拷贝的代价降低到最小,例如在vector中插入元素时,就可以使用move语义,减少对像的拷贝:

int r1= 1;
int &&r2  = std::move(r1);  ///< 省去了数据的拷贝操作
int r3 = r1;   

string str = "Hello";//这里假设我们只需要将str的内容放到vector中,完成以后永远都不需要再用到str
vector<string> v;
//调用常规的拷贝构造函数,新建字符数组,拷贝数据
v.push_back(str);

//先把str转为右值引用,然后调用移动拷贝构造函数转交所有权
v.push_back(move(str));

2、类型转换

static_cast< new_type >(expression)
dynamic_cast< new_type >(expression)
备注:new_type为目标数据类型,expression为原始数据类型变量或者表达式。

1)static_cast:静态类型转换

static_cast进行的是编译时的类型转换,只能用于已知的类型之间的转换,且不能转换掉const、volatile等属性。主要用于基本数据类型的转换、隐式转换的显式化和向上转型(子类指针或引用转为父类指针或引用)。例如:
int a = 10; double b = static_cast(a); // 将整数a转换成浮点数b Base* ptrB = static_cast(new Derived()); // 将派生类对象指针转换为基类指针

2)dynamic_cast:动态类型转换

主要用于安全的向下转型,由派生类转化为基类指针
dynamic_cast进行的是运行时的类型转换,可以在基类和派生类之间进行类型转换,并且能够检查类型是否符合转换。如果转换失败,则返回空指针或抛出std::bad_cast异常。主要用于向下转型(父类指针或引用转为子类指针或引用),但只有当父类指针或引用真正指向子类对象时才能成功转换。例如:
Derived d; Base* ptrB = &d; Derived* ptrD = dynamic_cast(ptrB); // 将基类指针转换为派生类指针 if (ptrD != nullptr) { // 转换成功 } else { // 转换失败 }
综上所述,static_cast用于已知类型的静态转换,而dynamic_cast用于未知类型的动态转换。使用时需要注意类型安全和转换结果的判断。

你可能感兴趣的:(c++,java,开发语言,驱动开发,linux)