C++11 和 C++14 简介及示例代码片段
无论您是已经使用 C++ 一段时间,还是编程新手,您仍然需要了解有关 C++11 的基本细节。
C++ 编程语言基于 C 编程,从一开始就连接到 UNIX。C的创造者是丹尼斯·里奇。
C++ 在 C 之上添加了许多面向对象的特性,包括:类、对象、模板、命名空间、少数高级数据类型、异常、引用、STL等。
C++ 被认为是一种支持面向对象编程和多线程的通用语言。C++ 的创造者是 Bjorne Strousturp。
1.什么是C++11?
我们在 C++ 中遇到的问题之一是缺乏标准。
第一个官方 C++ 标准于 1998 年左右开始,此后 C++ 标准一直在不断改进。有一个标准作为开发人员和组织的官方指南,对于它的发展至关重要。
这个标准正式称为C++11
C++14 只是 C++11 的一个小扩展,几乎没有错误修复和小改动。
该标准注册为信息技术编程语言 C++,正式名称为 ISO/IEC 14882:2011(广泛称为 C++11)。
早些时候,问题之一是当您尝试使用向量的向量时,您可能希望在定义的左侧写 >>。但是你必须写 > >,是的,中间有一个空格,这没有意义,而且让开发人员感到困惑。
现在,随着标准的调整,这个问题得到了解决。与此类似,修复了几个不合逻辑的小语法问题,并变得一致。
C++ 也与以前版本的 C 兼容。
我们还有模板别名,对模板和算法的一些改进,一些新模板,如:hash_tables、tuple 和 array。我们已经承诺元编程和类型跟踪、时间实用程序、内联命名空间、可变参数模板。
这些改进中的每一个都可以在单独的详细文章中进行解释。我们将尝试在本文中重点介绍新标准的某些部分。
2. 基于范围的 For 循环
要循环遍历容器中的事物(数组、堆栈、列表、向量等),我们通常会编写如下所示的 for 循环:
for(int i=0; i<n; ++i) doSomething;
在 for 循环内部,在第一部分,我们初始化变量 i=0;i 是 int 数据类型。您也可以使用未签名的 init。接下来我们有一个条件 i<n。最后,我们用 ++i 增加 i。变量 i 是 for 语句的局部变量。有几次,我错误地尝试做这样的事情,但没有成功: for(int i=0, int j=0;...
但是,现在,您有一个 for 循环可以输入某个数组的元素,对于您可以使用的其他容器:非成员 begin() 和 end()。
因此,如果您尝试将元素引入数组,则可以使用以下内容:
for(int& i: someDataStructure) { doSomething();}
如果您尝试显示存储在数组中的值,您可以保护数组的成员,并省略 & 如下所示:
for(int i: someDataStructure) doSomething();
在上面的两个for循环中,第一个是使用引用,第二个是启用按值访问。第一种方式允许修改数据结构中的元素,第二种方式不允许您修改正在使用的容器中的元素。
3.强类型枚举
使用枚举的旧方法有其缺陷,它通过一种新的声明数据的方法来修复,该方法几乎没有可能的选择。
一些示例情况是:一年中的几个月、一周中的几天、太阳系中的行星,或者可能是四条电阻器上的线路。
让我们看看我们将如何在坐标系的情况下使用它。众所周知,它有四个区域:第一、第二、第三和第四。它以坐标线为界。
例如:
enum class CoordinateArea { FirstArea, SecondArea, ThirdArea, FourthArea};
CoordinateArea caOne = CoordinateArea::FirstArea;
CoordinateArea caSome= CoordinateArea::FourhtArea;
您可能会注意到,您还需要范围解析运算符。
4. Lambda 函数的 Lamba 表达式
这就像一个有主体但没有名字的函数。它们的声明如下所示:
[firstPart](secondPart) TypeYouReturn{ BodyOfLambda}(acctualParameters);
firstPart 用于将在 lambda 函数内部利用的变量范围。如果需要,您可以使用更多变量。
将此语法用于 [firstPart] 时,请记住以下几点:
- [] 这意味着您不会向 lambda 提供任何东西。
- [&] 用于表示您有一些参考资料要弄乱。
- [=] 用于制作副本。
- [this] 用于封闭类。
secondPart 是无名函数的参数列表所必需的,但它也可以留空。
TypeYouReturn 用于注册将从您的 lambda 返回的类型。
BodyOfLambda 用于您希望执行的操作,在这里您将键入一些代码,这些代码将用于执行您打算在此函数主体中应用的操作。
实际参数用于向 lambda 函数提供输入。
lambda 函数的一个例子:
double dUpperPart = [](double dX, double dY)double{ return dX*dX +dY*dY;}
lambda 函数的另一个例子:
vectror<int> iVector;
for_each( begin(iVector), end(iVector), [](int n){if(n%2==0)cout<<n<<end;});
5. 静态断言
在您的代码中,如果您尝试输入超出范围的值或不应估算的值,则它是静态断言的良好候选者。
这有两个部分:
- 首先,被评估的表达式
- 其次,如果测试条件不匹配,则显示为消息的字符串文字。
这是语法:
static_assert(evaluatedExpression, stringMessage);
static_assert 的示例用法:
static_assert(sizeof(long long int)>=16;”This is unexpected”);
6. 随机数生成
这已经存在很长时间了。生成随机数的旧方法被一种新方法所取代。
要了解它是如何完成的,只需访问 Bjorne 的网站,这里有一个关于如何处理随机数的好例子。
7.移动和&&
为此,我们需要了解左值和右值。
l 代表左侧。它是左值的基本属性之一,它是内存中可以位于表达式左侧的对象;一个例子是一些变量。
r 代表右侧。这将位于表达式的右侧,不应在右侧找到,因为它是一个常数。
在旧标准中,我们只能使用左值作为参考,现在已经改变了,您也可以使用右值作为参考。它在您需要复制某些对象的情况下很有用,更好的解决方案是使用移动语义。
要在类中应用移动构造函数,您可以这样调用它:
MovableClass(MovableClass&&);
而且,如果您需要移动分配,则调用如下:
MovableClass&& operator=(MovableClass&&);
在新标准中,这得到了很好的支持,容器和算法使用这种移动语义并进行了适当的优化。
8. 关于指针的一些注意事项
指针是 C++ 和 C 中的重要概念之一。
如您所知,它们用于将某个对象的地址存储在内存中,这样您的代码就有很大的灵活性。
可以构造动态数据结构,您可以非常快速地访问数组元素等。
关于指针,有几件事值得一提。
首先,是用 nullptr 替换 NULL。这意味着您的指针没有保存地址,但它没有指向。这就像你有一个零值的变量,但它也有一些差异。
接下来的几件事是新类型的智能指针:唯一指针、共享指针和弱指针。让我们讨论一下它们的用途。
unique_ptr 是 C++ 的新特性,它将使您能够保护存储在内存中的某些资源的所有权。如果某物拥有所有权,它就不能共享,但它是可移动的。这意味着您可以将其转移到另一个唯一的指针。
shared_ptr 顾名思义,它适用于需要共享内存中某些资源的所有权的情况。
weak_ptr 允许访问可能存在于内存中的内容,如果您有占用内存的对象,则授予访问权限,并且可以删除该对象,如果上次使用过,则调用必要的析构函数。
unique_ptr 的示例将解决异常不安全代码。
唯一指针的语法:
unique_ptr<someType> suniquePtr(new someType(args));
...
uniquePtr.release();
对于 shared_ptr,声明将是这样的:
shared_ptr<someType> somePtr(new someType(args));
弱指针语法:
weak_ptr<someType> weakPtr= somePtr;
9. 统一初始化和初始化列表
如果您希望使用构造函数,最好用几个 {} 替换旧样式的初始化 ()。
使用构造函数初始化的旧样式可以这样实现:
CSomeClass SomeObject(argument1,argument2);
上面的内容将更改为如下所示:
CSomeClass SomeObject={argument1,argument2};
如果您需要将一些值放入向量中,通常会使用 push_back 几次,或者可以通过使用旧式括号的初始化来实现,现在已经过时了,
vector <int> ourVector;
for(int i=0; i<5; ourVector.push_back(i++));
最好按如下所示进行。在下面,您仍然可以将元素添加到列表的末尾,但现在从语法的角度来看更加统一:
vector< int> ourVector={0,1,2,3,4,};
对构造函数的改进
如果您尝试计算总和、最小值或计算满足某个条件的数字的数量,您可以用某个值初始化变量。
所以,如果你试图找到类和数据类型的类比,你会问你自己是否可以用值 0 初始化类的某个成员。
如果您的编译器支持新标准,则可以执行此操作。
通常的做法是:
class CSomeClass
{
private:
int nSomeValue=0;
...
}
这样,如果您调用构造函数,它会将这些值放入 nSomeValue,但如果您省略编写自己的构造函数,则 0 将作为某个起始值存储到该 nSomeValue 保留的位置。它很有用,我会向我们推荐它。
您可能喜欢的下一件事是委托构造函数。这意味着一旦您编写了一个构造函数,它也可以在其他构造函数中重用。
我们有一件有趣的事情是继承构造函数。现在,如果您可以在 CChild 类中使用构造函数。
为此,您将编写如下内容:
class CChild: public CParent
{
public:
using CParent::CParent
}
10. 虚拟功能的挑战
如果您熟悉虚拟方法,您就会知道拥有它们很重要,因为指针应该“知道”要访问哪些方法。
当有继承,需要应用虚方法时,在名字前面写virtual就足够了,每次在低级类中有方法时,都会用虚方法进行挖掘。
还有一些其他问题,其中之一是用于跟踪方法的大表,它可能会变得有点慢,但我不会在这些问题上花太多时间,我更愿意向您介绍其中一些问题的解决方案,目前覆盖和最终是神奇的解决方案。
如果您尝试覆盖该函数,这可能意味着您希望使用与在父类中应用的数据类型不同的数据类型,但是您需要更改继承类中方法的行为方式,现在您需要做的就是是添加覆盖并且您不是重载该方法,而是覆盖它。
要应用它,您只需添加覆盖并完成您想要的操作。
如果您希望防止方法被覆盖,则在方法前面添加 final,然后就无法修改方法的行为方式。
11. C++11 中的多线程
很长一段时间我们没有一些标准来在您的程序中使用更多线程。
不同的公司已经注意到程序员需要类似的东西,所以他们为此开发了自己的库。最流行的是POSIX。
编写线程时要记住以下几点:
- 如果你想调用线程,它是 std 命名空间中的一个类,你需要添加一些头文件。适当的是添加 include <thread>,或者它可能以其他方式签名。
- 当你开始你的线程时,你可以使用:加入、交换、分离、睡眠等。
- 如果您尝试保护某些资源免受其他线程的影响,以便现在获得预期结果,则应该在其库中添加不同类型的互斥锁:互斥锁、递归互斥锁、定时互斥锁和递归定时互斥锁。
12. C++11 风格的示例代码
现在,我们将看几个示例来说明 C++11 的新风格。请记住,您的编译器应该支持这一点。
C++11 标准的第一个例子:
#include <iostream>
using namespace std;
int
main(void)
{
int ourArray[5];
for(int& i: ourArray)
{
cout<<”Next element is->”;
cin>>i;
}
cout<<”Elements in array are!”<<endl;
for(int i: ourArray) cout<<n<<endl;
return 0;
}
带有向量的 C++ 标准的第二个示例:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int
main(void)
{
vectro<int> nVector { 0, 5, -3, 11, -3, 7, 0, 2, 7, -6, 11, 0, 21, 12, -5};
for_each(nVector.begin();
nVectro.end();
[](int i)
{
cout<<n<<” is”
if(n==0)
cout<<” zero ”;
else if(n>0)
cout<<” positive “;
else
cout<<” negative “
cout<<”number\n”;
} );
return 0;
}
您可以尝试使用数组作为新容器的几个示例,例如:元组或其他一些。
要使用元组,您可以这样做:
auto tuple = make_tuple(“triangle”, ‘t’, 10, 15, 20);
要声明将具有一个元素字符串而另一个是向量的映射,您可以这样做:
map<string,vector<int>> aMap;
- 点赞
- 收藏
- 关注作者
评论(0)