C++11学习总结

空指针常量 nullptr

NULL通常在C语言中预处理宏定义为(void*)0或者0,这样0就有int型常量和空指针的双重身份。但是C++03中只允许0宏定义为空指针常量,这就会造成如下的错误:

void foo(int n);
void foo(char* cArr);

上面声明了两个重载函数,当我调用foo(NULL),编译器将会调用foo(int)函数,而实际上我是想调用foo(char*)函数的。为了避免这个歧义,C++11重新定义了一个新关键字nullptr,充当单独空指针常量

新增基于范围的for循环

类似Java中foreach语句,为遍历数组提供了很大方便。

int nArr[5] = {1,2,3,4,5};
for(int &x : nArr)
{
    x *=2;   //数组中每个元素倍乘
}

自动类型推断 auto

编译器可以根据初始值自动推导出类型,但是不能用于函数传参以及数组类型的推导。无需提前声明定义的变量的数据类型。例如:

auto i = 1;    //编译器自动推断i为int类型

这个功能在各种标准模板库容器中使用,作用更突出。如下:

vector<int> vec(6,10);
vector<int>::iterator iter = vec.iterator();
auto iterAuto = vec.iterator(); //相比较上一句方便很多

Lambda表达式

如果代码里面存在大量的小函数,而这些函数一般只被一两处调用,那么不妨将它们重构成Lambda表达式,也就是匿名函数。作用就是当你想用一个函数,但是又不想费神去命名一个函数。
  该功能函数实际上在其他面向对象语言中早就存在,例如Java,Python都定义了该功能。C++中Lambda表达式格式如下:

[capture-list] (parameters) mutable -> return-type { statement }

1、lambda表达式各部分说明

[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。

(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略

mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分
可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。

{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

注意: 在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。

因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。

int main()
{
 // 最简单的lambda表达式, 该lambda表达式没有任何意义
 []{}; 
 
 // 省略参数列表和返回值类型,返回值类型由编译器推导为int
 int a = 3, b = 4;
 [=]{return a + 3; }; 
 
 // 省略了返回值类型,无返回值类型
 auto fun1 = [&](int c){b = a + c; }; 
 fun1(10)
 cout<<a<<" "<<b<<endl;
 
 // 各部分都很完善的lambda函数
 auto fun2 = [=, &b](int c)->int{return b += a+ c; }; 
 cout<<fun2(10)<<endl;
 
 // 复制捕捉x
 int x = 10;
 auto add_x = [x](int a) mutable { x *= 2; return a + x; }; 
 cout << add_x(10) << endl; 
 return 0; }

2、捕获列表说明

捕捉列表描述了上下文中哪些数据可以被lambda使用,以及使用的方式传值还是传引用。

[var]:表示值传递方式捕捉变量var
[=]:表示值传递方式捕获所有父作用域中的变量(包括this)
[&var]:表示引用传递捕捉变量var
[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
[this]:表示值传递方式捕捉当前的this指针

注意:
a. 父作用域指包含lambda函数的语句块
b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。
比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量 [&,a, this]:值
传递方式捕捉变量a和this,引用方式捕捉其他变量
c. 捕捉列表不允许变量重复传递,否则就会导致编译错误。 比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复
d. 在块作用域以外的lambda函数捕捉列表必须为空。
e. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都
会导致编译报错。
f. lambda表达式之间不能相互赋值,即使看起来类型相同

初始化列表

C++11 提供了统一的语法来初始化任意的对象,例如:

struct A {
    int a;
    float b;
};
struct B {

    B(int _a, float _b): a(_a), b(_b) {}
private:
    int a;
    float b;
};

A a {1, 1.1};    // 统一的初始化语法
B b {2, 2.2};

C++11 还把初始化列表的概念绑定到了类型上,并将其称之为 std::initializer_list,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如:

#include <initializer_list>

class Magic {
public:
    Magic(std::initializer_list<int> list) {}
};

Magic magic = {1,2,3,4,5};
std::vector<int> v = {1, 2, 3, 4};

对非内置类型成员变量,为了避免两次构造,推荐使用类构造函数初始化列表。但有的时候必须用带有初始化列表的构造函数:

  1. 成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
  2. const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。

显示重写(覆盖)override和final

在C++03中,很容易让你在本想重写基类某个函数的时候却意外地创建了另一个虚函数。例如:

struct Base {
    virtual void some_func(float);
}; 
struct Derived : Base {
    virtual void some_func(int);
};

本来Derived::some_func函数是想替代Base中那个函数的。但是因它的接口不同,又创建了一个虚函数。这是个常见的问题,特别是当用户想要修改基类的时候。
  C++11引入了新的语法来解决这个问题:

struct Base {
    virtual void some_func(float);
}; 
struct Derived : Base {
    virtual void some_func(int) override; // 病态的,不会重写基类的方法
};

override 这个特殊的标识符意味编译器将去检查基类中有没有一个具有相同签名的虚函数,如果没有,编译器就会报错!
  C++11还增加了防止基类被继承和防止子类重写函数的能力。这是由特殊的标识符final来完成的,例如:

struct Base1 final { };

struct Derived1 : Base1 { }; // 病态的, 因为类Base1被标记为final了

struct Base2 {
    virtual void f() final;
};

struct Derived2 : Base2 {
    void f(); // 病态的, 因为虚函数Base2::f 被标记为final了.
};

在这个例子中, virtual void f() final;语句声明了一个虚函数却也阻止了子类重写这个函数。它还有一个作用,就是防止了子类将那个特殊的函数名与新的参数组合在一起。
  需要注意的是,override和final都不是C++语言的关键字。他们是技术上的标识符,只有在它们被用在上面这些特定的上下文在才有特殊意义。用在其它地方他们仍然是有效标识符。

智能指针

C++11新增了std::shared_ptr、std::weak_ptr等类型的智能指针,用于解决内存管理的问题。

shared_ptr

std::shared_ptr包装了new操作符动态分配的内存,可以自由拷贝复制,基本上是使用最多的一个智能指针类型。

shared_ptr的原理是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。例如:比特老师晚
上在下班之前都会通知,让最后走的学生记得把门锁下。

  1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源。
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
#include <iostream>
#include <memory>
 
struct C
{
  C(int i) : i(i) {std::cout<<"C(int i)"<<i<<'\n';} 
  ~C(){std::cout<<"~C("<<i<<')'<<'\n';}
  int i;
};
 
int main()
{
    auto sp = std::make_shared<C>(12);
    std::shared_ptr<C> tmp(new C(13));
    std::cout << tmp.use_count() << '\n';
    std::shared_ptr<C> tmp1(tmp);
    std::cout << tmp1.use_count() << '\n';
}
//输出
C(int i)12
C(int i)13
1
2
~C(13)
~C(12)

STL容器

std::atomic_flag、std::atomic

  1. std::atomic_flag
    std::atomic_flag是一个原子的布尔类型,可支持两种原子操作:

    test_and_set():如果atomic_flag对象被设置,则返回true; 如果atomic_flag对象未被设置,则设置之,返回false
    clear():清除atomic_flag对象
    std::atomic_flag可用于多线程之间的同步操作,类似于linux中的信号量。使用atomic_flag可实现mutex.

  2. std::atomic
    std::atomic对int, char, bool等数据结构进行原子性封装,在多线程环境中,对std::atomic对象的访问不会造成竞争-冒险。利用std::atomic可实现数据结构的无锁设计。

  3. 示例

std::atomic<bool> ready(false);
std::atomic_flag flag = ATOMIC_FLAG_INIT;

右值引用

C++11 标准新引入了另一种引用方式,称为右值引用,用 “&&” 表示。

需要注意的,和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行初始化,比如:

int num = 10;
//int && a = num;  //右值引用不能初始化为左值
int && a = 10;

和常量左值引用不同的是,右值引用还可以对右值进行修改。例如:

int && a = 10;
a = 100;
cout << a << endl;

程序输出结果为 100。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CET4_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值