精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
最近写了几篇C++11智能指针的文章,想对C++11的全部知识点有个全部学习,但是发现国内搜索的都是不全,国内突出了重点,不是最全,所以搜索国外了一篇文章进行C++11全网最全归类引导,记住是引导,不是每个知识点都展开讲。
本文旨在帮助经验丰富的C++用户理解和使用C++11中一些有趣的增强功能。我将讨论一些有趣的C++11功能,包括它们在 Visual Studio 11 Beta 和 g++ 中的可用性。
需要非常好的 C++ 知识。在这里,我们只讨论新事物,这些事物大多是先进的。如果您不是专业的 C++ 开发人员,最好将本文留到以后。
auto 关键字允许从函数返回中推断变量的类型。这样,就不必显式指定类型。例如,以下代码:
int foo() { return 5; } int j = foo();
可以写成:
int foo() { return 5; } auto j = foo();
编译器会自动将 j 视为 int。
你可能会说大不了。事实并非如此。猜猜当你有一个 :typename::user::object::foodump::longlongyum::iterator
name::user::object::foodump::longlongyum::iterator foo() { return ... } name::user::object::foodump::longlongyum::iterator j = foo(); for(name::user::object::foodump::longlongyum::iterator x = some.begin() ; x < some.end() ; x++) { ... }
所有这些都非常无聊且容易出错。使用 auto 会让它变得相当简单和清晰:
name::user::object::foodump::longlongyum::iterator foo() { return ... } auto j = foo(); for(auto x = some.begin() ; x < some.end() ; x++) { ... }
我的评价:非常有帮助。 允许避免拼写错误。
>Visual C++ 11:可用。
G++:可用。
constexpr 关键字允许您指定始终具有常量返回值的函数。考虑这个问题:
int foo() { return 15; } int j[foo()];
编译器错误。编译器无法知道 foo() 返回值是否为 constant。
constexpr int foo() { return 15; } int j[foo()];
编译器知道这将返回一个常量。foo()
我的评价:不确定。 constexpr函数可以执行的操作有很多限制,而且,实际上,int j[15]比int j[foo()]更干净。但是,可以提示编译器用编译时计算替换运行时计算 - 尽管我还没有找到实际示例。
Visual C++ 11: Not available.
G++: Available.
这些修饰符允许您在成员函数中显式定义:
如果您想覆盖父函数,但不小心更改了函数签名,因此会生成第二个函数,则 override 很有用:
class a { public: virtual void foo(int) {...} }; class b : public a { public: virtual void foo() override; // Compiler error. void foo() has not replaced void foo(int) }
在这里,程序员缩进来用b::foo()覆盖a::foo() ,但没有 override 指示,就会创建一个新的虚函数,因为程序员忘记了foo()原始函数将 int 作为参数。
如果您想确保函数永远不会被覆盖,final 非常有用:
class a { public: virtual void foo(int) final {...} }; class b : public a { public: virtual void foo(int) {}; // Compiler error. void foo(int) is final. }
default 允许程序员告诉编译器应该自动生成 default 构造函数,尽管可能还有其他构造函数:
class a { public: a() = default; a(int) { ... } };
delete 允许程序员显式限制对成员的调用,例如由于不需要的强制转换或复制构造函数而被调用的函数:
class a { public: a(const a&) = delete; void x(int) = delete; void x(float); };
在这里,类复制构造函数不能被调用,并且对a::x()的调用必须显式传递一个浮点数(例如,你不能调用x(0) )。
我的评价:有用。 它有助于避免不必要的错误。
Visual C++ 11:不可用。
模板中的关键字 extern 告诉编译器不要实例化模板:
template <typename X> class a { public: void test(X); }; template class a<int>; // C++ 03 instantiation extern template class a<int> // C++ 11 won't instantiate.
C++ 11 不会实例化 “a”,但 a 将对当前编译单元可见。
我的评价:有用。它有助于避免在多个 cpp 文件中出现不必要的实例化。
Visual C++ 11:不可用。
C++ 11 具有无序的关联容器,称为哈希表。这些容器使用哈希函数来存储元素。
void test_hash() { unordered_map<string,int> days(7); const char* str[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; for(int i = 0 ; i < 7 ; i++) days[str[i]] = i; auto j = days["Wed"]; // j = 3; }
我的评价:如果你需要它,很有用。
Visual C++ 11:可用。
G++:可用。
最后,C++ 中缺少了一样东西。使用 {} 初始化非 POD 对象的能力。例如,这有效:
int j[5] = {1,2,3,4,5};
但是这个,并没有:
vector<int> j = {1,2,3,4,5};
人们必须以一种麻烦的方式分配值,例如使用 for 循环。C++ 11 允许使用一个名为Ainitializer_list特殊模板,该模板可以通过 { } 获取其值,这可以用作任何函数、构造函数或非构造函数中的成员参数:
template <typename X> class A { public: A(int x); A(std::initializer_list<int> y); void foo(std::initializer_list<X> list); }; A<int> a(5); // construct using A(int); A<float> b({1,2,3,4,5,6}); // construct using A(std::initializer_list<int> y); b.foo({0.5f,0.6f}); // call foo with parameters.
我的评价: 非常有用 .
Visual C++ 11::(不可用。
G++:可用。
lambda 函数是可以在另一个函数中使用的匿名函数。
语法:[capture][arguments]{body}(<call parameters>)
capture 是一个标识符列表,将在函数外部的主体(例如函数)中使用。如果标识符不在捕获列表中,则任何在 lambda 函数之外使用标识符的尝试都会生成编译器错误。
vector<int> flist(15); int sum = 0; for(int i= 0 ; i < 15 ;i++) flist[i] = i; for(int i = 0 ; i < 15 ; [&sum](int x) { sum += x;}(flist[i++])); // sum = 105.
[&sum](整数 x)} { 总和 += x;是 lambda 函数。它通过引用捕获 “sum” 外部变量,并将作为参数传递的 “x” 添加到其中。在 for 循环中,它立即用 flist[i] 调用。
您无需立即调用它:
auto f = [] (int x, int y) { return x + y; }; int j = f(5,6); // j = 11;
我的评估:有用,但要小心,因为它很容易导致代码难以维护。
Visual C++ 11:可用。
G++:可用。
nullptr 常量(类型 nullptr_t)允许您指定不得从零隐式转换的指针类型。
考虑这个问题:
int foo(int x) { ... } int foo(char* a) { ... } foo(NULL);
foo(NULL)调用将调用foo(int) , 而不是foo(char*) ,因为 NULL 定义为 0。
int foo(int x) { ... } int foo(char* a) { ... } foo(nullptr);
现在,foo(char*)将被调用。nullptr 可以隐式转换为任何其他指针类型,但不能转换为任何整型 int 类型(来自 bool 除外)。int j = nullptr 生成编译器错误。
我的评价:几乎没用。 你有同时接受整型和指针的重载函数是非常罕见的,如果有,你的设计中(通常)存在缺陷。
Visual C++ 11:可用。
G++:可用。
这些类似于函数指针,不同之处在于它们允许隐式转换其参数。例如:
int foo(int x) { return x + 1;} typedef int (*foop)(short y); foop f = foo; // Error. It is int foo(int), not int foo(short);
使用多态指针可以完成此类转换:
std::function<bool (int)> foo; bool f2(short a) { ... } foo = f2; // Valid. short can be converted to int bool A = foo(5);
我的评价:如果你知道自己在做什么,那就有用了。C++ 是强类型的,弱化该功能可能会导致麻烦。
Visual C++ 11:不可用。
G++:可用。
您可能从 IRegExp 中了解的函数现在位于标准标头 <regex> 中:
char* str = "<h2>Header</h2>"; regex rx("<h(.)>([^<]+)"); cmatch res; eegex_search(str, res, rx); // isolate "Header"
当然,正则表达式是一个独立的章节,我们不能在这里讨论。但很高兴将其视为标准。
我的评估:如果你需要正则表达式,这很有用。
Visual C++ 11:可用。我确定他们使用的是旧的 VB 的 IRegExp ;)
G++: Available。
在 C++ 11 中,sizeof 可用于获取类成员的大小,而无需类实例:
class a { public int b; }; int x = sizeof(a::b); // Valid in C++ 11. In C++ 03 you had to have an instance of a class.
我的评估:必需。
Visual C++ 11:不可用。
assert 关键字可以在运行时用于测试断言,#error 预处理器指令可以在预处理器中使用。new 关键字 static_assert 可用于编译器:
int j = 0; static_assert(j,"error!"); // Compiler error: compilation failed.
我的评价:可能有用,但不是很重要。
Visual C++ 11:可用。
G++:可用。
到目前为止,你可以使用 “string” 和 L“string” 分别使用普通字符串和宽字符串。C++ 11 添加了以下(非常有用的)文本:
使用 R“string” 可以从源复制/粘贴字符串,而无需转义特殊字符,例如 \。
char* a1 = "Hello there, you can use the \ and / characters to pass arguments"; // Ugh, probably a bug, we forgot to escape \ char* a2 = R"Hello there, you can use the \ and / characters to pass arguments"; // Fine.
我的评估:强制性,尤其是 R“string”。
Visual C++ 11:不可用。呃。
G++:可用。
枚举不是类型化的,例如:
enum test {a = 0,b,c,d,e}; // implementation of the enum and it's size is compiler-dependant int j = e; // valid. bool h = d; // valid.
现在可以键入它们:
enum class test: unsigned long {a = 0,b,c,d,e}; // implementation of the enum now is with unsigned long; Guaranteed size and type. int j = -1; if (j < test::c) //warning, comparing signed to unsigned.
我的评价:很好。
Visual C++ 11:不可用。
G++:可用。
线程类(包括互斥锁等同步内容)已添加到 C++ 11 中。
void test_thread_func(const char* message) { // Do something with parameter } void test_thread() { thread t(bind(test_thread_func,"hi!")); t.join(); }
这将在单独的线程中启动 “test_thread_func” 并等待它完成 join() 函数。还有许多其他功能,如互斥锁、线程本地存储支持、原子操作等。
我的评价:很好,尽管对于严重的线程模型,您通常需要特定于操作系统的扩展。幸运的是,有一个 native_handle() 成员函数,它返回本机句柄(在 Win32 中为 HANDLE),因此您可以在没有C++替代方法时使用它。
Visual C++ 11:可用。
G++:不可用(至少我无法编译)。
unique_ptr(C++ 11 中的新增功能)、shared_ptr 和 weak_ptr 可用。unique_ptr 以指针可以移动的方式扩展了 (现已弃用的) auto_ptr:
unique_ptr<int> p1(new int(100)); unique_ptr<int> p2 = move(p1); // Now p2 owns the pointer. p2.reset(); // memory is freed p1.reset(); // does nothing.
我的评价:很好,尽管你不应该过分依赖自动指针来摆脱困境。使用分配器类来避免 new 和 delete 很好,但不要过度使用,因为在我看来,xxxx_ptr类已经太多了。不要落入依赖智能指针来增强设计较弱的应用程序的陷阱。
另外,请注意,许多人投票支持使用智能指针,以便在发生异常时可以安全地销毁内存,因此您可以继续而不会泄漏。记住异常应该是什么:您 99% 不应该正常继续的错误。不要使用 exceptions 而不是 if 或 switch。 不要对密码无效、找不到文件等错误引发异常。不要捕获 “not enough memory” 异常。您的代码已经存在缺陷,或者 Windows 非常不稳定!
Visual C++ 11:可用。
G++:可用。
不受限制的联合允许在哪些方面有更多的自由来决定什么是联合,什么不能:
class A { int b; int c; A() { b =5; c = 4;} }; union B { int j; A a; // Invalid in C++ 03: A has a non trivial constructor. Valid in C++ 11: };
我的评价是:危险。 Union 主要为较低级别的功能实现,如 C 位调整、汇编内容等。他们应该受到非常严格的限制。
Visual C++ 11:不可用。
到目前为止,您可以使用特定于编译器的 #pragmas 来指定对齐方式。C++ 11 alows the alignas 关键字:
alignas(double) int d[8]; // d contains 8 integers, but it's size is enough to contain 8 doubles also.
此外,alignof 运算符返回标识符的对齐方式,例如 int j = alignof(d) 将返回 sizeof(double)。
我的评价:有用。
Visual C++ 11: 不可用。
G++:不可用。
本文的最后一篇文章讨论了可变参数模板。这些是可以接受任何数字和任何类型的参数的模板:
template <typename ...T> void foo(T...args)
这允许对 printf 等函数进行类型安全的实现。下面的示例检查参数的大小,并使用适当的参数调用 func1():
void func1(int a1, int a2, int a3) { int j = a1 + a2 + a3;} template<class...T> int funcV(A...args) { int size = sizeof...(A); if (size == 0) func1(1,2,3); if (size == 1) func1(1,2,args...); if (size == 2) func1(1,args...); if (size == 3) func1(args...); } int main() { funcV(0); funcV(0,1,2); funcV(5,3); funcV("hello"); // error, func1 does not accept a char*. }
由于 “args” 不容易被函数使用(因为它们的类型和大小未知),因此通常递归调用它们。
我的评价:有用,但它们真的很容易导致代码难以理解。请谨慎使用。
Visual C++ 11: 不可用。
G++: 不可用。