cplusplusprimer_note9

c++11新标准

列表初始化可以用于new表达式中

int * ar = new int [4] {1,2,3,4};

创建对象时可以使用大括号而不是圆括号来调用构造函数

Stump s1(1,2);//old style
Stump S1{1,2};//c++ 11
Stump S1 = {1,2};//c++ 11

如果类将initializer_list作为参数的构造函数,则只有该构造函数可以使用列表初始化形式,就是说,列表初始化格式就不会用来初始胡其他类型的构造函数了

模板别名using=代替typedef

新格式可以用于模板部分具体化

template<typename T>
using arr12 = std::arrary<T,12>;
std::array<double,12> a1;
//替换为
arr12<double> a1;

作用域内枚举:

enum Old1 {yes, no, maybe};
enum class new1 {};
enum struct new2 {};

显示转换运算符explict

class Plebe
{
    Plebe(int);
    explict Plebe(double);

    operator int() const;
    explict operator double() const;
}

Plebe a, b;
a =5; //implict
b = 0.5; //not allowed
b = Plebe(0.5);//explict

int n = a; //implict
double x = b; //not allowed;
x = double(b); //explict

类内成员初始化:

class Session
{
    int mem1 = 10; //in-class initialization
    double mem2 {1.2};//in-class initialization
    short mem3;
}

可使用等会或者大括号版本的初始化但是不能使用圆括号版本的初始化,结果与在构造函数中用初始化列表一样。,但是会被构造函数初始化列表覆盖

基于范围的for循环:内置数组和包含begin和end的类。如果要在循环中修改内容,使用引用类型

cbegin cend 将元素视为const

左值:表示数据的表达式,程序可获取其地址

右值引用 && 可关联到右值,但不能对其应用地址运算符的值。右值包括字面常量(c风格字符串除外,它表示地址)、类似x+y的表达式以及返回的函数

int x = 10;
int y = 23;
int && r1 = 13;
int && r2 = x + y;
double && r3 = sqrt(3.0);

将右值关联到右值引用导致该右值被储存到特定位置,且可以获取该位置的地址.引入右值引用主要是为了实现移动语义

移动构造不复制,只是修改记录,类似于移动文件。

  • 复制构造 const左值引用,引用关联到左值实参,可能执行深复制
  • 移动构造 右值引用, 引用关联到右值实参,可能修改实际参数,不应该为const

给空指针调用析构函数不会引发问题

编译器会优化,甚至不调用移动构造函数,直接创建了后转移对象

复制赋值和移动赋值

强制移动

Useless choices[10];
Useless best;
int pick;
...//selec one object,set pick to index
best = choices[pick];

如果想要选择数组中的某个元素,移动其,并丢弃其他的,如何实现?默认方法行不通,因为choices[pick]是左值,这将调用复制构造函数。可以使用static_cast<>将对象的类型强制转换为Useless &&,但是更简单的方式是使用头文件utility中定义的函数move。move一定会导致移动操作,如果没有定义移动构造运算符则使用复制运算符,否则就报错了。

主要好处是利用使用右值引用实现的库代码

默认提供:默认构造函数,复制构造函数,复制赋值运算符,析构函数,移动构造函数,移动构造运算符。

如果提供了析构函数,复制构造函数或复制赋值运算符,不会自动提供移动构造函数和移动赋值运算符;如果提供了移动构造函数和移动赋值运算符,不会自动提供赋值构造函数和复制赋值运算符。

默认的移动构造函数和移动赋值运算符与复制版本类似,也是逐个成员初始化并复制内置类型,如果成员是类对象,将使用相应类的构造函数和赋值运算符,就像参数为右值一样。如果定义了移动构造函数和移动赋值运算符,则调用,否则使用复制版本的。

default关键字显式声明方法的默认版本,防止编译器不自动创建

delete关键字静止编译器使用特定方法,比如要禁止复制对象可以禁用复制构造函数和复制赋值运算符

class Someclass
{
    public:
    Someclass(const & Someclass &) = delete;
    Someclass & operator = (const Someclass &) = delete;
}

delete关键字可以用于禁止特定的转换。比如禁止int参数:

void redo(double);
void redo(int) = delete;

委托构造函数:成员初始化列表

c++98:让命名空间中函数可用的语法:

namespace Box
{
    int fn(int) {}
    int fn(double) {}
    int fn(const char *) {}
}
using Box::fn;//fn的所有重载版本都可用

也可以用这种方法让基类的所有非特殊成员函数对派生类可用:

class C1
{
    public:
    int fn(int j) {}
    double fn(double w) {}
    void fn(const char * s) {}
};

class C2 : public C1
{
    public:
    using C1::fn;
    double fn(double) {}
};

C2 c2;
int k = c2.fn(3); //using C1::fn(int)
double z = c2.fn(2.4); //using C2::fn(double)

c++11将这种方法用于构造函数。派生类继承基类的所有构造函数(默认构造函数,复制构造函数,移动构造函数除外),但是不不会使用与派生类构造函数的特征标匹配的构造函数。

class BS
{
    int q;
    double w;
    public:
    BS() : q(0), w(0) {}
    BS(int k ) : q(k), w(100) {}
    BS(double x) : q(-1), w(x) {}
    BS(int k, double x) : q(k), w(x) {}
};

class DR : public BS
{
    short j;
    public:
    using BS::BS; 
    DR() : j(-100) {}// DR needs its own default constructor
    DR(double x) : BS(2*x), j(int(x)) {}
    DR(int i) : j(-2), BS(i, 0.5 * i) {}
}

int main()
{
    DR o1; //use DR()
    DR o2(18.8); //use DR(double) instead of BS(double)
    DR o3(10, 1.9); //use BS(int, double)
}

注意基类的构造函数只会初始化基类成员,如果还要初始化派生类成员,要使用成员列表初始化语法

如果在基类中声明了虚方法,派生类中提供不同版本会覆盖旧的版本。但是如果特征标不匹配,则会隐藏旧版本。可以使用override标识符显式指出,在没有覆盖时会报错。final

特殊含义的标识符,编译器根据上下文确定是否有特殊含义,在其他上下文中,可以用作常规标识符,如变量名或者枚举。

函数对象:

  • 函数指针
  • 函数符
  • lambda函数
vector<int> numbers(1000);
generate(vector.begin(), vector.end(), rand);

bool f3(int x) {return x % 3 == 0;}
bool f13(int x) {return x % 13 == 0;}

//函数指针
int count3 = count_if(numbers.begin(), numbers.end(), f3);
int count13 = count_if(numbers.begin(), numbers.end(), f13);

class f_mod
{
    private:
    int dv;
    public:
    f_mod(int d = 1) : dv(d) {}
    bool operator() (int x) {return x % dv == 0;}
}

//函数符
int count3 = count_if(numbers.begin(), numbers.end(), f_mod(3));
int count13 = count_if(numbers.begin(), numbers.end(), f_mod(13));

//lambda函数
int count3 = count_if(numbers.begin(), numbers.end(), [] (int x) {return x % 3 == 0;});
int count13 = count_if(numbers.begin(), numbers.end(), [] (int x) {return x % 13 == 0;});
//仅当lambda表达式由一条返回语句组成时自动类型推断才管用,否则需要使用新增的返回类型后置语法:
[](double x)->double{int y = x; return x - y;} //ret

为什么用lambda?距离,简洁,效率和功能

函数指针方法阻止了内联,因为编译器不会内联其地址被获取的函数,函数地址的概念意味着非内联函数,而函数符和lambda通常不会阻止内联

lambda有一些额外的功能,可以访问作用域内的任何动态变量,要捕获需要使用的变量,可将其名称放在中括号中,

[z]//按值访问z变量
[&count] //按引用访问变量
[&]//按引用访问所有变量
[=]//按值访问所有变量
[ted, &ed]//按值访问ted,按引用访问ed
[&, ted] //按引用访问所有变量,按值访问ted
[=, &ed] //按值访问所有变量,按引用访问ed

int count3 = 0;
int count13 = 0;
for_each(numbers.begin(), numbers.end(), [&] (int x) {count3 += x % 3 == 0; count13 += x % 13 == 0;});

包装器:

bind:可用来代替bind1st和bind2ed 更灵活

mem_fn允许将成员函数作为常规函数传递

reference_wrapper能够创建行为对象像引用但是能够被复制的对象

function:统一方式处理多重类似函数的形式

answer = ef(q);

ef可能是类似函数的很多东西,导致模板化的效率变低

functional头文件

调用的特征标相同就行

function<double(char, int)> f;
typedef function<double(char, int)> fdci;
fdci(dub);

可以将接受char参数和int参数,返回double参数的任何类函数对象赋值给f

可变参数模板

模板参数包,函数参数包:

template<typename... Args>
void show(Args... args)
{
    show(args...);//展开参数包
}

template<typename T,typename... Args>
void show(T value, Args... args)
{
    cout << value << endl;
    show(args...);//展开参数包
}

//添加一个只处理一项的版本,少打印endl
template<typename T>
void show(T value)
{
    cout << value;

}
void show(T value, const Args&... args)//可以按引用传递

Args是模板参数包,包含类型列表,args是函数参数包,包含值列表

并行编程:thread_local:把变量声明为静态储存,持续性与特定线程相关,线程过期变量也将过期

random头文件,

chrono头文件处理时间间隔

tuple头文件支持模板tuple 广义的pair

regex头文件

constexpr:在编译阶段计算结果为常量的表达式,const变量可以储存在只读内存中

调试工具assert

关键字static_assert编译阶段对断言进行测试,对于在编译阶段实例化的模板调试起来更简单

()

发表评论