C++ 11 复习要点

本文详细总结了C++11的关键特性,包括auto关键字、nullptr、范围for循环、大括号初始化器等语法改进,智能指针unique_ptr和shared_ptr在内存管理上的应用,新引入的std::array、std::forward_list等容器,以及lambda表达式的使用方法,最后探讨了移动语义和右值引用的概念及其重要性。
摘要由CSDN通过智能技术生成

C++11在C++98基础上增加了很多新的特性。本文来总结一下那些主要的特性,记住这些特性就可以了。

一、关键字和新语法

1.1 auto关键字

C++11以前,auto是用来表明变量是放在自动存储区(栈区)的。C++11开始,废弃了这个功能,转而用来进行类型判断,编译器会根据上下文来判断auto变量的类型。auto作为函数返回值时,只能用于定义函数,不能用于声明函数。下面的写法是可行的。也就是说auto附近必须有上下文可以推断类型。
//Test.h 示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#pragma once
class Test
{
public:
    auto TestWork(int a, int b)
    {
        return a + b;
    }
};

1.2 nullptr关键字

以前,我们都是用NULL 或者0来表示空指针,但是由于NULL可以隐式转换为 整型,在函数调用时可能会出现问题。比如:
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
void TestWork(int index)
{
std::cout << "TestWork 1" << std::endl;
}
void TestWork(int * index)
{
std::cout << "TestWork 2" << std::endl;
}
};

int main()
{
Test test;
test.TestWork(NULL);
test.TestWork(nullptr);
}
上面的代码,用NULL调用时,会先匹配整型,这不是我们希望的。而使用nullptr就可以规避这个问题,因为nullptr不允许隐式转换为整型,只允许隐式转换为bool型(false)和任意指针类型(空指针)。

1.3 基于范围的for循环

C++11 引入了一种更为简单的for语句,这种for语句可以很方便的遍历容器或其他序列的所有元素。
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int main()
{
int numbers[] = { 1,2,3,4,5 };
std::cout << "numbers:" << std::endl;
for (auto number : numbers)
{
std::cout << number << std::endl;
}

1.4 大括号初始化器

C++11中全面加入了列表初始化的功能,包括对vector,map,值类型,struct等等都可以使用列表初始化,还可以在函数中返回一个花括号括起来的列表,而在这之前我们只能对数组进行列表初始化。
//数组列表初始化
int xx[5]={1,2,3,4,5};
int yy[]={6,7,8,9,0};
 
//值类型进行初始化
int a{10};
int b={10};
int c={10.123}; // 编译器报错,g++ 5.3.1当列表初始化用于值类型的时候,如果有精度损失,编译器会报错。
 
//列表初始化还可以用结构体
typedef struct Str{
   int x;
   int y;
}Str;
Str s = {10,20};
 
//列表初始化类,必须是public成员,如果含有私有成员会失败
class Cls{
public:
   int x;
   int y;
};
Cls c  = {10,20};
 
//vector不仅可以使用列表初始化,还可以使用列表进行赋值,数组不能用列表赋值
vector<int>v1={1,2,3,4,5,6,7,8,9}; // 初始化
vector<int>v2;
v2={3,4,5,6,7}; //赋值
 
//map列表初始化
map<string ,int> m = {
      {"x",1},
      {"y",2},
      {"z",3}
};
 
//用函数返回初始化列表只展示关键代码,相关头文件自行添加
//同理结构体,类,map的返回也可以使用初始化列表返回
vector<int> getVector()
{
  return {1,2,3,4,5};
}
 
int main()
{
  vector<int> v = getVector();
  cout<<v[0]<<v[1]<<v.size()<<endl;
  return 0 ;
}

1.5 long long类型(长整型)

long long 类型实际上没有在C++ 98中存在,而之后被C99标准收录,其实现在市面上大多数编译器是支持 long long 的,但是这个类型正式成为C++的标准类型是在C++11中。标准要求long long至少是64位也就是8个字节。一个字面常量使用LL后缀表示long long类型,使用ULL后缀表示unsigned long long 类型。

1.6 char16_t 与 char32_t 类型(宽字符型)

为了表示更宽的字符集,C++11提供了2字节和4字节字符整型

1.7 raw 字符串 R"(  )"

C++11提供了写字符串常量的方式,R"()"内的内容会原原本本的显示出来,不会有转义的问题,正则表达式这种字符串很友好。

1.8 using 类型别名

类型别名其实早在C语言中就有了,一般情况下我们使用关键字typedef来声明一个类型的别名,在C++11中增加了另一种声明类型别名的方法就是使用using关键字,using关键字在C++11以前一般用来引用命名空间。
typedef int INT; // 右侧符号代表左侧
using INT2 = int; // 左侧符号代表右侧

INT a = 20;
INT2 b = 30;

1.9 decltype类型指示符

有时候会有这样的需求,我们需要知道一个表达式的类型,并使用该类型去定义一个变量,例如:

int a = 10;
int b = 20;
auto c = a + b; // OK a+b的类型是int,此时c的类型是int,并且c的值是 a+b

auto可以解决部分问题,例如我们定义的变量的类型就是表达式 a+b 的类型,但是如果我们仅仅需要定义一个与表达式 a+b 的类型相同的变量,但是我们又不希望将表达式a+b的值赋值给刚刚定义的变量,我们希望赋另外一个值或者是仅仅定义变量而不赋值呢。 这就需要用到C++11 提供的另一个类型说明符 decltype了。decltype作用于一个表达式,并且返回该表达式的类型,在此过程中编译器分析表达式的类型,并不会计算表达式的值。例如
int a = 10;
int b = 20;
decltype(a+b) c = 50; // OK c的类型就是 a+b 的类型int
对于引用类型decltype有一些特别的地方:
int a = 20 ;
int &b = a;
decltype(b) c ;  // Error c是引用类型必须赋值
decltype(b) d = a; // OK  d是引用类型,指向a
可以看到decltype如果作用于一个引用类型,其得到的还是一个引用类型。我们知道一个引用类型在使用的时候一般会当作其关联的那个变量的同义词处理,例如如果使用 cout<<b<<endl; 其中b实际上相当于a,但是decltype作用于引用类型的时候会保留引用性质。
如果一个表达式是一个解指针引用的操作,decltype得到的也是一个引用类型:
int a = 20 ;
int *p = &a;
decltype(*p) c = a;  // c的类型是int&
c = 50;
cout<<a<<endl;  // 输出50
当decltype作用于一个变量的时候,变量加不加括号是有区别的,例如:
int a = 20;
decltype(a) b = 30; //ok b的类型是 int
decltype((a)) c = a ; // ok c的类型是int& 其关联变量 a
加上括号之后编译器会把(a)当作是一个表达式处理,而变量是一种可以作为赋值语句左值的表达式,所以会解释成引用类型。

二、智能指针与内存管理

C++11 从boost 库中引入了share_ptr,unique_ptr,weak_ptr做智能指针,进行内存管理。C++11以前,有auto_ptr,但是一个对象只能有一个auto_ptr持有(防止重复析构所引用的对象),它利用的是栈内存释放规则,回收auto_ptr时,调用它的析构函数,在析构函数里调用所引用对象的析构。C++11摒弃了auto_ptr,使用另外三个来做动态内存管理。 智能指针是为了解决堆内存管理而存在的。要是用智能指针,必须包含memory头文件,这些智能指针的定义都在这个文件中:
#include <memory>
智能指针重载了解除引用运算符 * 使得他们可以像普通指针一样来获得所引用的对象的内容。
operator * 接触引用
T* get() 获得对象的地址

2.1 unique_ptr

unique_ptr作为auto_ptr的替代品,比auto_ptr更安全,因为它不允许赋值,也就是不能把对象所有权转移,会抛出编译错误。如:
unique_ptr<string> p1(new string("hello")); // #1
unique_ptr<string> p2 = p1;                        // #2
p2 = p1;                                                       // #3
#2和#3都是违法的,不允许使用复制构造函数和赋值运算符,这种特性是通过C++11的移动构造函数和右值引用实现的。
而在auto_ptr中,上面的语法都是正确的:
auto_ptr<string> p1(new string("string"));
auto_ptr<string> p2 = p1; 
p2 = p1; 
int n = p1.size(); // 错误,p1已经丧失所有权
p2会获得堆变量new string("sitnrg")所有权,如果再次使用p1,程序在 运行时会崩溃,这样很不安全,于是引入了unique_ptr来禁止引用转移。

2.2 shared_ptr

shared_ptr允许多个shadred_ptr引用同一个对象,和unique_ptr相反。

shared_ptr允许多个指针指向同一个对象,unique_ptr则独占所指向的对象,我们主要说明shared_ptr的使用。通过使用make_shared<type>()函数产生智能指针对象。

1
2
shared_ptr< int > p = make_shared< int >(40);  // p指向一个值为40的int对象
shared_ptr<string> p2 = make_shared<string>(10, 'c' );  //指向值为'cccccccccc'的string对象

make_shared<type>()函数中传递的值要与对应的type的构造函数相匹配,实际上应该就是直接调用的对应type的构造函数。

我们可以使用new初始化的指针来初始化智能指针:

1
2
share_ptr< int > p ( new 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值