摘要:C++语言程序设计:C++程序员是如何评价GO语言的
本篇C++语言程序设计将为大家讲解C++程序员是如何评价GO语言的,看完这篇文章会让你对C++语言程序设计的知识点有更加清晰的理解和运用。
常量(const)
Go的const关键字不像C(很少有用)或C ++中的const,它表示在初始化后不应该更改变量的值。 它更像C ++的constexpr关键字(自C ++ 11),它在编译时定义了值。 所以这有点像在C中通过#define定义的宏,而且是类型安全。 例如:
const pi = 3.14
请注意,我们不为const值指定类型,因此该值可以根据值的语法使用各种类型,有点像C宏#define。 但是我们可以通过指定一个类型来限制它:
const pi float64 = 3.14
与C ++中的constexpr不同,没有可以在编译时评估的constexpr函数或类型的概念,所以你不能这样做:
const pi = calculate_pi()
你不能这样做
type Point struct {
X int
Y int
}
const point = Point {1,2}
虽然你可以使用一个简单的类型,它的底层类型可以是const:
type Yards int
const length Yards = 100
只有for循环
Go语言中的只有for循环 – 没有while或do-while循环。 与 C,C ++或Java语言相比,GO语言在这方面做了简化,尽管现在有多种形式的for循环。
例如:
for i:= 0; i < 100; i ++ {
...
}
或者像C中的while循环一样:
for keepGoing {
...
}
而for循环对于诸如字符串,切片或map之类的容器有一个基于范围的语法,我稍后会提到:
for i, c := range things {
...
}
C ++有一个基于范围的for循环,C ++ 11以后版本,但我喜欢Go可以(可选)给你索引和值。 (它为您提供索引,或索引和值,让您忽略带 _ variable 名称的索引。)
本机(Unicode)字符串类型
Go具有内置的字符串类型,并且内置比较运算符,如==,!=和<(和Java一样)。 像Java一样,字符串是不可变的,所以一旦创建便不能更改,不过可以通过将其他字符串与内置运算符 + 中的其他串连接起来来创建新字符串。 例如:
str1:=“foo”
str2:= str1 +“bar”
GO语言源代码总是UTF-8编码,字符串文本可能包含非ASCII utf-8代码点。 GO调用Unicode代码点“runes(符文)”。
虽然内置的len()函数返回字节数,而字符串的内置运算符[]运行在字节上,但是有一个utf8包用于处理字符串作为符号(Unicode代码点)。 例如:
str:=“foo”
l:= utf8.RuneCountInString(str)
而基于范围的for循环在runes中处理,而不是字节:
str:=“foo”
for _,r:= range str {
fmt.Println(“rune:%q”,r)
}
c++ 仍没有标准等效项。
Slices(切片)
GO语言的Slices(切片)与 c 中动态分配的数组类似, 尽管它们实际上是底层数组的视图, 而两个切片可以是同一底层数组的不同部分的视图。他他们感觉有点像C ++ 17或GSL :: span中的std :: string_view,但它们可以轻松调整大小,如C ++ 17中的std :: vector或Java中的ArrayList。
我们可以声明一个像这样的范围, 并追加到它:
a := []int{5, 4, 3, 2, 1} // A slice
a = append(a, 0)
数组(大小固定,不像切片)具有非常相似的语法:
a := [...]int{5, 4, 3, 2, 1} // An array.
b := [5]int{5, 4, 3, 2, 1} // Another array.
通过指针将数组传递给函数使用时必须注意,否则就会导致按值赋值。
与C ++中的std :: array或std :: vector不同,切片不是(深度)可比较或可复制的,这感觉相当不方便。
如果内置的append()函数需要比现有容量多(可能超过当前长度),则可以分配更大的底层数组。 所以你应该始终如此分配append()的结果:
a = append(a, 123)
我认为你不能将指针指向切片中的元素。 如果可以的话,垃圾收集系统需要保留以前的底层数组,直到你停止使用该指针。
与C或C ++数组不同,不同于使用std :: vector的operator [],尝试访问切片的无效索引将导致紧急(有效地崩溃),而不仅仅是未定义的行为。 我更喜欢这个,虽然我想像是边界检查有一些小的性能成本。
Maps(映射)
Go有一个内置的 map 类型。这大致相当于C ++的std :: map(平衡二叉树)或std :: unordered_map(哈希表)。GO的maps显然是哈希表,但是我不知道它们是单独链接的哈希表(如std::unordered_map)还是开放寻址哈希表(不幸的是,标准C ++中没有什么)。
显然,哈希表中的 keys 必须是hashable 和 comparable。这本书提到了可比性,但是很少有事情是可比的,他们都很容易hashable。只有基本类型(int,float64,string等,但不是slice)或数据结构是可比较的,所以可以用它们作为一个关键。可以通过使用(或制作)您的值的哈希值的基本类型(如int或字符串)来解决此问题。我喜欢C ++需要一个std :: hash <>专业化,尽管我希望写一个更容易。
与C ++不同,您不能保留指向地图中的元素的指针,因此更改值的一部分意味着将整个值复制回地图,大概用另一个查找。显然,当地图必须增长时,完全避免无效指针的问题。 C ++可以让您承担风险,指定何时可能无效。
Go Maps显然是一个比C更大的优势,否则您必须使用一些第三方数据结构或编写自己的数据,通常只有很少的类型安全。
看起来像下面这样:
m := make(map[int]string)
m[3] = "three"
m[4] = "four"
Multiple return values(多个返回值)
Go中的函数可以有多个返回类型,更明显的是输出参数。 例如:
func getThings() (int, Foo) {
return 2, getFoo()
}
a, b := getThings()
这有点像在现代C ++中返回元组,特别是在C ++ 17中的结构化绑定:
std::tuple<int, Foo> get_things() {
return make_tuple(2, get_foo());
}
auto [i, f] = get_things();
Garbage Collection(垃圾回收)
与 Java 一样, GO 具有自动内存管理, 因此可以信任在使用完这些实例之前不会释放它们, 也不需要显式释放它们。因此,可以放心地完成此操作,,而不必担心以后释放该实例:
func getThing() *Thing {
a := new(Thing)
...
return a
}
b := getThing()
b.foo()
你甚至可以这样做,不用关心和了解实例是在堆栈还是堆上创建的:
func getThing() *Thing {
var a Thing
...
return &a
}
b := getThing()
b.foo()
我不知道Go如何避免循环引用或不需要的“泄漏”引用,因为Java或C ++将使用弱引用。
我不知道如何,或者如果,Go避免了Java由于垃圾收集而间歇性放缓的问题。 Go似乎是针对系统级代码,所以我想它一定要做得更好。
然而,也像Java一样,并且可能像所有垃圾收集一样,这仅对于管理内存而非一般资源是有用的。 程序员通常很高兴在代码完成使用后一段时间内释放内存,而不一定立即。 但其他资源,如文件描述符和数据库连接,需要立即释放。 一些事情,如互斥锁,通常需要在明显的范围结束时释放。 破坏者使之成为可能。 例如,在C ++中:
void Something::do_something() {
do_something_harmless();
{
std::lock_guard<std::mutex> lock(our_mutex);
change_some_shared_state();
}
do_something_else_harmless();
}
Go不能这样做,所以它有defer(),而是让你指定一个事情发生在一个功能结束。 这是一个烦人的延迟与功能相关联,而不是一般范围。
func something() {
doSomethingHarmless()
ourMutex.Lock()
defer ourMutex.Unlock()
changeSomeSharedState()
// The mutex has not been released yet when this remaining code runs,
// so you'd want to restrict the use of the resource (a mutex here) to
// another small function, and just call it in this function.
doSomethingElseHarmless()
}
这感觉像是一个尴尬的黑客,就像Java的试用资源一样。
我更愿意看到一种语言,以简明的语法给我所有的范围资源管理(包括析构函数),引用计数(如std :: shared_ptr <>)和垃圾回收,所以我可以有可预测的,明显的, 但可靠,必要时释放资源,垃圾收集时我不在乎。
当然,我不是假装内存管理在C ++中很容易。 当它很困难时,这可能非常困难。 所以我明白垃圾收集的选择。 我只是期望系统级语言提供更多。
不喜欢GO语言的地方
除了上面提到的较小的语法烦恼以及缺乏简单的通用资源(而不仅仅是内存)管理之外,还有其它一些缺陷。
No generics(没有泛型)
Go专注于类型安全,特别是对于数字类型,使得缺乏泛型令人惊讶。我可以记得在泛型之前使用Java的感觉是多么令人沮丧,而这感觉差不多是尴尬的。没有泛型,我很快发现自己不得不选择缺乏类型安全或反复重新实现每种类型的代码,感觉就像这个语言作斗争。
我知道泛型是难以实现的,必须做出选择,觉得GO语言能到达什么程度(可能超过Java,但不如C ++),我知道Go将远远超过一个更好的C.但我认为泛型是不可避免的一次,像Go,你追求静态型安全。
不知何故,切片和maps容器是通用的,可能是因为它们是内置类型。
Lack of standard containers(缺少标准容器)
Go在其标准库中没有队列或堆栈。在C ++中,我定期使用std :: queue和std :: stack。我认为这些将需要仿制药。人们可以使用go的切片(动态分配的数组)实现相同的功能,并且可以将其包装在自己的类型中,但是类型只能包含特定类型,因此将为每种类型重新实现。或者您的容器可以容纳{}类型的接口(显然有点像Java对象或C ++ void *),放弃(静态)类型安全。
以上,关于C语言的全部内容讲解完毕啦,欢迎大家继续关注!更多关于C语言的干货请关注职坐标C语言频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号