最近完成了c++11的具体内容的学习,因为是随性记录的笔记,没有格式.
有些东西不好记录,但是一看书就会明白,有些内容我也只是走马观花,推荐使用c++ primer 第六版后面的那个c++11特性的汇总来学习,还有一本书是深入理解c++11新特性(貌似是这个书,百度下就能找到,很好找.)
下面是笔记内容。
预定义宏的新增加
func 这个宏用来返回函数的名字,并且也可以用在类和结构体中。
_Pragma这是个操作符,使用格式_Pragma(字符串字面量) eg: _Pragma(“once”) 等同于 #pragma once
变长参数的宏定义,以及VA_ARGS。变长参数的宏定义可以用…来表示,而预定义宏VA_ARGS则可以在宏定义的实现,部分替换省略号代表的字符串.#define PR(…) printf(VA_ARGS)
数类型的等级规则,长度越大的等级越高..长度相同下,标准整型的等级高于扩展类型,相同大小的有符号类型和无符号类型等级相同,所以转换规则,是低等级向高等级转,有符号向无符号转.
如果要扩展整型,那么必须符合C++11标准(有符号和无符号的相同类型占同样大小的内存空间)
__cplusplus
静态断言static_assert();静态断言是用来在编译期间进行断言的工具.相比传统的断言在运行调用时才会执行,静态断言在编译期间运行.
noexcept修饰符与noexcept操作符
快速初始化成员变量
在c++11中,允许非静态成员变量使用=或者{}进行非静态成员变量的初始化.
Class init
{
int a=1;
Double b{1.2};
};所以应该全面使用花括号来初始化.
Class init
{
Int a = 1;
String b{“hello”};//不能使用()
C c{1};//不能使用()
};
非静态成员的sizeof
11标准中可以对非静态成员变量使用sizeof操作符.
Sizeof(people::hand);
扩展的friend语法
Class poly;
Typedef poly p;
Class lilei
{
friend class poly; // C++ 98通过 c++11通过
};
Class jim
{
Friend poly;// C++ 98不通过 c++11通过
};
Class hanmeimei
{
Friend P;// C++ 98不通过 c++11通过
};
也就是在声明一个类为另一个类的友元时不再需要使用class关键字,甚至还可以使用别名.
Final和override控制
Final关键字是用来阻止派生类覆盖它修饰的虚函数.也就是阻止一个接口的重载性.
Override 关键字是用来标记该函数必须是重载其基类中的同名函数否则代码无法通过编译…
模版函数的默认模版参数
也就是函数的模版有支持带默认参数了.
Template<typename T = int>
Void fun(T arg)
{
}
外部模板意思就是这个函数的实现的是文件外的一个函数模版,外部模版的使用的同时也要使用显式实例化.外部模版的语法就是在模版的显示实例化的声明前面加上extern
c++11还加入了局部和匿名类型作为模版的实参,也就是可以是用下面的这些类型
Typedef Struct
{
Int a ;
Int b;
}B;
继承构造函数
有时希望能够像继承普通的函数那样继承基类的构造函数.所以新标准支持了继承构造函数.
使用关键字using 来完成基类的构造函数的继承.
Using base::base;
继承的基类构造函数只能初始化基类的成员
另外有时候还会遇到一些这样的问题,多继承中可能存在继承冲突的问题,比如:
Struct A{A(){}};
Struct B{B(){}};
Struct C:A,B
{
using A::A;
UsingB::B;
};
这个时候就是存在冲突了.
可以这样解决:显式定义派生类的构造函数.
Struct A{A(){}};
Struct B{B(){}};
Struct C:A,B
{
using A::A;
UsingB::B;
C(){};
};
如果基类中的构造函数被声明为私有成员函数,或者是虚继承的,那么就不能声明继承构造函数.一旦使用了继承构造函数那么编译器不会生成默认构造函数了.
委托构造函数
顾名思义,一个构造函数可以调用另外一个构造函数,将构造对象的任务委托给它.
class info
{
public:
info()
{
initrest();
}
info(int i):info()
{
type = i;
}
info(char e):info()
{
name = e;
}
~info();
private:
void initrest(){}
int type;
char name;
};
注意的是,委托构造函数和初始化列表不能共存.
委托构造函数和目标构造函数。。。。
要避免委托构造函数和目标构造函数初始化相同的成员。
目标构造函数总是先于委托构造函数执行的。
在异常处理方面,从目标构造函数中产生的异常可以在委托构造函数中捕捉。
左值和右值
可以取地址的有名字的就是左值,反之就是右值.
出现在等号左边的叫做左值,等号右边的叫做右值.
其中右值引用就是对右值进行的引用,&&
在右值引用也就是将常量左值引用的其中一个点,常量左值引用,可以接受非常量左值,常量左值,右值等进行初始化…
移动构造函数
这个就是在右值引用的基础上的新语法.
有的时候并不需要深拷贝,而是需要进行资源的转移,比如将a的堆内存转移到b,而不是要b开辟一个空间,然后拷贝数据,释放a…所以移动构造就是这样的功能….
Class base
{
Base(base && arg){}//这个就是移动构造函数.
};
显式转换操作符
Explicit 这个关键字是禁止隐式类型转换,所以只能够显式的进行类型转换.
同时explicit的使用范围也支持到了自定义类型转换函数上
比如
Explicit operator base()const {return base();}
列表的初始化
内置类型可以使用初始化列表来完成初始化,如果要想让自定义类型也可以像内置类型一样使用{}进行初始化,那么可以#include头文件,并且声明一个以initailzer_list模版类为参数的构造函数就ok了
#include<vector>
#include<string>
using namespace std;
enum Gender
{
boy,girl
};
class People
{
public:
People(initialzer_list<pair<string,Gender>> h)
{
auto i = h.begin();
for(;i != h.end();++i)
{
data.push_back(*i);
}
}
private:
std::vector<pair<string,Gender>> data;
};
People ship2012={{"garfield",boy},{"hellokitty",girl}};
列表初始化是唯一一种可以防止类型收窄的初始化方式.
POD类型
简单的说,pod类型表示这个类型是一个普通的类型.
Pod类型包括两个概念,平凡的和标准布局.
其中平凡的类或者结构体应该符合以下定义:
1.拥有平凡的构造函数和析构函数。
平凡的默认构造函数就是说什么都不干,而是编译器生成的,不能自己定义.后面会介绍default用法.
2.拥有平凡的拷贝构造函数和移动构造函数
平凡的拷贝构造及本省等同于使用memcpy进行类型的构造,
3.拥有平凡的拷贝赋值运算符和移动赋值运算符
4.不能包含虚函数以及虚基类
Pod的标准布局:
1.所有非静态成员有相同的访问权限
2.类或者结构体继承时,满足以下两种情况之一
2.1:派生类中有非静态成员,且只有一个仅包含静态成员的基类
2.2:基类有非静态成员,而派生类没有非静态成员
3.类中第一个非静态成员的类型与其基类不同
非受限联合体
现在联合体可以有非pod类型的成员.任何非引用类型都可以成为联合体的数据成员;
用户自定义字面量
Type Operator “” _C(const char *col,size_t n){} 当然这只是一种,接收字符串的常量
内联名字空间
Inline namespace ….
{
}
Auto类型推导
值得注意的是,auto volatile,const 之间的关系.
其中const 和 volatile 被一起叫做cv 限制符
Auto 的变量不能从其初始化表达式中带走cv限制符….
double foo();
float *bar();
const auto a = foo();//a:const double
const auto &b = foo();//b: const double&
volatile auto *c = bar();//c:volatile float *
auto d = a; //d:double
auto &e = a;//e:const double &
auto f =c ;//f:float *
volatile auto &g=c;//g:volatile float * &
注意引用….
auto 4种情况不能推倒
对于函数fun()来说,auto不能是其形参类型.
对于结构体来说,非静态成员变量的类型不能是auto的
不能声明数组
实例化模板的时候使用auto作为模板参数,也是不行的
auto在进行类型推断时,一般会忽略顶层const(top-level const),而保留底层const(low-level const)。如果想要保留顶层const,则必须显式的在auto前添加const指示符。
所谓顶层const,指的是当前的数据类型本身是常量,如double,int或者相关的指针本身是常量;
而底层const,指的是如指针、引用等复合类型,其所指向的数据类型是常量。
Decltype
Decltype(expression) var;用法;
decltype的四条规则
decltype(e)
如果e是一个没有带括号的标记表达式,或者类成员访问表达式,那么decltype(e)就是e所命名的实体类型,
如果e是一个被从在的函数,那么会导致编译错误.
否则,假设e的类型是T,如果e是一个将亡值,那么decltype(e)为T&&
否则,假设e的类型是T,如果e是一个左值,那么decltype(e)为T&
否则,假设E的类型是T,则decltype(e)为T
与auto不同,decltype是能够带走cv限制符的,如果对象的定义中有const或者volatile,使用decltype进行推倒时,其成员不会继承const或者volatile限制符.
强类型枚举
传统的枚举类型具有一点缺陷,不太符合c++的标准,所以新标准中,改善了枚举类型,
可以使用强类型的枚举,语法:
Enum class ssex{man,woman};
当然我们也可以指定枚举类型底层的数据类型
Enum class type:char {general,light,medium,heavy};
这个就是表示以char类型为底层的枚举类型.
以后使用枚举类型必须使用作用域了…
智能指针
c++11中去除了auto_ptr这个智能指针,而只有三个智能指针:shared_ptr,unique_ptr,weak_ptr
默认函数控制
默认函数的控制default和delete
在C++自定义的类编译器会默认生成一些程序员未定义的成员函数,比如:
1.构造函数 2.拷贝构造函数3.拷贝赋值函数4.移动构造函数5.移动拷贝函数6.析构函数
在c++中一旦实现了自定义的版本,编译器就不会生成默认版本了.所以此时可以使用default关键字来完成告诉编译器提供默认版本的.
class twocstor{
public:
twocstor()= default;
twocstor(int i):data(i){}
private:
int data;
};
=delete 关键字表示显示删除函数,不仅可以用于类中,还可以用于普通的函数中.
class convtype
{
public:
convtype(int i){}
convtype(char c)=delete;
};
int main(){
convtype c(2);
convtype cc('a');//不能通过编译
}
追踪返回类型
在泛型编程中可能不确定函数的返回类型,我们需要根据函数中一些表达式来推导函数的返回类型所以就有了追踪返回类型
template<typename T1,typename T2>
auto sum(T1 &t1,T2 &t2)->decltype(t1+t2)
{
return t1+t2;
}
Lambda函数
Lambda函数的语法格式
capturemutable->return_type{statement}
其中[capture]是捕捉列表,后面详讲作用.
(parameters)是参数,和普通函数的参数一样.
Mutable 是修饰符,默认情况下,lambda函数总是一个const韩桑,mutable可以消除const常性.
->return_type是返回类型,用追踪返回类型的形式来声明函数的返回类型.
{statement}函数体.
捕捉列表的几种形式:
[var] 这个是表示值传递方式捕捉变量var
[=] 值传递捕捉所有父作用域的变量包括this
[&] 引用传递捕捉所有的父作用域变量包括this
[&var] 引用传递捕捉var
[this] 值传递捕捉当前this指针
捕捉列表中按值传递的和按引用传递的效果是不一样的,比如按照值传递的捕捉列表在其中的值在lambda函数定义的时候就已经定了,而按照引用传递的值则是在lambda函数在调用时才决定的。
常量表达式constexpr
在c++中已经有了const关键字来中表示常量性,不过这个常量性是运行时的常量性,不是编译时期的常量性.所以编译期间的常量性使用了新的关键字constexpr.
如果没有编译时期的常量,可能会导致下面的问题:
Const int getconst(){ return 1;}
Void constless(int cond){
Int arr[getconst()] = {0};
Enum {e1 = getconst(),e2};
}
这些都无法编译,如果有了编译时期的常量,那么就可以告诉编译器这些是常量,那么上面的问题解决了.
比如 constexpr int getconst(){return 1;}
对于常量表达式函数要求:
函数体只有单一的return返回语句
函数必须返回值(不能是void函数)
使用前必须有定义
Return 返回语句表达式中不能使用非常量表达式的函数,全局数据,且必须是一个常量表达式.
对于常量表达式值是指由constexpr关键字修饰的变量.
对于自定义类型的,用constexpr修饰会比较麻烦些,通常要定义之定义常量构造函数,并且不能用constexpr来修饰自定义类型的定义.
下面的例子是错误的:
Constexpr struct mytype{int i;}
Constexpr mytype mt ={0};
正确的做法应该这样:
Struct mytype{
Cosntexpr mytype(int x):i(x){}
Int i;
};
Constexpr mytype my = {0};
常量表达式的构造函数也有使用上的约束,主要是以下两点:
函数体必须为空
初始化列表只能有常量表达式来赋值
常量表达式也是可以用于模版的,但是有这样的一个注意:
当声明为常量表达式的模版函数后,而某个该模版函数的实例化结果不满足常量表达式的需求的话,constexpr会被自动忽略…..
原子类型
c++11引入了多线程的支持.同时也增加了原子类型,这样的可以不用自己对这些数据进行加锁来达到原子操作了.
同时增加了memory_order来解决顺序一致性的问题.
STL新增的容器
新增的有forward_list,array还有四种无序的容器哈希map,哈希set,以及tuple.