asp网站安全性,wordpress备案选项,问卷调查微信小程序怎么做,建网站卖产品本文由
伯乐在线 - 周昌鸿 翻译自 Meeting C。欢迎加入
技术翻译小组。转载请参见文章末尾处的要求。上周Meeting C2013结束后#xff0c;我对C思考了很多#xff0c;有一些内容和指针有关。在C 11中只对指针进行了小量的更新#xff08;引入了nullptr#xff09;#xf…本文由
伯乐在线 - 周昌鸿 翻译自 Meeting C。欢迎加入
技术翻译小组。转载请参见文章末尾处的要求。上周Meeting C2013结束后我对C思考了很多有一些内容和指针有关。在C 11中只对指针进行了小量的更新引入了nullptr不过过去几年中C中指针的语义和用法却发生了很多变化。
首先我们从指针的原始意义开始C11中简单如type* pt nullptr; 这里的指针是C语言中的核心概念指针并不是C发明的据我所知也不是C发明的。但是C规范中定义了指针并给出了在C和C中使用指针的指导。事实上指针是一个变量它存储的值是内存中的一个地址。如果你对指针进行解引用操作就能访问指针指向的变量。指针实际上是一个基础变量它不知道它所指向的值是否有效也不能感知其指向的值是否无效。在C语言中一个指针指向0说明其不指向任何值因此也不具有效的值。所有其他指针都应该指向内存中有意义的地址但实际上有些指针没有正确的初始化或者干脆越出了应有的范围。
在C11中将指针正确初始化为0的方法是使用关键字nullptr。这让计算机知道该指针当前为空。另外还有一种常用的方式是将0定义为NULL或者其他定义或声明。C11中使用nullptr统一了这种方式。C中还引入了引用它看起来像是变量的别名其优势是使用引用的时候必须先初始化因此在引用生命周期起始时需要指向一个有效地址。不过引用也只是指针的解引用所以一旦其引用的变量作用范围结束其引用也无效了使用指针时你可以将指针置为0但是针对引用却不能这么做。
但是在C11和在C11标准之前一些事情发生了变化指针是语言的核心概念但是你在现代化的C代码和函数库中却很少看到它们。远在C11之前boost创建了一系列非常有用的智能指针类针对指针进行了封装对其核心机制通过操作符重载。智能指针本身不是一个指针而是一个栈上的变量或对象成员。智能指针使用了RAII来解决指针的一些问题这并不是指针的职责。当在椎中分配内存时new返回了指向该部分内存的地址所以每分配一块动态内存就需要使用一个指针相当于创建对象的一个操作句柄。但是指针仅仅是一个简单的变量不知道变量的拥有关系也不能自动释放堆上的内存空间。智能指针担当了这一角色拥有指针并在变量超出作用域时自动管理其堆上的值。在栈上的值意味着一旦相应的栈被销毁其管理的堆上的值会被自动释放即使是在发生异常的情况下。
过去的一些年C出现了一些不同风格的使用从使用类的C及大量使用指针到类似我想Widget和QT这样面向对象的框架。在过去5-10年中的形成的一种新样式被认为是现代C一种趋向尽力发掘语言本身扩展能力并试图找到不同特性针对不同场合的应用。值得注意的是boost在这一趋势中起到了引领风范的C框架。C标准在设计其标准库时也借鉴了这一点。与此同时值语义变得流行起来并且与move语义成为未来C一个关键点。来自Tony van Eerds在Meeting C的一份备忘幻灯片引起了我对指针的思考。它有两列一个代表引用语义一个代表值语义以及其朗朗上口的主题词
哦不使用指针 vs 哦不要使用指针
所以在C11或者后续的C14使用值语义的趋势盖过了使用指针。指针在取后台还是工作着不过在新的C14中new和delete都将不提倡直接使用new被抽象化为make_shared/make_unique。其内部使用了new但是返回一个智能指针。shared_ptr 和 unique_ptr都表现为值语义类型。智能指针同样在其作用域结束时使用delete释放内存。这让我思考C中的指针是不是都可以填充不同的“角色”或者被替换掉。
继承和虚拟函数
指针一个非常重要的用途是在继承中使用指针来指向一系列拥有相同接口的类型值。我想用Shape例子来阐明这一点这里有一个基类Shape同时其含有一个虚拟函数叫area的方法。同时它还有几个派生类叫RectangeCirecle和Triangle。现在有一个指针容器比如std::vectorShape*来容纳指向不同形状的对象指针每个对象都有自己的计算面积方法。这是C中最常用指针的方式尤其是在面向对象时。现在好消息是这里同样支持使用智能指针当其使用这些智能指针时内部会进行访问指针。Boost中甚至还有一个指针容器能在清空容器时自动释放其中的智能指针元素。
现在考虑虚函数调用这虽然不和指针有直接联系虚函数调用通常会有点点慢同时也不容易编译器针对其进行优化。所以如果其类型在运行时是可知的就可以使用静态分发或者编译器多态性来正确调用相应的虚函数方法而不是在运行时使用虚函数指针。作为一种模式被叫做CRTP已经实现了这一方式。最近的研究显示这在gcc4.8中可以提高性能。有趣的是通常情况下使用gcc4.9优化器可以针对动态分发进行更进一步的优化。还是让我们继续回到指针。
不确定指针
有时候指针被用于有一系列可选值作为参数或者返回不确定的函数中通常都默认为0用户可以选择传递一个有效的指针给该函数。或者在返回的情况下函数返回一个空指针表示执行失败。对于错误情景现代C中常使用异常但是在有些嵌入式平台上不能工作因此返回0在C的一些场合中也是一个有效的使用方式。同样的这里也可以使用智能指针智能指针可以扮演指针的操作句柄。不过常常会导致堆上内存开销使用堆或者并没有替代不确定的角色。这需要使用一个可选值类型来代替用于确定其存储的值是否有效。Boost库有一个boost::optional来表示可选值类型。因此可以考虑在C14中引入有一个类似的可选类型。所以现在std::optional会被移入到技术预览版TS中将来会变成C14或者C1y的一部分。
当前的标准库中已经使用了一些可选类型比如std::set::insert会返回一个pairiterator,bool类型其第二个参数表示请求值是否插入到set容器中。容器通常返回尾迭代器来表示无效但是如果要求返还一个值时这个角色过去通常都是用指针来表示指针为0表示函数执行失败因此这里的指针可以被可选类型替代 1 2 3 4 5 optionalMyValue ov queryValue(42); if(ov) cout *ov; else cerr value could not be retrieved; 因此可选类型和智能指针类型替代了指针的一部分语义填充了其角色。但是它们是值语义并大部分都在栈上使用。
有效的指针
在写作我对C指针用法的思考时我主要关注于那些指针可以被其他比如智能指针和可选类型等替换的场景但是低估了实际上有些场景指针仍然有用。感谢来自redditemail和社交媒体的一些反馈。
非拥有者指针就是这样一个例子这里未来的几年还是需要使用指针。shard_ptr有对应的weak_ptr但是unique_ptr没有对应的伙伴。这里就需要使用非拥有者原始指针。比如在一个由父和子对象构成的树或者图中。但是未来C中会新增exempt_ptr来代替。
在处理函数中的传递的值时指针还是具有用处的Herb Sutter写了一篇非常好的文章:《GotW about this in May》。Eric Niebler 在他的Meeting C会议的笔记中也谈及了同时移动语义会影响你应该如何在函数中传递或者返回值。 Category C11 Input Arguments small/POD/sink pass by value all others pass by const ref Output return by value Input/Output non const ref / stateful Algorithm Object
这个表格来自 Eric Nieblers 的笔记, 请看幻灯片中的16/31 (建议你阅读所有的幻灯片)
Eric Niebler说过在能使用移动语义时尽可能使用移动语义。一个可选参数为例vector::emplace_back接收一个参数当其只是将把元素移动到适当位置这时你应得使用移动语义。一些输出参数返回一个值编译器可以使用移动语义或者CopyEllision拷贝去除的优化技术。针对一些以对象为输入/输出参数非常引用也是可选择性优化的但是Eric在他的笔记中指出对象算法的状态在构造函数中应使用槽参数。
在传递常量非常量引用时指针可以做同样的事情不过有些不同你需要对指针测试其是否为空。我个人更喜欢在函数/方法或者构造函数时传递引用而不是指针。
指针计算
之前我提到过从我个人的观点指针只是一个普通的变量其值指向一个地址或者更精确地说是其指向值得一个地址号码。这个地址号码可以被复制你可以对其进行加或减法操作。这常常用于遍历数组或者计算两个指针的的距离这在使用数组时很有用。这里对数组的便利其实就是迭代器所以在实际代码时指针可以代替迭代器使用。但是从我多年C开发经验来看我几乎没有用到针对指针的计算操作。而且在C中指针的计算已经有了非常好的抽象。我的观点是理解指针计算是重要的这有助于理解代码中指针的具体作用。
再见指针
理论上C可以不使用指针但是由于指针是C/C语言的核心概念指针本身仍然会继续存在。但是它的角色会变更在你使用C时你不再需要考虑指针。随着C的继续发展C11和C14朝着更抽象对开发者更友好的方向发展。使用智能指针和可选类型指针要么被封装从而更适用安全的值类型要么完全被它们替代掉。