C++11(二)

简介: C++11

完美转发


观察下面的代码


int main()
{
  int x = 1;
  int y = 2;
  int&& rr1 = 0;
  const int&& rr2 = x + y;
  rr1++;
  rr2++;
  return 0;
}


通过上面的学习肯定知道,右值被引用之后会被存储到特定的位置,可以对特定的位置进行取地址,所以rr1++正确;rr2++错误,因为其引用的数据被const所修饰不能进行修改


7cbe807e75326fd2da78b4bbb7fa9e6a_e034dc9e77fe4dad9b653942dbd385a1.png


运行结果与预期一致

既然右值被引用之后可以进行”修改“,是不是可以理解为其属性变为了左值;再结合上面当进行移动构造时,右值本身是不能进行修改的,但是经过右值引用之后,其属性变为了左值,资源交换也就可以进行;所以,当右值被右值引用之后,其属性变为了左值


观察下列代码


void Fun1(int& x)
{
  cout << "Fun1(int& x)" << endl;
}
void Fun1(int&& x)
{
  cout << "Fun1(int&& x)" << endl;
}
int main()
{
  int i = 0;
  Fun1(i);
  Fun1(0);
  return 0;
}


6848d3ec393c5e94f4a321dbc3d4001f_ec79674de1234901b5b8937629f46f53.png


左值匹配左值引用,右值匹配右值引用没有什么问题,当只有右值引用结果会是怎么样呢???


9df221b7a76fe001150282417ed305b0_7f895f6591d54fc8aa3728bc274383b0.png


有结果可知,当左值去匹配右值引用时,程序崩溃;那么是不是存在某种函数,既可以匹配左值同时也可以匹配右值呢???


模板中的&&万能引用


template<class T>
void PerfectForward(T&& t)
{
  Fun1(t);
}


万能模板可以匹配任何类型的数据,先检测上面的数据


int main()
{
  int i = 0;
  PerfectForward(i);
  PerfectForward(0);
  return 0;
}

e781ffa1f5077f8a00eea1ce51f64f24_87725a33ec5d4bd69d2c8061aad3df58.png


程序正常运行,证明了万能模板可以匹配左值也可以匹配右值


图解:

a1bc6c5e25c298a1287fb0389f9b4495_c28956dd8096406b8c09aeadecf246be.png


当左值i匹配模板时,模板会将其推演为int类型,实际类型是int&;当右值0匹配模板时,模板将其推演为int,实际类型是int&&


万能模板虽然解决了类型匹配的问题,但是又引出了一个新问题,为什么程序运行的结果都是左值引用呢???

解释起来也很简单,当右值被右值引用之后其属性变成了左值,所以全都调用的左值引用


为了解决这个问题,又引入了完美转发std::forward,在右值引用之后会保持其属性


template<class T>
void PerfectForward(T&& t)
{
  Fun1(std::forward<T>(t));
}


9484b855e47c1c2247440e96a226e7ee_61112568f2d54a6f84e75c9c39d0cf3b.png


至此,以上有关属性改变的问题已经全部解决


新的类功能


默认成员函数


C++11新增了两个默认成员函数:移动构造和移动赋值重载


对于新增的成员函数需要注意如下点:


如果没有实现移动构造函数,且没有实现析构函数,拷贝构造和赋值重载中的任一个,则编译器会自动生成一个默认移动构造:对于内置类型变量会执行按字节拷贝;对于自定义类型需要看成员是否实现移动构造,如果已经实现就调用移动构造,否则就调用拷贝构造

移动赋值重载亦是如此


强制生成默认成员函数的关键字:default


C++11可以让使用者更好地使用默认成员函数,如果需要使用某个默认函数,但是并没有实现,那么可以使用关键字default显示指定函数生成


namespace yjm
{
  class Person
  {
  public:
  Person(const char* name = " ", int age = 0)
    :_name(name)
    , _age(age)
  {
  }
  Person(const Person& p)
    :_name(p._name)
    ,_age(p._age)
  {
  }
  Person(Person&& p) = default;
  private:
  string _name;
  int _age;
  };
}
int main()
{
  yjm::Person s1("yjm",20);
  yjm::Person s2(s1);
  //调用由关键字生成的右值引用构造函数
  yjm::Person s3(std::move(s1));
  return 0;
}


1b29f9d7d617ef2f070482301cc1faba_c5159003b017462c973fa74b40f1d9e2.png


禁止生成默认成员函数的关键字:delete


既然存在强制生成,那么就会存在禁止生成;关键字delete便是为了禁止生成某种默认成员函数


复用上面的代码,运行结果如下


25341861b1ed07fc95686be93be50c6d_1ab04c9ef3034873a99882ae48ac4f8b.png


被关键字 delete修饰的默认成员函数是不可以被调用的


可变参数模板


C++11的新特征可变参数模板,可以创建接受可变参数的函数和类模板


如下就是一个可变参数的函数模板


//Args是模板参数包,args是函数形参参数包
//声明参数包Args... args,这个参数包可以包含0到任意个模板参数
template<class ...Args>
void showlist(Args... args)
{}


上面的参数args前面有省略号,所以它就是一个可变模板参数,把带有省略号的参数称为参数包,其包含0到任意个模板参数;无法直接获取参数包args中的每个参数,只能通过展开参数包的方式来获取每个参数


递归函数方式展开参数包


template<class T>
void showlist(const T& t)
{
  cout << t << endl;
}
template<class T,class ...Args>
void showlist(T value, Args... args)
{
  cout << value << " ";
  showlist(args...);
}
int main()
{
  showlist(1);
  showlist(1,1.1);
  showlist(1, 1.1,string("hello world"));
  return 0;
}

image.png


递归函数参数T value, Args... args;第一个参数接受传递的第一个参数,第二个参数接受剩余的剩余的传递参数;通过子递归函数将参数打印出来


逗号表达式展开参数包


仅供了解即可


template<class T>
void printarg(T t)
{
  cout << t << " ";
}
template<class ...Args>
void showlist(Args... args)
{
  int arr[] = { (printarg(args),0)... };
  cout << endl;
}
int main()
{
  showlist(1);
  showlist(1, 1.1);
  showlist(1, 1.1, string("hello world"));
  return 0;
}

83814f2a72dda9687913bd2d896a5c50_efcab993cf9a4e52862ae769ecde0191.png


STL容器中的emplace相关接口函数


fafa4093410f87c1ae77e32358f372b0_71c05634a86647c38df0915e90e6d38b.png


template<class ...Args>
void emplace_back(Args&&... args);


可以观察到容器list的emplace_back接口支持模板的可变参数;接下来就探索此接口有什么优点


int main()
{
  pair<int, yjm::string>kv(20, "sort");
  std::list<std::pair<int, yjm::string>>lt;
  lt.emplace_back(kv);//左值
  lt.emplace_back(make_pair(20, "sort"));//右值
  lt.emplace_back(10, "sort");//构造pair参数包
  cout << endl;
  lt.push_back(kv);//左值
  lt.push_back(make_pair(30, "sort"));//右值
  lt.push_back({ 40, "sort" });//右值
  return 0;
}

b97797aff6a9ea47b0d0caf51486278b_1ba2539121af4fc982224a23f2d066a2.png


由运行结果来看,emplace_back接口减少了拷贝提高效率


目录
相关文章
|
10月前
|
编译器 C++
多态(C++)上
多态(C++)
29 0
|
11月前
|
SQL Java 关系型数据库
mysql实现不存在就插入,存在就更新,sql直接执行和mybatis实现的坑!
insert into ... on duplicate key update 字段=新值, mybatis执行报错: SQLException: No value specified for parameter 4,你甚至惊奇的发现你只传了3个参数却提示没找到第4个参数......亲身经历什么叫一个bug找一天
293 0
|
9月前
|
存储 C语言 容器
C语言-史上最详细的通讯录
C语言-史上最详细的通讯录
43743 49
|
10月前
|
安全 Java 程序员
c++异常
c++异常
63 0
|
10月前
|
C语言
通讯录(C语言) 下
通讯录(C语言)
151 0
|
10月前
|
存储 编译器 C++
|
10月前
|
C语言 C++
初级C语言 函数 (一)
初级C语言 函数
47 0
|
10月前
|
存储 SQL 缓存
探秘MySQL底层架构:设计与实现流程一览
Mysql,作为一款优秀而广泛使用的数据库管理系统,对于众多Java工程师来说,几乎是日常开发中必不可少的一环。无论是存储海量数据,还是高效地检索和管理数据,Mysql都扮演着重要的角色。然而,除了使用Mysql进行日常开发之外,我们是否真正了解它的底层架构以及设计实现的流程呢?本篇博客将带您深入探索Mysql底层架构的设计与实现流程,帮助您更好地理解和应用这个强大的数据库系统。让我们一同揭开Mysql底层的神秘面纱,探寻其中的奥秘。
36348 14
探秘MySQL底层架构:设计与实现流程一览
|
10月前
|
C语言
通讯录(C语言) 上
通讯录(C语言)
151 0
|
10月前
|
存储 网络协议 Linux
Linux网络基础
Linux网络基础
63 0