从C++到Go(二)1
上一节讲完了概念差异,这节讲解语法
语法
声明语法和C++相反,类型跟在名字后面。类型的语法和变量的使用方式与C++不同。类型声明可以很容易地从左向右阅读。(var v1 int
-> 变量v1
是一个int
)
//Go C++
var v1 int // int v1;
var v2 string // const std::string v2; (approximately)
var v3 [10]int // int v3[10];
var v4 []int // int* v4; (approximately)
var v5 struct { f int } // struct { int f; } v5;
var v6 *int // int* v6; (but no pointer arithmetic)
var v7 map[string]int // unordered_map<string, int>* v7; (approximately)
var v8 func(a int) int // int (*v8)(int a);
声明通常采用关键字地形式,后跟要声明的对象的名称。关键字可以是var
, func
, const
, 或 type
中的一个。方法的声明是一个例外,因为方法的接收者出现在要声明的对象名称之前。
你也可以使用关键字,然后在圆括号中使用一系列声明。
var (
i int
m float64
)
当声明一个函数时,你必须为每个参数提供参数名或者不提供任何参数(也就是说,C++允许void f(int i, int);
但是Go不允许func f(i int, int)
)。为了方便,在Go中你可以将具有相同类型的多个名称分组:
func f(i, j, k int, s, t string)
变量可以在声明时初始化。完成此操作后,可以指定类型但不是必须的。当没有指定类型时,变量的类型为初始化表达式的类型
var v = *p
如果一个变量没有被显式初始化,则必须指定变量的类型。此时变量将被隐式初始化为类型的0值(0
, nil
等)。因此在Go中没有未初始化的变量。
在函数内部,存在一个简短的声明语法:=
v1 := v2 // C++11: auto v1 = v2;
这等价于
var v1 = v2 // C++11: auto v1 = v2;
Go允许同时执行多个赋值。也就是说,首先计算右侧的所有值,然后将这些值赋值给左侧的变量。
i, j = j, i // Swap i and j.
函数可以有多个返回值,在圆括号列表中声明。返回的值可以赋值给变量的列表
func f() (i int, j int) { ... }
v1, v2 = f()
在Go中多个返回值是错误处理的主要机制
result, ok := g()
if !ok {
// Something bad happened.
return nil
}
// Continue as normal.
…
或者更简洁地说,
if result, ok := g(); !ok {
// Something bad happened.
return nil
}
// Continue as normal.
…
Go语言的代码通常不使用分号。从技术上说,所有Go语句都是由分号结束的。但是会将非空白行的末尾视为分号,除非该行为明显不完整(精确的规则被描述在语言规范中)。这么做的后果是,在某些情况下,Go不允许你使用换行符。例如,你可能想这么做
func g()
{ // 无效
}
一个分号将被插入到g()
的后面,导致了一个函数声明而不是一个函数定义。类型地,你可能想这么做
if x {
}
else { // 无效
}
一个分号将被插入到}
后面,导致了一个语法错误。
由于分号会结束语句,因此您可以像在C ++中一样继续使用它们。但是,这是不推荐的编码风格。Go语言的代码省略不必要的分号,实际上,除了for
语句中的分号和在一行中需要多个简单语句时,几乎不再使用分号。
我们建议你不要关心分号和括号的位置,而是使用gofmt
程序格式化代码。这个工具会产生一个唯一标准的Go语言代码风格,并且让你把注意力集中在代码而不是代码的格式上。虽然这种风格可能在刚开始看起来很奇怪,但熟悉之后会带来舒适感。
当使用指向结构体的指针时,使用.
而不是->
。从语法上讲,结构体和指向结构体的指针的使用方式相同。
type myStruct struct{ i int }
var v9 myStruct // v9是一个结构体类型
var p9 *myStruct // p9是一个指向结构体的指针
f(v9.i, p9.i)
Go不需要if
语句、for
语句和switch
语句中的圆括号。Go需要再if
或for
语句中使用花括号。
if a < b { f() } // 无效
if (a < b) { f() } // 无效
if (a < b) f() // 无效
for i = 0; i < 10; i++ {} // 有效
for (i = 0; i < 10; i++) {} // 无效
Go没有while
语句,因此也没有do/while
语句。for
语句可以等价地成为while
语句。
Go允许break
和continue
指定一个标签,该标签必须引用for
、switch
或select
语句。
再switch
语句中,case
标签不会穿越。你可以让他们穿越通过使用fallthrough
关键字。
switch i {
case 0: // 空的case
case 1:
f() // 当i == 0时,f不会被调用!
}
一个case
可以有多个值
switch i {
case 0, 1:
f() // f is called if i == 0 || i == 1.
}
在case
中的值不需要为常量,任何支持相等性比较操作的类型例如字符串或指针,都可以被使用,当忽略switch
的值时,默认为true
(switch true)。
switch {
case i < 0:
f1()
case i == 0:
f2()
case i > 0:
f3()
}
在包含defer
语句的函数返回后,defer
通常用来代替C ++中析构函数,但与调用代码关联,而不与任何特定的类或对象关联。
d := open("filename")
defer close(fd) // fd will be closed when this function returns.