百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

C++初学者必须掌握的10个特性(中)

ztj100 2025-01-03 20:47 11 浏览 0 评论

Strongly-typed enums 强类型枚举

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

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

12enum classOptions {None, One, All};Options o = Options::All;

Smart Pointers 智能指针

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

· 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。

1void foo(int* p){std::cout << *p << std::endl;}std::unique_ptr<int> p1(newint(42));std::unique_ptr<int> p2 = std::move(p1); // transfer ownershipif(p1)foo(p1.get());(*p2)++;if(p2)foo(p2.get());

第二个例子展示了shared_ptr。用法相似,但语义不同,此时所有权是共享的。

1voidfoo(int* p){}void bar(std::shared_ptr<int> p){++(*p);}std::shared_ptr<int> p1(newint(42));std::shared_ptr<int> p2 = p1;bar(p1);foo(p2.get());

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

1auto p3 = std::make_shared<int>(42);

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

1void foo(std::shared_ptr<int> p, intinit){*p = init;}foo(std::shared_ptr<int>(newint(42)), seed());

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

1auto p = std::make_shared<int>(42);std::weak_ptr<int> wp = p;{auto sp = wp.lock();std::cout << *sp << std::endl;}p.reset();if(wp.expired())std::cout << "expired"<< std::endl;

如果你试图锁定(lock)一个过期(指被弱引用对象已经被释放)的weak_ptr,那你将获得一个空的shared_ptr.

Lambdas

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

1std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);std::for_each(std::begin(v), std::end(v), [](intn) {std::cout << n << std::endl;};auto is_odd = [](intn){returnn%2==1;};auto pos = std::find_if(std::begin(v), std::end(v), is_odd);if(pos != std::end(v))std::cout << *pos << std::endl;

更复杂的是递归lambda。考虑一个实现Fibonacci函数的lambda。如果你试图用auto来声明,就会得到一个编译错误。

1auto fib = [&fib](intn){returnn < 2 ? 1 : fib(n-1) + fib(n-2);}
2error C3533: 'auto &': a parameter cannot have a type that contains 'auto'error C3531:'fib': a symbol whose type contains 'auto' must have an initializererror C3536:'fib': cannot be used before it is initializederror C2064:term does not evaluate to a function taking 1 arguments

问题出在auto意味着对象类型由初始表达式决定,然而初始表达式又包含了对其自身的引用,因此要求先知道它的类型,这就导致了无穷递归。解决问题的关键就是打破这种循环依赖,用std::function显式的指定函数类型:

1std::function<int(int)> lfib = [&lfib](intn){returnn < 2 ? 1 : lfib(n-1) + lfib(n-2);}

非成员begin()和end()

也许你注意到了,我在前面的例子中已经用到了非成员begin()和end()函数。他们是新加入标准库的,除了能提高了代码一致性,还有助于更多地使用泛型编程。它们和所有的STL容器兼容。更重要的是,他们是可重载的。所以它们可以被扩展到支持任何类型。对C类型数组的重载已经包含在标准库中了。

我们还用上一个例子中的代码来说明,在这个例子中我打印了一个数组然后查找它的第一个偶数元素。如果std::vector被替换成C类型数组。代码可能看起来是这样的:

1intarr[] = {1,2,3};std::for_each(&arr[0], &arr[0]+sizeof(arr)/sizeof(arr[0]), [](intn){std::cout << n << std::endl;};auto is_odd = [](intn) {returnn%2==1;};auto begin = &arr[0];auto end = &arr[0]+sizeof(arr)/sizeof(arr[0]);auto pos = std::find_if(begin, end, is_odd);if(pos != end)std::cout << *pos << std::endl;

如果使用非成员的begin()和end()来实现,就会是以下这样的:

1intarr[] = {1,2,3};std::for_each(std::begin(arr), std::end(arr), [](intn){std::cout << n << std::endl;};auto is_odd = [](intn) {returnn%2==1;};auto pos = std::find_if(std::begin(arr), std::end(arr), is_odd);if(pos != std::end(arr))std::cout << *pos << std::endl;

这基本上和使用std::vecto的代码是完全一样的。这就意味着我们可以写一个泛型函数处理所有支持begin()和end()的类型。

1template<typenameIterator>voidbar(Iterator begin, Iterator end){std::for_each(begin, end, [](intn){std::cout << n << std::endl;}auto is_odd = [](intn) {returnn%2==1;}auto pos = std::find_if(begin, end, is_odd);if(pos != end)std::cout << *pos << std::endl;}template<typenameC>voidfoo(C c){bar(std::begin(c), std::end(c));}template<typenameT, size_tN>voidfoo(T(&arr)[N]){bar(std::begin(arr), std::end(arr));}intarr[] = {1,2,3};foo(arr);std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);foo(v);

static_assert和 type traits

static_assert提供一个编译时的断言检查。如果断言为真,什么也不会发生。如果断言为假,编译器会打印一个特殊的错误信息。

1template<typenameT, size_tSize>classVector{static_assert(Size < 3, "Size is too small");T _points[Size];};intmain(){Vector<int, 16> a1;Vector<double, 2> a2;return0;}
2error C2338: Size is too small'Vector<T,Size>' beingcompiledwith[T=double,Size=2]

static_assert和type traits一起使用能发挥更大的威力。type traits是一些class,在编译时提供关于类型的信息。在头文件<type_traits>中可以找到它们。这个头文件中有好几种class:helper class,用来产生编译时常量。type traits class,用来在编译时获取类型信息,还有就是type transformation class,他们可以将已存在的类型变换为新的类型。

感谢作者冯上(@治不好你我就不是兽医 ),本文转自伯乐在线

如果你想学习编程,如果你想通过自己的努力在IT领域有所建树,关注我们的微信公众号:程序员互动联盟 or coder_online。我们这里有java高手,C++/C高手,windows/Linux高手,android/ios高手,一起帮你实现愿望。

相关推荐

从IDEA开始,迈进GO语言之门(idea got)

前言笔者在学习GO语言编程的时候,GO语言在国内还没有像JAVA/Php/Python那样普及,绕了不少的弯路,要开始入门学习一门编程语言,最好就先从选择一个好的编程语言的开发环境开始,有了这个开发环...

基于SpringBoot+MyBatis的私人影院java网上购票jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于SpringBoot...

基于springboot的个人服装管理系统java网上商城jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...

基于springboot的美食网站Java食品销售jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...

贸易管理进销存springboot云管货管账分析java jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目描述贸易管理进销存spring...

SpringBoot+VUE员工信息管理系统Java人员管理jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍SpringBoot+V...

目前见过最牛的一个SpringBoot商城项目(附源码)还有人没用过吗

帮粉丝找了一个基于SpringBoot的天猫商城项目,快速部署运行,所用技术:MySQL,Druid,Log4j2,Maven,Echarts,Bootstrap...免费给大家分享出来前台演示...

SpringBoot+Mysql实现的手机商城附带源码演示导入视频

今天为大家带来的是基于SpringBoot+JPA+Thymeleaf框架的手机商城管理系统,商城系统分为前台和后台、前台用的是Bootstrap框架后台用的是SpringBoot+JPA都是现在主...

全网首发!马士兵内部共享—1658页《Java面试突击核心讲》

又是一年一度的“金九银十”秋招大热门,为助力广大程序员朋友“面试造火箭”,小编今天给大家分享的便是这份马士兵内部的面试神技——1658页《Java面试突击核心讲》!...

SpringBoot数据库操作的应用(springboot与数据库交互)

1.JDBC+HikariDataSource...

SpringBoot 整合 Flink 实时同步 MySQL

1、需求在Flink发布SpringBoot打包的jar包能够实时同步MySQL表,做到原表进行新增、修改、删除的时候目标表都能对应同步。...

SpringBoot + Mybatis + Shiro + mysql + redis智能平台源码分享

后端技术栈基于SpringBoot+Mybatis+Shiro+mysql+redis构建的智慧云智能教育平台基于数据驱动视图的理念封装element-ui,即使没有vue的使...

Springboot+Mysql舞蹈课程在线预约系统源码附带视频运行教程

今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的Springboot+Mysql舞蹈课程在线预约系统,系统项目源代码在【猿来入此】获取!https://www.yuan...

SpringBoot+Mysql在线众筹系统源码+讲解视频+开发文档(参考论文

今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的在线众筹管理系统,主要实现了普通用户在线参与众筹基本操作流程的全部功能,系统分普通用户、超级管理员等角色,除基础脚手架外...

Docker一键部署 SpringBoot 应用的方法,贼快贼好用

这两天发现个Gradle插件,支持一键打包、推送Docker镜像。今天我们来讲讲这个插件,希望对大家有所帮助!GradleDockerPlugin简介...

取消回复欢迎 发表评论: