C++11新标准

  • 新类型
    • 新增long long 和unsigned long long ,用来支持64(或更宽)的整形;
    • 新增 char16_t 和char_32_t ,用来支持16位和32位的字符表示;
  • 统一的初始化
    • C++11扩大了大括号括起的列表的使用范围,使其可以用于所有内置类型和用户定义的类型(即类对象)。使用初始化列表时,可以不添加等号(=)。
    • 初始化列表语法可以防止缩窄,即禁止将数值赋给无法存储它的数值变量。编译器禁止将值存储在比它 “窄” 的变量中。但是允许转换为更宽的类型。另外,只要值在较窄类型的取值范围内,将其转换为较窄的类型也是允许的。
    • C++11提供了模板类initializer_list,可将其用作构造函数的参数。如果类有接受initializer_list 作为参数的构造函数,则厨师阿虎列表语法就只能用于该构造函数。列表中的元素必须是同一种类型或者可转换为同一个类型。STL容器提供了将initializer_list作为参数的构造函数。
vector<int> a1(10);  //vector有10个元素,没有初始化
vector<int> a2{10};  //a2有一个元素,那个元素是10
vector<int> a3{4,6,1};   //a3里面有3个元素4、6、1.
- 除了可以用做构造函数外,还可以将initializer_list 用做常规函数的参数。
#include<initalizer_list>

double sum(std::initalizer_list<double> i1)
{
	double tot = 0;
	for(auto p = i1.begin();p != i1.end();++p)
		tot += *p;
	return tot;
}
int main()
{
	double total = sum({2.5,3.1,4});
}
  • 声明
    • auto,以前是一个存储类型说明符,没有什么用。于是C++11将其用于实现自动类型推断。这要求进行显式初始化,让编译器能够将变量的类型设置为初始值的类型。并且auto还可以简化模板的声明。
    • decltype,将变量的类型声明为表达式指定的类型。decltype(x) y; 让y的类型与x相同,其中x 是一个表达式。
      • decltype 比这些示例演示的要复杂些。为确定类型,编译器必须遍历一个核对表。decltype(expression) var;。核对表分三步。
      1. 第一步:如果expression 是一个没有用括号括起的标识符,则var 的类型与该标识符的类型相同,包括const 等限定符。
      2. 第二步:如果expression 是一个函数调用,则var 的类型与函数的返回类型相同。注意:并不会调用函数,编译器通过查看函数的原型来获悉返回类型,而无需实际调用函数。
      3. 第三步:如果expression 是一个左值,则var 为指向其类型的引用。但是此时的expression 使用括号括起来的标识符。
      4. 第四步:如果上面的条件都不满足,则var 的类型与 expression 的类型相同。
    • 另一种函数声明语法(c++后置返回类型)
      • 有一个相关的问题是decltype 本身没有办法解决的,看看下面的模板函数:
      template<class T1, class T2>
      type?? gt(T1 x, T2 y)
      {
      	....
      	return x+y;
      }
      
      • 上面的代码中我们无法预先知道将x和y相加得到的类型。可以将返回类型设置为 decltype(x+y),但是不幸的是,此时还没有声明参数x和y,他们不再作用域中,必须在声明参数后使用decltype。因此C++新增了一种声明和定义函数的语法。
      • auto h(int x,float y) -> double。这将返回类型移到了参数声明后面。->double 被称为后置返回类型。其中auto 是一个占位符,表示后置返回类型提供的类型。因此我们可以这样写代码
      template<class T1,class T2>
      auto gt(T1 x, T2 y) -> decltype(x + y)
      {
      	...
      	return x+y;
      }
      
      • 上面的代码中,decltype 在参数声明后面,因此 x 和 y 位于作用域内,可以使用它们。
    • 模板别名:using=
      • 以前我们创建别名是使用typedef:typedef std::vector<std::string>::iterator itType;
      • c++11提供了另一种创建别名的方法,using itType = std::vector<std::string>::iterator;
      • 差别在于,新语法也用于模板部分具体化,但是typedef 不能:
      template<typename T>
      using arr12 = std::array<T,12>;
      
      • 上述语句具体化模板array<T,int>(将参数int设置成12)。
    • nullptr
      • 空指针是不会指向有效数据的指针。以前,C++在源代码中使用0表示这种指针,就导致了一个问题,0既可以表示指针常量,又可以表示整形常量。C++11新增关键字nullptr,用于表示空指针;他是指针类型,不能转换为整形类型。为向后兼容。C++11允许使用0来表示空指针,因此表达式 nullptr==0 为 true ,但使用nullptr 而不是 0 提供了更高的类型安全。注意:可将0传递给接收int参数的函数,但如果您试图将nullptr 传递给这样的函数,编译器将此视为错误。
  • 智能指针:C++11撇弃了auto_ptr,并新增了三种智能指针:unique_ptr、shared_ptr、weak_ptr。
  • 异常规范方面的修改
    • C++11撇弃了异常规范,但是指出函数不会引发异常有一定的价值,因此添加了关键字 noexcept。
      void func(short,short) noexcept;//表明函数不会抛出异常
  • 对类的修改
    • 显式转换运算符 explicit
    • 类内成员初始化
      • 可以在类中中对成员进行初始化,可以使用等号或者大括号版本的初始化,但不能使用圆括号版本的初始化。
      • 如果构造函数在成员初始化列表中提供了相应的值,这些默认值将被覆盖。
  • 模板和STL方面的修改
    • 基于范围的for循环,如果在循环中修改数组或者容器的每个元素,可使用引用类型。
    • 新的STL容器
      • forward_list 单向链表,只能沿一个方向遍历。与双向链表的list 容器相比,它更简单,在占用空间方面更加经济。
      • unordered_map、unordered_multimap、unordered_set、unordered_multiset。这四种容器都是使用哈希表实现的。
      • C++11还新增了模板array。要实例化这种模板,可指定元素类型和固定的元素数:std::array<int,360> ar; //数组array中有360个int型的数据。
    • 新的STL方法
      • 新增了cbegin()和cend()。与begin()和end()一样,这些新方法也返回一个迭代器,指向容器的第一个元素和最后一个元素的后面,因此可用于指定包含全部元素的区间。另外,这些新方法将元素视为const。于此类似,crbegin()和crend()是rbegin()和rend()的const版本。
  • 右值引用
    • 使用&&表示,右值引用可关联到右值,即可出现在赋值表达式右边,但是对其应用地址运算符的值。右值包括字面常量(C-风格字符串除外,它表示地址)、诸如x+y表达式以及返回值的函数(条件是该函数返回的不是引用)。
    • 将右值关联到右值引用导致该右值被存储到特定的位置,且可以获取该位置的地址。比如:int && r1 = 13; 虽然不能将运算符&用于13,但可将其用于r1,。通过将数据与特定的地址相关联,使得可以通过右值引用来访问该数据。
  • 移动语义和右值引用
    • 先来看一段代码,感觉一下C++11之前的复制过程
    vector<string> vstr;   //vector中有2000个元素,每个元素都是string类型的,每个string的字符串都有1000个字符。
    vector<string> vstr_copy1(vstr); //进行复制
    
    • vector和string 类都使用动态内存分配,因此他们必须定义使用某种new版本的复制构造函数。为初始化对象vstr_copy1,复制构造函数vector < string > 将使用new 给2000个string对象分配内存,而每个string对象又将调用string的复制构造函数,该构造函数使用new 为1000个字符分配内存。接下来,全部2000000个字符都将从vstr控制的内存中复制到vstr_copy1控制的内存中。
    • 上述工作量很大,我们再来看一段代码
    vecctor<string> allcaps(const vector<string & vs>)
    {
    	vecctor<string> temp;
    	...
    	return temp;
    }
    vector<string> vstr;
    vector<string> vstr_copy(allcaps(vstr));
    
    • 在函数allcaps()中,创建了对象temp,该对象管理着2000000个字符;vector和string 的复制构造函数创建这2000000个字符的副本,然后程序删除 allcaps() 返回的临时对象。这里的要点是做了大量的无用功。考虑到临时副本被删除了,如果编译器将对数据的所有权直接转让给vstr_copy,不是更好吗?也就是说,不将2000000个字符复制到新的地方,在删除原来的字符,而将字符留在原来的地方,并将vstr_copy 与之相关联。
    • 移动构造函数解析
      • 首先,右值引用让编译器知道何时可使用移动语义。
      • 第二步,编写移动构造函数,使其提供所需的行为。
    • 移动赋值运算符
Useless & Useless::operator=(const Useless & f)
{
	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)
{
	if (this == &f)
		return *this;
	delete[] pc;
	n = f.n;
	pc = f.pc;
	f.n = 0;
	f.pc = nullptr;
	return *this;
}
  • 强制移动,就是明明是左值,但是我就想要让他变成右值,来使用它。
    • 第一种方法:我们可以使用运算符 static_cast<>将对象的类型强制转换成 Useless &&。
    • C++11提供了一种更简单的方式,使用头文件 utility 中声明的函数 std::move()。
    • 我们需要注意的一点是:std::move()并非一定会导致移动操作。看下面的例子,Chunk 是一个包含私有数据的类,见下面代码
    Chunk one;
    ...
    Chunk two;
    two = std::move(one);  
    
    • 上述表达式 std::move(one) 是右值,因此上述赋值语句将调用 Chunk 的移动赋值运算符—前提是定义了这样的运算符。但是如果Chunk 没有定义移动运算符,编译器将使用复制赋值运算符。如果也没有定义复制赋值运算符,将根本上不允许上述赋值。
    • 对于我们来说,右值引用的好处在于能够使用右值引用实现移动语义的库代码。例如:STL类中现在都有复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符。
  • 新的类功能
    • 在原有的4个特殊成员函数(默认构造函数、复制构造函数、复制赋值构造函数和析构函数)的基础上,C++11新增了两个:移动构造函数和移动赋值运算符。这些成员函数是编译器在各种情况下自动提供的。
    • 默认的方法和禁止的方法
      • 假定我要使用某个默认的函数,而这个函数由于某种原因不会自动创建。那么我们可以使用 default 来显示的声明这些方法的默认版本。还有一点,关键字 default 只用于6个特殊的成员函数。比如:我们自己提供了移动构造函数,因此编译器不会自动创建默认的构造函数、复制构造函数和复制赋值构造函数(=运算符重载)。
      • 关键字 delete 可用于禁止编译器使用特定方法。可以用于任何函数。delete 还有一种用法就是禁止特定的转换。比如:
      //第一种情况
      void redo(double);
      redo(5);  //int值5将被提升为5.0
      //第二种情况
      void redo(double);
      void redo(int) = delete;
      redo(5);
      //在这种情况下,redo(5)与redo(int) 相匹配。编译器检测到这一点以及redo(int) 被禁用后,
      //将这种调用视为编译错误。
      
      • 因此这说明了 delete 关键字的用法:它只用于查找匹配函数,使用它们将导致编译错误。
    • 委托构造函数
      • C++11允许我们在一个构造函数的定义中使用另一个构造函数,这被称为委托,因为构造函数暂时将创建对象的工作委托给另一个构造函数。
      class Notes {
      int k;
      double x;
      std::string st;
      public:
      	Notes();
      	Notes(int);
      	Notes(int, double);
      	Notes(int, double, std::string);
      };
      
      Notes::Notes(int kk, double xx, std::string stt) : k(kk), x(xx), st(stt) {}
      Notes::Notes() : Notes(0, 0.1, "oh") {}
      Notes::Notes(int kk) : Notes(kk, 0.01, "dd") {}
      Notes::Notes(int kk, double xx) : Notes(kk, xx, "uh") {}
      
    • 管理虚方法:override 和 final
      • 虚方法对实现多态类层次结构很重要。假设基类声明了一个虚方法,而您决定在派生类中提供不同的版本,这将覆盖旧版本。如果特征标不匹配,将隐藏而不是覆盖旧版本。
      • 在C++11中,可使用虚说明符 override 指出您要覆盖一个虚函数:将其放在参数列表后面。如果声明与基类方法不匹配,编译器将视为错误。
      • 说明 final 解决了另一个问题。您可能想禁止派生类覆盖特定的虚方法,为此可在参数列表后面加上 final 。
      • 注意:说明符 override 和 final 并非关键字,而是具有特殊含义的标识符。这意味着编译器根据上下文确定它们是否有特殊含义;在其他上下文中,可将他们用作常规标识符,如变量名或者枚举之类的。
  • Lambda函数
    • [] (int x) { return x % 3 == 0; }bool f( int x ) { return x % 3 == 0; }
    • 差别主要有两个,使用 [] 替代了函数名;没有声明返回类型。返回类型相当于使用 decltype 根据返回值推断得到的,上面的就是 bool 。如果 Lambda 不包含返回语句,推断出的返回类型将为 void 。
    • 仅当lambda表达式完全由一条返回语句组成时,自动类型推断才管用;否则,需要使用新增的返回类型后置语法:[] (double x) ->double { int y = x; return x - y; }
    • 最后,lambda 有一些额外的功能。具体来说,lambda 可访问作用域内的任何动态变量;要捕获要使用的变量,可将其名称放在中括号内。如果只指定了变量名,如 【z】,将按值访问变量;如果在名称前加上 & ,如 【&count】,将按引用访问变量。 【&】让您能够按引用访问所有动态变量,而【=】让您能够按值访问所有动态变量。还可以混用这两种方式。
    int count13;
    ...
    count13 = std::count_if(numbers.begin(), numbers.end(), [](int x){return x %13 == 0;});
    
    可以替换成下面这种代码
    int count13 = 0;
    std::for_each(numbers.begin(), numbers.end(), [&count13](int x){count13 += x % 13 == 0;});
    
  • 好了,今天的c++11标准先介绍在这里,我们后面再来说。
  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值