原生字符串(raw string literals)
很多时候,当我们只需要一行字符串的时候,字符串转义往往成了一个负担,和写和读都带了很大的不便。例如,对于如下路径"C:\Program Files\Microsoft.NET\ADOMD.NET",我们必须把它写成如下形式:
string path = "C:\\Program Files\\Microsoft.NET\\ADOMD.NET";
可能你会说这个并没有多大影响,下面这个正则表达式的例子呢?你能看出来原文到底是什么吗?
string exp = "('(?:[^\\\\']|\\\\.)*'|\"(?:[^\\\\\"]|\\\\.)*\")|";
在C#中,我们可以通过@关键字来取消字符串转义。现在,在C++ 11中,也增加了这样的语法。对于前面的例子,它的非转义形式为:
string path = R"(C:\Program Files\Microsoft.NET\ADOMD.NET)";
从上面的例子中可以看出,它的语法格式如下:
它的语法格式比C#的@前缀要稍微复杂点,不过这个复杂也有复杂的好处,那就是字符串里面可以带引号,例如:
string path = R"(this "word" is escaped)";
而C#就无法保持原始字符串格式,对引号仍需要转义:
string path = @"this ""word"" is escaped";
委托构造函数(Delegating constructors)
C++的构造是不能复用的,为了复用其初始化操作,我们往往会增加一个Initial函数:
class Foo
{
private:
int A;
public:
Foo() : A(0)
{
Init();
}
Foo(int a) : A(a)
{
Init();
}
private:
void Init()
{
// do something
}
};
这样一来就增加了一个只调用一次的Init函数,并且一旦这个Init函数被其它成员函数调用的话,可能导致重复初始化,也是一个隐患。PS:本例比较简单,通过构造函数默认参数也可以解决构造函数复用问题,但默认参数也有一些局限和带来一些问题,限于篇幅就不做更多的讨论了。
在C++ 11中,引入了委托构造函数的语法,其功能和C#中的this构造函数非常类似,就是语法上稍有差异:
class Foo
{
private:
int A;
public:
Foo() : Foo(0)
{
}
Foo(int a) : A(a)
{
// do something
}
};
初始化列表(initializer list)
在C++ 03中,可以用列表的形式来初始化数组,这种方式非常直观,但只能适用于数组,不能适用于我们自定义的容器:
int anArray[5] = { 3, 2, 7, 5, 8 }; // ok
std::vector<int> vArray = { 3, 2, 7, 5, 8 }; // not ok
在C++ 11中,我们则可以使得我们自定义的容器对象支持这种列表的形式的初始化方式:
template <typename T>
class MyArray
{
private:
vector<T> m_Array;
public:
MyArray() { }
MyArray(const initializer_list<T>& il)
{
for (auto x : il)
m_Array.push_back(x);
}
};
void main()
{
MyArray<int> foo = { 3, 4, 6, 9 };
}
统一初始化(Uniform initialization)
C++的对象初始化方式是非常多样的:
int a = 2; //"赋值风格"的初始化
int aa [] = { 2, 3 }; //用初始化列表进行的赋值风格的初始化
complex z(1, 2); //"函数风格"的初始化
C++ 11中,允许通过以花括号的形式来调用构造函数。这样多种对象构造方式便可以统一起来了:
int a = { 2 };
int aa [] = { 2, 3 };
complex z = { 1, 2 };
值得一提的是,这种花括号的构造方式还可以用于函数的参数和返回值的类型推导,非常简洁。
void useMyStruct(MyStruct x)
{
}
useMyStruct({ 2, 3.5f });
MyStruct makeMyStruct(void)
{
return { 2, 3.5f };
}