适用于Swift开发人员的C ++

2021-01-04 20:03:43

从某种意义上讲,Swift非常类似于C ++,当我说C ++时,我的意思是C ++ 11及更高版本。也可能会说Swift是更干净的C ++,或者说C ++没有80年代的向后兼容能力。为了给出一个想法,这里是一个最小的现代C ++代码:

#include< iostream> // 1使用命名空间std; // 2自动main()-> int {// 3 cout<< "你好,世界!" ; // 4返回0; // 5}

等效的Swift将是import iostream。框架通常带有一个伞头作为单个文件,因此通常我们需要像Swift中那样为每个框架添加一个include语句。但是与Swift不同的是,我们需要为要使用自己的代码使用的每个文件添加include语句。

与Swift不同,C ++对名称空间有适当的支持。因此,如果不同框架中的两个类型具有相同的类型名称,则可以使用它们的名称空间来解析它们。例如fwkA :: JSON和fwkB :: JSON。为了获得更像Swift的行为,我们可以添加使用来指示符号不需要每次使用都使用其名称空间。

这是用C ++编写函数的现代方式。以经典方式,此函数也可以写成:

像Swift一样,我们也可以重写C ++中的运算符。在这种情况下,iostream库提供cout,它是ostream类型的对象,可输出到标准输出流。 iostream还为运算符<<用于许多常见类型,例如我们上面使用的字符串。但是,如果我们提供运算符<<<对于我们的类型。与此等效的Swift将是CustomStringConvertible协议。

最后,由于我们的函数要求返回类型为int,因此我们必须以return语句结束函数。

通过基本介绍,现在让我们从Swift的角度更详细地了解C ++。 就像在Swift中可以由编译器使用auto推导类型一样,但是如果需要,我们也总是可以显式提供类型信息。 与Swift不同,类型转换也可以在C ++中隐式发生。 需要仔细注意这一点,以免难以发现错误。 对于其他情况,我们需要将数据从一种类型显式转换为另一种类型。 例如int到string 让label ="宽度为" let width = 94let widthLabel =标签+字符串(宽度) auto label =&#34 ;;宽度是&#34 ;; auto width = 94; auto widthLabel =标签+ to_string(width); C ++中的字符串插值不如Swift插值。 但是尽管如此,我们仍可以使用显式字符串转换来构造字符串,就像我们在上面看到的那样 让苹果= 3let橙= 5let appleSummary =我有\(苹果)苹果。" let fruitSummary ="我有\(苹果+橙)水果块。"

自动苹果= 3;自动橙子= 5;自动苹果摘要="我有" + to_string(苹果)+"苹果。自动水果摘要=我有" + to_string(苹果+橘子)+"块水果。&#34 ;;

自动苹果= 3; ostringstream ss; ss<< "我有" <<苹果<< "苹果。" ; auto str = ss。 str();

让苹果= 3let橙= 5let appleSummary =我有\(苹果)苹果。" let fruitSummary ="我有\(苹果+橙)水果块。"

自动苹果= 3;自动橙子= 5;自动appleSummary =(ostringstream()<<"我有"<<<<" apples。") .str(); auto fruitSummary =(ostringstream()<<"我有"<(苹果+橘子)<<"水果块。&#34 ;)。str();

&这意味着我们希望以item作为参考。如果我们要使用自动项目,它将始终创建一个新副本。

for(auto& [key,value]:职业){cout<<键<< " :" <<值<< endl;}

Swift开发人员喜欢Optional类型。对我们来说幸运的是,C ++还提供了以c ++ 17开头的可选类型。尽管不像Swift那样花哨,但核心思想是相同的:一种具有值或没有值的类型。这是一个例子:

func问候(必填_:布尔)->串? {需要退货吗?可选的(" Hello"):nil;} func main()-> Int {let s = greeting(false)?? "嗨" print(s)//如果let str = greeting(true){print(str)// Hello} return 0}

自动问候语(需要布尔){需要返回吗?可选{" Hello"}:nullopt;}自动main()-> int {auto s = greeting(false).value_or(" Hi"); cout<< s<<恩德尔//嗨if(auto str = greeting(true)){cout<< * str<<恩德尔// Hello} return 0;}

* str可以认为是Swift中的力量展开str!。解引用后,我们可以使用。或方便的->操作员访问变量。

func greet(_ person:字符串,当天:String)->字符串{return" Hello \(person),今天是\(day)。"}打招呼(" John&#34 ;,在:" Wednesday")

自动greetPersonOnDay(字符串人,字符串日)-> string {return(ostringstream()<<" Hello"<< person<<&#34 ;,今天是"<< day<<&#34 ;。")。str();} greetPersonOnDay(" John&#34 ;," Wednesday")l

但是,如果您真的想念命名参数,那么这里有许多各种形状和大小的技巧。最简单的方法是使用结构作为参数

struct Args {字符串人,天; };自动打招呼(Args args)->字符串{return(ostringstream()<<#34; Hello"<< args。person<<&#34 ;,今天是"<< args。day&lt ;<"。")。 str(); }问候({。person =" John",。day =" Wednesday"})

func hasAnyMatches(list:[Int],条件:(Int)-> Bool)->布尔{用于列表中的项目{如果condition(item){return true}}返回false} funless lessThanTen(number:Int)->布尔{返回编号< 10} var number = [20,19,7,12] hasAnyMatches(列表:数字,条件:lessThanTen)

自动hasAnyMatches(vector< int>列表,function< bool(int)>条件)-> bool {for(auto item:list){if(condition(item)){返回true; }} return false;} auto lessThanTen(int number)-> bool {返回号< 10;}自动编号= vector< int> {20,19,7,12}; hasAnyMatches(numbers,lessThanTen);

也就是说,C ++仍然没有Swift提供的所有精美的功能操作,例如map,filter等,但很快就会在C ++ 20系列中推出。现在,我们可以将transform等同于Swift map

transform(numbers.begin(),numbers.end(),numbers.begin(),[](自动编号)-> auto {自动结果= 3 *数字;返回结果;});

这里的C ++代码实际上会使数字突变。如果我们想要真正的Swift地图等效物,我们将必须创建一个空向量并将数据附加到其中

自动映射的数字=向量<整数> {};变换(数字。开始(),数字。结束(),back_inserter(mappedNumbers),[](自动n){返回3 * n;});

与C ++中的Swift不同,结构和类之间没有太大区别,唯一的区别是C ++结构默认情况下将所有成员都公开,而类默认情况下将所有成员都私有。实例是否可变取决于实例的声明方式。

类Shape {func simpleDescription()->字符串{返回带有\(numberOfSides)边的形状。" } var numberOfSides = 0} var shape = Shape()shape.numberOfSides = 7var shapeDescription = shape.simpleDescription()

class Shape {public:auto simpleDescription()->字符串{return(ostringstream()<"形状为"< numberOfSides<<"边。")。str(); } int numberOfSides = 0;};自动整形= Shape(); shape.numberOfSides = 7;自动shapeDescription = shape.simpleDescription();

class Shape {init(name:String){self.name = name} func simpleDescription()->字符串{返回带有\(numberOfSides)边的形状。" } var numberOfSides = 0 var名称:String}

class Shape {public:Shape(string name):name(name){} auto simpleDescription()->字符串{return(ostringstream()<"形状为"< numberOfSides<<"边。")。str(); } int numberOfSides = 0;字符串名称;};

class Square:Shape {init(sideLength:Double,name:String){self.sideLength = sideLength super.init(name:name)numberOfSides = 4} func area()-> Double {return sideLength * sideLength}覆盖func simpleDescription()->字符串{返回"边长为\(sideLength)的正方形。" } var sideLength:Double} let shape = Square(sideLength:5.2,name:" My square")

class Square:public Shape {public:Square(double sideLength,string name):sideLength(sideLength),Shape(name){numberOfSides = 4; } auto area()-> double {return sideLength * sideLength; }自动simpleDescription()->字符串{return(ostringstream()<<"边长为"< sideLength)的正方形.str(); } private:double sideLength;};自动形状= Square(5.2," My Square");

在C ++中,方法默认情况下绑定到声明的类型。让我们看一个例子来理解我的意思。

自动形状= make_shared<形状> ("我的形状"); //创建新的Shape cout<<形状-> simpleDescription()<< Endl; //打印:具有0面的形状。形状。重置(新Square(5.2,"我的Square")); //释放Shape并创建新的Square cout<<形状-> simpleDescription()<< Endl; //打印:具有4面的形状。

在这种情况下,由于shape最初声明为Shape类型,因此即使在将其分配给Square类型之后,simpleDescription仍然会调用Shape :: simpleDescription。为了获得与Swift相同的行为,我们必须将基类simpleDescription()标记为虚拟的,这样,如果将Square实例分配给Shape,它将在运行时动态调度Square :: simpleDescription。

类Shape {虚拟自动simpleDescription()->字符串{return(ostringstream()<<"形状为"<< numberOfSides<<<")。 str(); } // ...}; class Square:public Shape {auto simpleDescription()->字符串覆盖{return(ostringstream()<<"边长为"<<" sideLength的正方形)。 str(); } // ...};自动形状= make_shared<形状> ("我的形状"); //创建新的Shape cout<<形状-> simpleDescription()<< Endl; //打印:具有0面的形状。形状。重置(新Square(5.2,"我的Square")); //释放Shape并创建新的Square cout<<形状-> simpleDescription()<< Endl; //打印:边长为5.2的正方形

Swift具有内置的引用计数机制。 C ++还提供引用计数,但是它不是内置的。为了获得与Swift相同的行为,我们需要使用shared_ptr和weak_ptr。这是C ++的外观

class MyString {public:MyString(string str):str(str){cout<< "创建" << Endl; }〜MyString(){cout<< "被摧毁" << Endl; }字符串str; };自动s0 = make_shared< MyString> ("你好!"); cout<< "第一强裁判:" << s0-> str<< " refCount:" << s0 use_count()<< Endl;自动w0 = weak_ptr< MyString> (s0); cout<< "第一个弱引用:" << s0-> str<< " refCount:" << s0 use_count()<< Endl;自动s1 = s0; cout<< "第二强裁判:" << s1-> str<< " refCount:" << s0 use_count()<< Endl;

这里没有什么奇怪的,因为您可以看到仅创建了一个实例,并且两个实例都引用同一事物,只是引用计数增加了。同样,弱引用不会增加引用计数。最后,完成后,实例将自动释放。就像迅速。

C ++中的错误处理比Swift更复杂。因此,如果我们想从Swift错误处理的角度看待C ++异常处理,它可能看起来像:

func send(job:Int,toPrinter printerName:String)抛出->字符串{如果printerName ==" BadPrinterr" {抛出PrinterError.noToner}返回"作业已发送"}执行{让printerResponse =尝试发送(作业:1040,toPrinter:" BadPrinter")print(printerResponse)}捕获让错误{打印(错误说明)}

自动sendJobToPrinter(int作业,字符串printerName)->字符串{如果(printerName ==" BadPrinter"){抛出PrinterError :: noToner(); }返回"作业已发送&#34 ;;}尝试{auto printerResponse = sendJobToPrinter(1040," BadPrinter"); cout<< printerResponse<< endl;} catch(const exception& error){cout<< error.what()<< endl;}

与Swift不同,假定C ++函数总是抛出异常。如果保证它们不会抛出任何异常,那么我们可以将它们标记为noexcept,在这种情况下,如果引发任何异常,应用程序就会崩溃。

在Swift中,我们的PrinterError必须符合Error,类似地,在C ++中,我们的自定义异常PrinterError必须将异常子类化。

由于C ++枚举不如Swift出色,因此实现PrinterError类型的异常要复杂得多,但是我们可以即兴进行。

class PrinterError:公共异常{public:静态自动outOfPaper()-> PrinterError {返回PrinterError(0,"缺纸"); }静态自动noToner()-> PrinterError {返回PrinterError(1," no墨粉"); }静态自动onFire()-> PrinterError {返回PrinterError(2," on fire"); } PrinterError(int errorCode,const std :: string& message)noexcept:errorCode(errorCode),message(message){}自动what()const noexcept-> const char * {返回消息。 c_str(); } int errorCode;字符串信息; };

如果有C ++击败Swift的一件事,那就是泛型。这是一个通用类在C ++中的外观示例

模板<类型名元素> class Stack {public:auto push(Element e){storage。 push_back(e); }自动弹出()->可选<元素> {if(storage.empty()){返回nullopt;自动lastEle =存储。背部 ();储存。 pop_back();返回lastEle; } private:vector<元素>储存; };

func anyCommonElements< T:序列,U:序列>(_ lhs:T,_rhs:U)->布尔值T.Element:相等,T.Element == U.Element {对于lhs中的lhsItem {对于rhss中的rhsItem {如果lhsItem == rhsItem {return true}}}返回false} anyCommonElements([1、2、3] ,[3])

模板< typename T,typename U>自动anyCommonElements(T lhs,U rhs)-> bool {for(auto lhsItem:lhs){for(auto rhsItem:rhs){if(lhsItem == rhsItem){返回true; }}}返回false;} anyCommonElements(vector< int> {1,2,3},vector< int> {3});

请注意,对Sequence或Equatable的类型没有限制,因为C ++编译器会自动进行处理。如果类型约束失败,例如T.Element没有实现==,则编译器将抛出通常难以破译的错误。如果您喜欢这种控制级别,则c ++ 20提出了有关约束和概念的建议,该建议与Swift所处的位置非常接近。