本文主要介绍并学习C++11,14,17新的内容,供老鸟、菜鸟共同学习。
auto关键字
用于实现自动类型推导
void auto_test()
{
auto a = 5;
printf("%d\n", a); //5
auto p = new int();
*p = 6;
printf("%d\n", *p); //6
auto *pstr = new std::string();
pstr->append("abc");
printf("%s\n", pstr->c_str()); //abc
}
decltype关键字
获取变量或表达式的类型
void decltype_test()
{
decltype(5) x = 5;
printf("%d\n", x); //5
}
move语义
类似于swap
void move_test()
{
//交换字符串
string s1 = "s1";
string s2 = "s2";
string tmp(std::move(s1));
s1 = std::move(s2);
s2 = std::move(tmp);
cout << "s1=" << s1 << " s2=" << s2 << endl; //s1=s2 s2=s1
}
右值引用
void rvalue_test()
{
string && s = string("abc");
cout << s << endl; //abc
}
参考:
http://blog.csdn.net/booirror/article/details/45057689
lambda表达式
类似于一个局部函数。
语法:
[capture](parameters) mutable ->return-type{statement}
1.[capture]:捕捉列表。捕捉列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数。捕捉列表能够捕捉上下文中的变量以供Lambda函数使用;
2.(parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略;
3.mutable:mutable修饰符。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空);
4.->return-type:返回类型。用追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导;
5.{statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
与普通函数最大的区别是,除了可以使用参数以外,Lambda函数还可以通过捕获列表访问一些上下文中的数据。具体地,捕捉列表描述了上下文中哪些数据可以被Lambda使用,以及使用方式(以值传递的方式或引用传递的方式)。语法上,在“[]”包括起来的是捕捉列表,捕捉列表由多个捕捉项组成,并以逗号分隔。捕捉列表有以下几种形式:
1.[var]表示值传递方式捕捉变量var;
2.[=]表示值传递方式捕捉所有父作用域的变量(包括this);
3.[&var]表示引用传递捕捉变量var;
4.[&]表示引用传递方式捕捉所有父作用域的变量(包括this);
5.[this]表示值传递方式捕捉当前的this指针。
上面提到了一个父作用域,也就是包含Lambda函数的语句块,说通俗点就是包含Lambda的“{}”代码块。上面的捕捉列表还可以进行组合,例如:
1.[=,&a,&b]表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量;
2.[&,a,this]表示以值传递的方式捕捉变量a和this,引用传递方式捕捉其它所有变量。
不过值得注意的是,捕捉列表不允许变量重复传递。下面一些例子就是典型的重复,会导致编译时期的错误。
3.[=,a]这里已经以值传递方式捕捉了所有变量,但是重复捕捉a了,会报错的;
4.[&,&this]这里&已经以引用传递方式捕捉了所有变量,再捕捉this也是一种重复。
//lambda表达式
void lambda_test()
{
vector<int> v = { 1, 2, 3, 4 };
int a = 5;
auto func1 = [=](vector<int> &v) -> int
{
int sum = 0;
for (auto i : v)
{
sum += i * a;
}
return sum;
};
int sum = func1(v);
cout << sum << endl;
std::for_each(v.begin(), v.end(), [=](int &x)->int{ x = (a + 1); return x; });
}
nullptr关键字
空指针类型
void nullptr_test()
{
int *p = NULL;
int *q = nullptr;
assert(p == q); //true
}
static_assert静态断言
static_assert用来做编译期间的断言,因此叫做静态断言。
语法:
static_assert(常量表达式,提示字符串)
如果第一个参数常量表达式的值为真(true或者非零值),那么static_assert不做任何事情,就像它不存在一样,否则会产生一条编译错误,错误位置就是该static_assert语句所在行,错误提示就是第二个参数提示字符串。
使用static_assert,我们可以在编译期间发现更多的错误,用编译器来强制保证一些契约,并帮助我们改善编译信息的可读性,尤其是用于模板的时候。
static_assert可以用在全局作用域中,命名空间中,类作用域中,函数作用域中,几乎可以不受限制的使用。
编译器在遇到一个static_assert语句时,通常立刻将其第一个参数作为常量表达式进行演算,但如果该常量表达式依赖于某些模板参数,则延迟到模板实例化时再进行演算,这就让检查模板参数成为了可能。
性能方面,由于是static_assert编译期间断言,不生成目标代码,因此static_assert不会造成任何运行期性能损失。
void static_assert_test()
{
static_assert(sizeof(void *) == 4, "64-bit code generation is not supported.");
}
参考:http://blog.csdn.net/thesys/article/details/5641350
Range based for loop序列for循环
//序列for循环
void for_test()
{
vector<int> v = { 1, 2, 3, 4 };
for (auto i : v)
{
printf("%2d", i); // 1 2 3 4 5
}
}
所有支持iterator的容器均可以使用该语法。
final关键字
类被final修饰,不能被继承
class cls final
{
};
class cls2 : public cls //编译报错
{
};
虚函数被final修饰,不能被override
class cls
{
virtual void func() final{}
};
class cls2 : public cls
{
virtual void func();//编译报错
};
override关键字
被override修饰后如果父类无对应的虚函数则报错,无法override,这个有什么作用呢,假如你想虚继承基类的函数,但是继承的时候写错了,参数类型不对或个数不对,但是编译没问题,运行时候缺和你设计的不一样不被调用,override就是辅助你检查是否继承了想要虚继承的函数
class cls
{
virtual void func() = 0;
};
class cls2 : public cls
{
virtual void func() override {}
virtual void func2() override {} //编译报错
};
Strongly typed enums强类型枚举 & Forward declared enums 枚举前置声明
在标准C++中,枚举类型不是类型安全的。枚举类型被视为整数,这使得两种不同的枚举类型之间可以进行比较。C++03 唯一提供的安全机制是一个整数或一个枚举型值不能隐式转换到另一个枚举别型。 此外,枚举所使用整数类型及其大小都由实现方法定义,皆无法明确指定。 最后,枚举的名称全数暴露于一般范围中,因此C++03两个不同的枚举,不可以有相同的枚举名。 (好比 enum Side{ Right, Left }; 和 enum Thing{ Wrong, Right }; 不能一起使用。)
C++11 引进了一种特别的 “枚举类”,可以避免上述的问题。
enum class Enumeration
{
Val1,
Val2,
Val3 = 100,
Val4 /* = 101 */,
};
此种枚举为类型安全的。枚举类型不能隐式地转换为整数;也无法与整数数值做比较。 (表示式 Enumeration::Val4 == 101 会触发编译期错误)。
枚举类型所使用类型必须显式指定。在上面的示例中,使用的是默认类型 int,但也可以指定其他类型:
enum class Enum2 : unsigned int {Val1, Val2};
枚举类型的语汇范围(scoping)定义于枚举类型的名称范围中。 使用枚举类型的枚举名时,必须明确指定其所属范围。 由前述枚举类型 Enum2 为例,Enum2::Val1是有意义的表示法, 而单独的 Val1 则否。
此外,C++11 允许为传统的枚举指定使用类型:
enum Enum3 : unsigned long {Val1 = 1, Val2};
枚举名 Val1 定义于 Enum3 的枚举范围中(Enum3::Val1),但为了兼容性, Val1 仍然可以于一般的范围中单独使用。
在 C++11 中,枚举类型的前置声明 (forward declaration) 也是可行的,只要使用可指定类型的新式枚举即可。 之前的 C++ 无法写出枚举的前置声明,是由于无法确定枚举参数所占的空间大小, C++11 解决了这个问题:
enum Enum1; // 不合法的
enum Enum2 : unsigned int; // 合法的 C++11
enum class Enum3; // 合法的 C++11
enum class Enum4: unsigned int; // 合法的 C++11
enum Enum2 : unsigned short; // 不合法的 C++11
参考:
http://www.cnblogs.com/tekkaman/p/3500904.html
Initializer lists 初始化list
//初始化方式
class cls
{
public:
int a;
int b;
};
void init_test()
{
int arr[3]{ 1, 2, 3 };
vector<int> v{ 1, 2, 3, 4 };
map<int, string> m{ { 1, "a" }, { 2, "b" }, {3, "c"} };
cls c{ 1, 2 };
cout << "a=" << c.a << " b=" << c.b << endl; //a=1 b=2
}
参考:
http://blog.csdn.net/hailong0715/article/details/54018002
Template aliases模板别名
在C++11中,允许使用using关键字为一个模板来定义别名,实际上using包含了typedef的所有功能,来看下使用using关键字和typedef关键字定义普通类型别名的用法。
可以看到在对普通类型的别名定义上,两种方法的使用基本等效,唯一不同的仅仅是定义的语法,using使用起来就像是赋值,但是在定义函数函数指针的时候,using看起来可读性要稍微好一点。
//Template aliases模板别名
template<typename T>
using alias_map = std::map < string, T > ;
alias_map<int> map_t;
alias_map<string> map_str;
using uint_t = unsigned int; //等效于typedef
using StrToStrMap = map < string, string > ; //等效于typedef
typedef void(*func)(int, int);
using func = void(*)(int, int);
参考:
http://blog.csdn.net/hailong0715/article/details/53994462
Raw string literals 原生字符串
不做任何转义的字符串
string s = R"(abc)";
cout << s << endl; //abc
string s2 = R"xx(a(b)c)xx";
cout << s2 << endl; //a(b)c
参考:
http://blog.csdn.net/lovehuanhuan1314/article/details/57127616
>> for nested templates 模板双>>语法优化
模板使用中,支持>>符号了。之前的版本报语法错误。
vector<vector<int>> vv; //不报语法错误了。
Default template arguments in function templates函数模板中的默认模板参数
template<typename t1, typename t2 = int>
void func_template(t1 _t1, t2 _t2)
{
cout << "t1=" << _t1 << " t2=" << _t2 << endl;
}
void func_template_test()
{
func_template(1, 5); //t1=1 t2=5
func_template("str1", "str2"); //t1=str1 t2=str2
}
参考:http://blog.csdn.net/hailong0715/article/details/53994462
Forwarding constructors
如果一个类包含多个构造函数,C++ 11允许在一个构造函数中的定义中使用另一个构造函数,但这必须通过初始化列表进行操作,如下:
class Notes
{
int k; double x; string s;
public:
Notes(int kk, double xx, string ss) : k(kk), x(xx), s(ss){ }
Notes(int kk) : Notes(0, 0.5, “benxin”){ k = kk; }
...
};
Defaulted methods 默认方法 & Deleted methods 删除方法
假设你为类定义了构造函数,那么类就不会自动提供默认的构造函数了,然而,如果你仍然想使用类提供的默认版本,那么可以使用default关键字:
class cls
{
public:
cls(int){}
cls() = default;
};
相反地,如果要禁用编译器提供的默认函数,可以使用delete:
class cls
{
public:
cls(int){}
cls() = default;
cls(const cls & other) = delete; //禁用拷贝构造函数
};
int _tmain(int argc, _TCHAR* argv[])
{
cls c(5);
cls c2(c); //编译错误
return 0;
}
当然要想禁用某个编译器提供的函数也可以显式声明为private,但是使用delete更方便且不易出错。
注意:default关键字只能用于6个特殊函数,而delete却能够用于任何成员函数。
Trailing return type in functions函数返回类型后置
template<typename T, typename U>
auto add(T t, U u) ->decltype(t + u)
{
return t + u;
}
void return_type_test()
{
auto rst = add<int, double>(5, 10);
cout << rst << endl;
}
参考:http://blog.csdn.net/yockie/article/details/51731281
extern templates外部模板
用于模板性能上的改进
//fun.h
template <typename T>
void fun(T t){
}
//use1.cpp
void test1(){
fun<int>(1);
}
//use2.cpp
extern template void fun<int>(int);
void test2(){
fun<int>(1);
}
参考:http://blog.csdn.net/liqinghua1653/article/details/12317611
sizeof on non-static data members without an instance 非静态成员可以使用sizeof
class cls
{
public:
int a;
};
int _tmain(int argc, _TCHAR* argv[])
{
cout << sizeof(cls::a) << endl; //c++11通过编译
return 0;
}
Changed restrictions on union members 修改成员的限制约束
C++11中union除了继承C语言的数据共享内存之外,行为上越来越像一个类,比如成员默认是public类型。c++11中的新特性:
1、能够有成员函数,包括构造函数和析构函数
2、不能继承,也不能被继承,也不能有虚函数
3、可以有成员变量是类,但是这个类成员不能有自己定义的构造、拷贝、析构、赋值等函数,否则编译不过(至少在vs2012上是的)
自己定义的构造、拷贝、析构、赋值函数称为non-trivial函数,默认的则称为trivial函数,相关内容参考链接3和4
4、不能有引用成员,不管是左值引用还是右值引用,不能有静态成员变量,但是可以有静态成员函数
5、在类的内部使用union,整个类包装联合体
参考:
http://blog.csdn.net/xiangyubobo/article/details/44238603
noexcept 关键字
void func() noexcept //不抛出异常
{
}
Non-static data member initializers 非静态成员初始化
class cls
{
public:
int a = 5; //初始化
};
constexpr 关键字
参考:
http://blog.csdn.net/csxiaoshui/article/details/39473419
C99 compatibility 兼容性
参考:
http://blog.csdn.net/robin__chou/article/details/50814095
Variadic macros & Variadic templates 变参宏及变参模板
C++ 11提供了一个用省略号表示的元运算符,使得可以声明表示模板参数列表,其语法如下:
template<typename... Args>
void show(Args... args){ }
其中Args表示模板参数列表,args表示函数参数列表。
这里有一个问题,我们如何访问具体的某个参数呢?使用args[2]吗?答案是否定的,我们在可变参数中不能使用索引来访问,这里需要递归地访问每个参数:
template<typename T, typename... Args>
voidshow(T value, Args... args)
{
cout << value << “, ”;
show(args...);
}
每次调用show,可变参数将减少一个,直到调用show()时,调用参数为空,不接受,则退出递归。
Alignment support
参考:
http://blog.csdn.net/a627088424/article/details/45243157
未讲解的内容
[cpp]11 Local and unnamed types as template arguments
[cpp]20 explicit type conversion operators
[cpp]26 Generalized attributes
[cpp]29 Inline namespaces
[cpp]32 User defined literals
[cpp]33 Encoding support in literals
[cpp]34 Arbitrary expressions in template deduction contexts
[cpp]39 Thread local storage
各编译器支持情况
vs/clang/g++
http://cpprocks.com/c11-compiler-support-shootout-visual-studio-gcc-clang-intel/
vs各版本支持情况
https://msdn.microsoft.com/zh-cn/library/hh567368.aspx