当前位置 : 主页 > 网络编程 > 其它编程 >

movestd函数示例_一系列C++开发者应当使用的核心语言特性与标准库函数

来源:互联网 收集:自由互联 发布时间:2023-07-02
在C11新标准中语言本身和标准库都增加了很多新内容本文只涉及了一些皮毛。不过我相信这些新特性当中有一些应该成为所有C开发者的常规装备 在C11新标准中语言本身和标准库都增加了
在C11新标准中语言本身和标准库都增加了很多新内容本文只涉及了一些皮毛。不过我相信这些新特性当中有一些应该成为所有C开发者的常规装备

在C11新标准中语言本身和标准库都增加了很多新内容本文只涉及了一些皮毛。不过我相信这些新特性当中有一些应该成为所有C开发者的常规装备。你也许看到过许多类似介绍各种C11特性的文章。下面是我总结的C开发者都需要学习和使用的C11新特性。

4eb7df26e48b73901170cb13c2f6b963.png

行文不易新手上路多多关注这真的对我很重要私信更有惊喜

auto

在C11之前auto关键字用来指定存储期。在新标准中它的功能变为类型推断。auto现在成了一个类型的占位符通知编译器去根据初始化代码推断所声明变量的真实类型。各种作用域内声明变量都可以用到它。例如名空间中程序块中或是for循环的初始化语句中。

auto i 42; // i is an intauto l 42LL; // l is an long longauto p new foo(); // p is a foo*

使用auto通常意味着更短的代码(除非你所用类型是int它会比auto少一个字母)。试想一下当你遍历STL容器时需要声明的那些迭代器(iterator)。现在不需要去声明那些typedef就可以得到简洁的代码了。

std::map> map;for(auto it begin(map); it ! end(map); it){}

要注意的是auto不能用来声明函数的返回值。但如果函数有一个尾随的返回类型时auto是可以出现在函数声明中返回值位置。这种情况下auto并不是告诉编译器去推断返回类型而是指引编译器去函数的末端寻找返回值类型。在下面这个例子中函数的返回值类型就是operator操作符作用在T1、T2类型变量上的返回值类型。

emplate auto compose(T1 t1, T2 t2) -> decltype(t1 t2){ return t1t2;}auto v compose(2, 3.14); // vs type is doublenullptr

以前都是用0来表示空指针的但由于0可以被隐式类型转换为整形这就会存在一些问题。关键字nullptr是std::nullptr_t类型的值用来指代空指针。nullptr和任何指针类型以及类成员指针类型的空值之间可以发生隐式类型转换同样也可以隐式转换为bool型(取值为false)。但是不存在到整形的隐式类型转换。

void foo(int* p) {} void bar(std::shared_ptr p) {} int* p1 NULL;int* p2 nullptr; if(p1 p2){} foo(nullptr);bar(nullptr); bool f nullptr;int i nullptr; // error: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type

为了向前兼容0仍然是个合法的空指针值。

基于范围的for循环

为了在遍历容器时支持”foreach”用法C11扩展了for语句的语法。用这个新的写法可以遍历C类型的数组、初始化列表以及任何重载了非成员的begin()和end()函数的类型。

如果你只是想对集合或数组的每个元素做一些操作而不关心下标、迭代器位置或者元素个数那么这种foreach的for循环将会非常有用。

std::map> map;std::vector v;v.push_back(1);v.push_back(2);v.push_back(3);map["one"] v; for(const auto for(auto v : kvp.second) { std::cout <

我总觉得 C中虚函数的设计很差劲因为时至今日仍然没有一个强制的机制来标识虚函数会在派生类里被改写。vitual关键字是可选的这使得阅读代码变得很费劲。因为可能需要追溯到继承体系的源头才能确定某个方法是否是虚函数。为了增加可读性我总是在派生类里也写上virtual关键字并且也鼓励大家都这么做。即使这样仍然会产生一些微妙的错误。看下面这个例子

class B{public: virtual void f(short) {std::cout <<"B::f" <D::f 按理应当重写 B::f。然而二者的声明是不同的一个参数是short另一个是int。因此D::f(原文为B::f可能是作者笔误——译者注)只是拥有同样名字的另一个函数(重载)而不是重写。当你通过B类型的指针调用f()可能会期望打印出D::f但实际上则会打出 B::f 。

另一个很微妙的错误情况参数相同但是基类的函数是const的派生类的函数却不是。

class B{public: virtual void f(int) const {std::cout <<"B::f " <同样这两个函数是重载而不是重写所以你通过B类型指针调用f()将打印B::f而不是D::f。

幸运的是现在有一种方式能描述你的意图。新标准加入了两个新的标识符(不是关键字):

  • override表示函数应当重写基类中的虚函数。
  • final表示派生类不应当重写这个虚函数。
  • 第一个的例子如下

    class B{public: virtual void f(short) {std::cout <<"B::f" <现在这将触发一个编译错误(后面那个例子如果也写上override标识会得到相同的错误提示)D::f : method with override specifier override did not override any base class methods

    另一方面如果你希望函数不要再被派生类进一步重写你可以把它标识为final。可以在基类或任何派生类中使用final。在派生类中可以同时使用override和final标识。

    class B{public: virtual void f(int) {std::cout <<"B::f" <被标记成final的函数将不能再被F::f重写。

    强类型枚举

    传统的C枚举类型存在一些缺陷它们会将枚举常量暴露在外层作用域中(这可能导致名字冲突如果同一个作用域中存在两个不同的枚举类型但是具有相同的枚举常量就会冲突)而且它们会被隐式转换为整形无法拥有特定的用户定义类型。

    在C11中通过引入了一个称为强类型枚举的新类型修正了这种情况。强类型枚举由关键字enum class标识。它不会将枚举常量暴露到外层作用域中也不会隐式转换为整形并且拥有用户指定的特定类型(传统枚举也增加了这个性质)。

    enum class Options {None, One, All};Options o Options::All;智能指针f80cb4413340e67d859ee0eeff93e01d.png

    行文不易新手上路多多关注这真的对我很重要私信更有惊喜

    已经有成千上万的文章讨论这个问题了所以我只想说现在能使用的带引用计数并且能自动释放内存的智能指针包括以下几种

    • unique_ptr: 如果内存资源的所有权不需要共享就应当使用这个(它没有拷贝构造函数)但是它可以转让给另一个unique_ptr(存在move构造函数)。
    • shared_ptr: 如果内存资源需要共享那么使用这个(所以叫这个名字)。
    • weak_ptr: 持有被shared_ptr所管理对象的引用但是不会改变引用计数值。它被用来打破依赖循环(想象在一个tree结构中父节点通过一个共享所有权的引用(chared_ptr)引用子节点同时子节点又必须持有父节点的引用。如果这第二个引用也共享所有权就会导致一个循环最终两个节点内存都无法释放)。

    另一方面auto_ptr已经被废弃不会再使用了。

    什么时候使用unique_ptr什么时候使用shared_ptr取决于对所有权的需求我建议阅读以下的讨论http://stackoverflow.com/questions/15648844/using-smart-pointers-for-class-members

    以下第一个例子使用了unique_ptr。如果你想把对象所有权转移给另一个unique_ptr需要使用std::move(我会在最后几段讨论这个函数)。在所有权转移后交出所有权的智能指针将为空get()函数将返回nullptr。

    void foo(int* p){std::cout <<*p <第二个例子展示了shared_ptr。用法相似但语义不同此时所有权是共享的。

    void foo(int* p){}void bar(std::shared_ptr p){(*p);}std::shared_ptr p1(new int(42));std::shared_ptr p2 p1; bar(p1);foo(p2.get());

    第一个声明和以下这行是等价的

    auto p3 std::make_shared(42);

    make_shared是一个非成员函数使用它的好处是可以一次性分配共享对象和智能指针自身的内存。而显示地使用shared_ptr构造函数来构造则至少需要两次内存分配。除了会产生额外的开销还可能会导致内存泄漏。在下面这个例子中如果seed()抛出一个错误就会产生内存泄漏。

    void foo(std::shared_ptr p, int init){*p init;}foo(std::shared_ptr(new int(42)), seed());

    如果使用make_shared就不会有这个问题了。第三个例子展示了weak_ptr。注意你必须调用lock()来获得被引用对象的shared_ptr通过它才能访问这个对象。

    auto p std::make_shared(42);std::weak_ptr wp p; {auto sp wp.lock();std::cout <<*sp <如果你试图锁定(lock)一个过期(指被弱引用对象已经被释放)的weak_ptr那你将获得一个空的shared_ptr.

    Lambdasf7417d875527f070371ed0729abaf51f.png

    行文不易新手上路多多关注这真的对我很重要私信更有惊喜

    匿名函数(也叫lambda)已经加入到C中并很快异军突起。这个从函数式编程中借来的强大特性使很多其他特性以及类库得以实现。你可以在任何使用函数对象或者函子(functor)或std::function的地方使用lambda。你可以从这里(http://msdn.microsoft.com/en-us/library/dd293603.aspx)找到语法说明。

    std::vector v;v.push_back(1);v.push_back(2);v.push_back(3); std::for_each(std::begin(v), std::end(v), [](int n) {std::cout <结论

    关于C11还有很多要说的。本文只是各种入门介绍中的一个。本文展示了一系列C开发者应当使用的核心语言特性与标准库函数。然而我建议你能更加深入地学习至少也要再看看本文所介绍的特性中的部分。

    26bab9a77c8350df3a148a007d4472e8.png

    行文不易新手上路多多关注这真的对我很重要私信更有惊喜

    网友评论