GO和C++的语法对比¶
1 基本常识¶
1.1 程序入口main¶
- GO
- 包名为main
- 入口函数为main
- C++
- 入口函数为main
- C++代码
//C++代码
int main(){
}
- GO代码
//GO代码
package main
func main() {
}
1.2 一般规则¶
- Go中声明变量时类型在变量名后面
- Go中定义函数时返回值类型在函数名后面
1.3 语句结尾分号¶
- GO
- 以分号
;
作为语句结尾 - 可不写分号
;
,但以换行区分
- 以分号
- C++
- 必须以分号
;
作为语句结尾 - Go代码
- 必须以分号
package main
import (
"fmt"
)
func main() {
fmt.Println("hello world!")
fmt.Println("hello world!");fmt.Println("hello world!")
}
- C++代码
#include<iostream>
int main(){
std::cout<<"hello world!"<<std::endl;
}
1.4 引用其它文件¶
- GO
- 使用关键字
import
- 使用关键字
- C++
- 使用
include
- 使用
- Go代码
package main
import (
"fmt"
)
func main() {
fmt.Println("hello world!")
}
- C++代码
#include<iostream>
int main(){
std::cout<<"hello world!"<<std::endl;
}
1.5 花括号{}¶
- 相同点:
- 用于函数体
- 用于数组、map、struct类型初始化变量值(c++还能用于int等基本类型,Go不能)
- 用于限制局部范围
- 不同点:
- 用于函数、for、if、switch等时,Go中左花括号
{
不能单独成行 - Go示例
package main
import (
"fmt"
)
type S struct{
a1 int
a2 int
}
var m = map[int]int{1:1}
var arr =[3]int{1,2,3}
func main(){
s1:=S{1,2}
fmt.Println("s1 =",s1)
{// 局部区域
v1:=1
fmt.Print(v1)
}
}
/* 输出结果
s1 = {1 2}
1
*/
1.6 小括号()¶
- 相同点:
- 作用运算符高优先级范围
- 作为函数参数范围
- 不同点:
- Go小括号
()
可以用在多行变量声明范围 - Go小括号
()
可以用在import
导入多个模块的范围 - Go中小括号不用于for、if、switch等上面
- Go示例
package main
// 用于import
import (
"fmt"
)
type S struct{
a1 int
a2 int
}
// 用于函数参数,函数返回值
func (s *S)sum()(sum int){
//用于函数调用
fmt.Println(*s)
sum=s.a1+s.a2
return sum
}
func main(){
var s =S{1,2}
fmt.Println(s.sum())
}
/* 输出结果
{1 2}
3
*/
1.7 ++i和i++¶
- 相同点:
- C++和GO都有i++,后缀++的形式
- 不同点:
- Go中不支持++i前缀形式
- Go中i++只能单独占一行,因为i++是一条语句,而不是一个表达式
- Go代码
package main
import "fmt"
func main() {
var i int = 0
i++;
fmt.Println("i =",i)
// error i++只能单独一行
//a:=i++
// error Go中没有++i
//++i
}
- C++代码
#include <iostream>
using namespace std;
int main() {
int i=0;
i++;
++i;
int j=i++;
}
1.8 Go语法误区¶
2 变量¶
- 语法差异:
- GO:
var 变量名 变量类型 = 变量值
- C++:
变量类型 变量名 = 变量值
- Go中未使用的局部变量会引发编译错误,全局变量不会
- GO可以通过运算符
:=
进行变量定义,但只能在函数体中,且会覆盖外层作用域中的同名变量,而c++中没有此运算符 - Go代码
//GO
package main
import (
"fmt"
)
//声明并初始化
var var1 int =1
//声明但不初始化,值默认0
var var2 int
//声明并初始化,可以省略类型,编译器会自动识别,类似于c++中auto功能
var var3 =3
// 多变量声明,相同类型
var var5,var6 int
// 多变量声明,不同类型
var (
var7 int
var8 string
)
func main() {
fmt.Println("GO: hello world")
// :=定义只能用于函数体内,不用用在全局,且必须之前没有定义过
var4 := 4
fmt.Print("var1= ",var1," var2 =",var2," var3 =",var3 ," var4 =",var4)
}
- C++代码
//c++
int i1=2;
int i2;
// 多变量声明
int i1=1,i2=2;
3 常量¶
- GO常量和变量类似,以关键字
const
取代var
就是声明变量,C++中常量声明也是由关键字const
声明 - GO和C++中const常量都要声明时初始化,且不能改变
- Go代码
//GO
package main
const i1 int=1
const i2=2
// 多常量定义
const(
i4,i5 int=3,4
)
const i6,i7=5,6
//i1 i2 i3 分别为 0 1 2,j1 j2 j3 分别为 0*3 1*3 2*6
const(
i1,ji=iota,iota*3
i2,j2
i3,j3
)
func main() {
}
- C++代码
//c++
const int i1=1;
const auto i2=2;
// 多常量定义
const int i1=1,i2=2;
4 数据类型¶
4.1 枚举¶
- GO中并没有枚举
- 但可以借助
const
和iota
来模仿实现个类似功能,而C++通过关键字enum
来定义枚举类型,且默认自增+1 - Go代码
//GO
//iota==0,也有自增+1作用
const(
i1=iota //0
i2 //1
i3 //2
i4=10 //10
i5 //10
i6=iota //5 重新自增+1,且计数累计从i1~i5
i7 //6
i8=iota*2//7*2
i9 //8*2
i10 //9*2
)
const(
i1 float32=iota
i2 int
i3
)
- C++代码
//c++
enum {
i1=0, //0
i2, //1
i3 //2
}
enum :int{ //bool char均可
i1=0,
i2,
i3
};
4.2 引用¶
- GO中没有泛化概念的引用,
slice map channel
这3种预定义类型具有引用功能,而C++中任意类型均可成为引用,形如Type& name
。 - C++引用必须声明时赋值
4.3 指针¶
- 相同点:
- 可以使用
new
分配指针 - 用
&
取地址 - 用
*
取指针指向的值 - 不同点:
- Go用
.
来取指针的成员,C++用->
来取指针的成员 - 用
new
分配的指针,在Go中不用手动释放内存,C++要调用detele释放 - Go代码
package main
import (
"fmt"
)
type A struct{
i int
}
func main() {
var str1 = "hello"
var ptr *string = &str1
fmt.Printf("str = %s ptr = %p str1 addr=%p", *ptr, ptr, &str1)
var pA =new(A)
pA.i=1;
fmt.Print("pA value:",*pA)
}
//执行结果:
//str = hello ptr = 0xc00004c250 str1 addr=0xc00004c250
//pA value:{1}
- C++代码
//cpp
int main(){
int i=1;
int* pi1=&i;
int* pi2=new int;
int* pi3=new int(2);
delete pi2;
delete pi3;
}
5 容器¶
5.1 数组¶
- 语法:
- Go:
var 数组名 [数组大小]类型
- 定义并初始化:
var 数组名 = [数组大小]类型{值1,值2,...,值n}}
- C++:
类型 数组名[数组大小(可省略,自动推导)]
- 定义并初始化:
类型 数组名[数组大小(可省略,自动推导)] = {值1,值2,...,值n}
- 相同点:
- 数组的大小都是固定的
- 数组分配在连续的内存空间
- 不同点:
- Go中数组不用用const修饰,而C++中可以
- Go代码
package main
import (
"fmt"
"unsafe"
)
// 固定大小数组,不初始化
var arr1 [10]int
// 固定大小数组,初始化部分
var arr2= [3]int{1,2,3}
func main() {
fmt.Println("arr1:",arr1);
fmt.Println("arr2:",arr2);
fmt.Println("size of int = ",unsafe.Sizeof(int(0)))
for i := 0; i < len(arr2); i++ {
fmt.Printf("arr[%d] addr =[%p]\n",i,&arr1[i])
}
/*
注释
*/
}
/*输出结果:
arr1: [0 0 0 0 0 0 0 0 0 0]
arr2: [1 2 3]
size of int = 8
arr[0] addr =[0x2b06e0]
arr[1] addr =[0x2b06e8]
arr[2] addr =[0x2b06f0]
*/
- C++代码
#include <iostream>
using namespace std;
int arr1[10];
int arr2[10] = {1, 2, 3};
int arr3[] = {1, 2, 3};
int main() {
cout << "arr1:";
for (auto& elem : arr1) {
cout << elem << " ";
}
cout << endl << "arr2:";
for (auto elem : arr2) {
cout << elem << " ";
}
cout << endl << "arr3:";
for (auto elem : arr3) {
cout << elem << " ";
}
}
/* 输出结果
arr1:0 0 0 0 0 0 0 0 0 0
arr2:1 2 3 0 0 0 0 0 0 0
arr3:1 2 3
*/
5.2 切片¶
- Go的切片是可以理解成动态数组,Go中数组大小是固定的,而切片大小是动态可变的
- Go切片可以用make创建
- Go切片和数组一样可以通过索引来访问元素
- Go中切片是一种C++中的引用类型,在函数内改变切片值,出了作用域,依然有效
- Go示例
package main
import (
"fmt"
)
// 切片,不初始化
var slice1 []int
// 切片,初始化
var slice2= []int{1,2,3}
// make 创建切片
var slice3=make([]int,5,10)
func main() {
fmt.Println("slice1",slice1)
fmt.Println("slice2 ",slice2)
fmt.Println("slice2[0:3] ",slice2[0:3])
fmt.Println("slice2[1:2] ",slice2[1:2])
fmt.Println("slice2[:] ",slice2[:])
fmt.Printf("slice3[:]=%v len=%d cap=%d",slice3[:],len(slice3),cap(slice3))
for i, v := range slice2 {
fmt.Println("index = ",i," value = ",v)
}
for i := 0; i < len(slice2); i++ {
println(slice2[i])
}
}
5.3 map¶
- 语法:
- Go:
var map名 map[键类型]值类型
- 定义并初始化:
var map名 = map[键类型]值类型{键1:值1,键2:值2,...,键n:值n}}
- make创建:
var map名 = make(map[键类型]值类型)
- 不同点:
- Go中map是一种C++中的引用类型,在函数内改变map值,出了作用域,依然有效
- Go示例
package main
import "fmt"
var map1 map[int]string
var map2 = map[int]string{1: "hello", 2: "world"}
// 使用make创建map
var map4=make(map[string]int)
func main() {
map3 := map[string]string{"1": "hello", "2": "world"}
fmt.Println("map3:", map3)
// 获取map中指定key对应的值
fmt.Println("map3 key \"2\" value=",map3["2"])
// 向map中插入元素
map4["3"]=3
fmt.Println("map4:", map4)
//使用for range遍历map
for k, v := range map3 {
fmt.Printf("key %v value %v\n",k,v)
}
}
/* 输出结果
map3: map[1:hello 2:world]
map3 key "2" value= world
map4: map[3:3]
key 1 value hello
key 2 value world
*/
5.4 list¶
- 声明语法:
- Go:
var 变量名 list.List
- 使用list内建函数
list.New()
创建list指针:变量名 := list.New()
- Go示例
package main
import (
"container/list"
"fmt"
)
var l1 =list.List{}
var l2 list.List
func main() {
// 通过list内建函数New创建list指针
l3:=list.New()
l3.PushBack(1);
l3.PushBack("hello")
for e:=l3.Front();e!=nil;e=e.Next(){
fmt.Print(e.Value," ")
}
}
/* 输出结果
1 hello
*/
6 别名¶
- 相同点:
- 都可以设置类型、函数的别名
- 不同点:
- 语法不同:
- Go:
type 类型别名 类型
- C++:
typedef 类型 类型别名
- Go:
- Go代码
package main
import (
"fmt"
"reflect"
)
func add(a, b int) int {
return a + b
}
type add_type func(int, int) int
type int32 int
var i1 int32
var i2 int
func main() {
fmt.Println("i1=", reflect.TypeOf(i1))
fmt.Println("i2=", reflect.TypeOf(i2))
var f_add add_type
f_add = add
fmt.Println("f_add(1,2)=", f_add(1, 2))
}
// 输出结果:
// i1= main.int32
// i2= int
// f_add(1,2)= 3
- C++代码
#include <iostream>
using namespace std;
int f() {
return 1;
}
typedef int int32;
typedef int(*f_t)();
int main() {
int32 i1=1;
int i2=2;
f_t fun=f;
cout<<"i1 type= "<<typeid(i1).name()<<endl;
cout<<"i2 type= "<<typeid(i2).name()<<endl;
cout<<"fun()"<<fun()<<endl;
}
// 输出结果:
// i1 type= i
// i2 type= i
// fun()1
7 流程控制¶
7.1 for¶
- 相同点:
- Go和C++ for结构大致相同
- 都可以使用break、continue
- 不同点:
- Go中for处理语句不需要放在小括号
()
里面 - Go中左花括号
{
必须和for
在同一行,不能单独成行 - Go代码
package main
import "fmt"
func main() {
var arr = [3]int{1, 2, 3}
// 不需要()包裹
// 左花括号{和for在同一行
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
}
/* 输出结果
0
1
2
*/
- C++代码
#include <iostream>
using namespace std;
int main() {
int arr[3]={1, 2, 3};
for (int i = 0; i < sizeof(arr)/sizeof(int); i++) {
cout<<arr[i]<<endl;
}
}
/* 输出结果
0
1
2
*/
7.2 for Range¶
- 不同点:
- Go中数组、切片、字符串返回索引和值共2个值;map返回key和值;C++中只能返回1个值
- Go中需要使用Range关键字,C++中不需要
- Go代码
package main
import "fmt"
func main() {
var arr=[3]int{1, 2, 3}
for index, v := range arr {
fmt.Printf("index =%v value =%v\n",index,v)
}
var m = map[int]int{1:1, 2:2, 3:3}
for k,v := range m {
fmt.Printf("key =%v value =%v\n",k,v)
}
}
/* 输出结果
index =0 value =1
index =1 value =2
index =2 value =3
key =1 value =1
key =2 value =2
key =3 value =3
*/
- C++代码
#include <iostream>
#include <map>
using namespace std;
int main() {
map<int, int> m = {{1, 1}, {2, 2}, {3, 3}};
for (auto elm : m) {
cout << "key =" << elm.first << " value =" << elm.second << endl;
}
}
/* 输出结果
key =1 value =1
key =2 value =2
key =3 value =3
*/
7.3 if¶
- 相同点:
- Go和C++ if结构大致相同,都有
if、else if、 else
分支 - 支持在条件中嵌入一条表达式(c++17支持)
- 不同点:
- Go中条件不需要放在小括号
()
里面 - Go中左花括号
{
必须和if、else if、 else
分支在同一行,不能单独成行 - Go代码
package main
import "fmt"
func main() {
// 条件不需要()
// {要与if、else if 、else在同一行
// 条件里面可以写一条表达式
if s := 1; s < 0 {
fmt.Println("s<0")
} else if s > 0 && s < 2 {
fmt.Println("0<=s<2")
} else {
fmt.Println("s>=2")
}
}
/* 输出结果
0<=s<2
*/
- C++代码
// -std=c++17
#include <iostream>
using namespace std;
int main() {
if(int i=1;i<0){
cout<<"i<0";
}
else if(i>=0&&i<2)
{
cout<<"0<<i<2";
}
else{
cout<<"i>=2";
}
}
/* 输出结果
0<<i<2
*/
7.4 switch¶
- 相同点:
- 语法形式大致相同,都是通过switch、case、default控制流程
- 不同点:
- Go中switch不用小括号
()
包裹条件 - Go中每一个case语句都是单独的语句块,不需要通过break来跳出
- Go中一个case可以有多个条件,而C++中只能有一个
- Go中switch能对字符串做判断,而C++中只能对枚举、整形或整形组合的表达式做判断
- Go中switch可以想if一样,在case里面填表达式
- Go中使用关键字fallthrough来继续执行case后面分支(类似默认的C++ case行为,继续执行后面的case代码,除非调用break跳出)
- Go代码
package main
import "fmt"
func main() {
var s string= "hi"
// 不用()包裹
switch s {
case "hello":
fmt.Println("hello world")
// 一个case多个条件
case "hi","hi1":
fmt.Println("hi world")
// fallthrough 能够继续执行后面分支,C++中默认支持
fallthrough
default:
fmt.Println("defalut")
}
//switch 后为空,case分支做判断
switch{
case s=="hi":
// 不用break跳出,默认支持
fmt.Println("hi")
default:
fmt.Println("default")
}
}
/* 输出结果
hi world
defalut
hi
*/
- C++代码
#include <iostream>
using namespace std;
int main() {
int i=1;
switch(i+1){
case 1:
cout<<"1"<<endl;
break;
case 2:
cout<<"2"<<endl;
break;
defalut:
cout<<"defalut"<<endl;
}
}
/* 输出结果
2
*/
7.5 goto¶
Go和C++中goto完全一样,一样的语法,一样的限制条件 - Go代码
package main
func f() int {
x := 2
goto L
// 变量声明定义不能被goto跳过
//Y := 3
L:
x++
return x
}
func main() {
}
- C++代码
#include <iostream>
using namespace std;
int f() {
int x = 2;
goto L;
// 变量声明定义不能被goto跳过
// int Y = 3;
L:
x++;
return x;
}
int main() {}
8 函数¶
- 相同点:
- 都有匿名函数,但C++中是lambda函数
- 不同点:
- go函数需要用
func
标识符指示 - go可以返回多个值,C++中可以是要tuple、pair或结构体方式
- go中可以在函数内部通过
defer
关键字定义延后执行语句 - 函数语法
func 函数名(参数列表)(返回值列表){
函数体
}
- Go示例
package main
import "fmt"
func sum(a int,b int) int{
return a+b
}
// 2. 当参数类型一致时,可以简化参数类型的声明
func sum1(a ,b int,c bool) int{
return a+b
}
// 3.1. 可以指定返回值名,这样可以在函数体直接对返回值赋值,能不用显式return一个值
// 3.2. 当返回值指定了名称时需要用()括起来
// 3.3. 当返回值有多个时也需要用()括起来
func sum2(a int,b int) (c int){
c=a+b
return
}
func swap(a int,b int) (int,int){
return b,a
}
func main() {
fmt.Println("sum(1,2) =",sum(1,2))
fmt.Println("sum1(1,2,false) =",sum1(1,2,false))
fmt.Println("sum2(1,2) =",sum2(1,2))
a,b :=swap(1,2)
fmt.Println("swap(1,2) =",a,b)
}
// 输出结果:
// sum(1,2) = 3
// sum1(1,2,false) = 3
// sum2(1,2) = 3
// swap(1,2) = 2 1
8.1 匿名函数¶
- 不同点:
- go中匿名函数直接省略函数名,而C++中是lambda函数
- 函数语法
func (参数)(返回值) {
函数体
}
- Go代码
package main
import (
"fmt"
)
func main() {
func() { fmt.Println("匿名函数") }()
}
// 输出结果:
// 匿名函数
- C++代码
#include <iostream>
using namespace std;
int main() {
[]() { cout << "匿名函数" << endl; }();
}
8.2 Go中defer延后执行语句¶
c++函数中没有延后执行语法
package main
import (
"fmt"
)
func main() {
fmt.Println("1")
// 延后执行顺序机制类似栈,先定义的后执行
defer fmt.Println("2")
defer fmt.Println("3")
fmt.Println("4")
}
// 输出结果:
// 1
// 4
// 3
// 2
9 对象¶
- 不同点:
- Go中有struct关键字,但没有class
- Go中成员没有私有、公有概念;
- Go中包外调用具有公有,私有属性。成员变量、成员函数的首字母大写则允许访问,小写则禁止访问
- Go中没有构造、析构函数
- Go中类成员函数不在类中声明
- Go中继承是直接讲其它结构名声明在结构中
- Go中通过实现接口定义的方法,来达到多态目的
package main
import (
"fmt"
)
type Dog struct{
_name string
}
func (dog *Dog) Name() {
fmt.Printf("我是%s\n",dog._name)
}
func (dog *Dog) eat(){
fmt.Printf("%s 开始吃饭\n",dog._name)
}
func main() {
dog:=Dog{"dog"}
dog.Name()
dog.eat()
}
// 输出结果:
// 我是dog
// dog 开始吃饭
9.1 封装¶
- Go中也能将函数、变量组合在一个struct中,通过首字母大小写控制包外的访问权限
9.2 继承¶
- 语法规则:将被继承的结构体名称写在带继承的结构中
package main
import "fmt"
type human struct {
name string
age int
}
type student struct {
//继承human
human
stuId int64
}
func main() {
var a student
a.name = "boo"
a.age = 13
a.stuId = 2013020100
fmt.Println(a)
}
// 输出结果:
// {{boo 13} 2013020100}
9.3 多态¶
- 多态的实现是借助于interface的功能
10 interface¶
title: interface使用场景
- 实现对象多态特性
- 充当万能类型
10.1 实现对象多态特性¶
- Go中的接口对应了C++中抽象基类,都定义了一些方法,由此实现多态
- Go中不能继承成员,且必须实现全部接口方法,才是实现了接口
package main
import (
"fmt"
)
type Animal interface{
eat()
Name()
}
type Dog struct{
_name string
}
type Cat struct{
_name string
}
func (dog *Dog) Name() {
fmt.Printf("我是%s\n",dog._name)
}
func (dog *Dog) eat(){
fmt.Printf("%s 开始吃饭\n",dog._name)
}
func (cat *Cat) Name(){
fmt.Printf("我是%s\n",cat._name)
}
func (cat *Cat) eat(){
fmt.Printf("%s 开始吃饭\n",cat._name)
}
func main() {
var animal Animal
animal=&Cat{"cat"}
animal.Name()
animal.eat()
animal=&Dog{"dog"}
animal.Name()
animal.eat()
}
// 输出结果:
// 我是cat
// cat 开始吃饭
// 我是dog
// dog 开始吃饭
10.2 万能类型¶
11 异常¶
- 不同点:
- Go:使用
panic
来抛异常,使用recover
捕获异常 - C++:使用
throw
来抛异常,使用try catch
来捕获异常
package main
import (
"fmt"
)
func div(a int, b int) (c int) {
if b==0{
panic("分母不能=0")
}
return a/b
}
func main() {
defer func () {
fmt.Println(recover())
}()
div(1,0)
}
// 输出结果:
//分母不能=0
#include<iostream>
using namespace std;
void fun(bool flag){
if(!flag){
throw "throw a info!";
}
}
int main(){
try{
fun(false);
}
catch(const char* e){
std::cout<<e<<endl;
}
}
// 输出结果:
//throw a info!
12 错误¶
- Go中一般使用错误来替代异常机制,通过函数返回值返回错误信息,由返回的error是否为nil来判断函数是否执行成功
- Go代码
package main
import (
"errors"
"fmt"
)
func div(a int, b int) (c int, errstr error) {
if b == 0 {
return 0, errors.New("分母不能=0")
}
return a / b, nil
}
func main() {
val, err := div(1, 0)
if err != nil {
fmt.Println(err.Error())
}else{
fmt.Println(val)
}
}
// 输出结果:
// 分母不能=0
12.1 实现自己的error类型¶
- error类型是一个接口,实现Error函数就能定义一个自己的error类
type error interface {
Error() string
}
- Go代码
package main
import (
"fmt"
)
func div(a int, b int) (c int, errstr *MyErrors) {
if b == 0 {
return 0, &MyErrors{"分母不能=0",a,b}
}
return a / b, nil
}
type MyErrors struct{
err string
i1 int
i2 int
}
func (mErr *MyErrors) Error() string{
return fmt.Sprintf("(%v,%v)%s\n",mErr.i1,mErr.i2,mErr.err)
}
func main() {
val, err := div(1, 0)
if err != nil {
fmt.Println(err.Error())
}else{
fmt.Println(val)
}
}
// 输出结果:
// (1,0)分母不能=0
13 tag¶
https://zhuanlan.zhihu.com/p/258978922
14 反射¶
title: 什么是反射?
color: 99,178,129
collapse: open
支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。
- 不同点:
- C++中没有反射,但提供了typeid运算符来获取类型,但受限于不同的编译器实现,得到的结果不同
- Go代码
package main
import (
"fmt"
"reflect"
)
type A struct {
}
func main() {
fmt.Println(reflect.TypeOf("str"))
fmt.Println(reflect.TypeOf(1))
fmt.Println(reflect.TypeOf(1.1))
fmt.Println(reflect.TypeOf(false))
fmt.Println(reflect.TypeOf(A{}))
}
// 输出结果:
// string
// int
// float64
// bool
// main.A
- C++代码
#include <iostream>
#include <string>
using namespace std;
struct A{
};
class B{
int i;
};
int main(){
cout<<"类型A:"<<typeid(A).name()<<endl;
cout<<"类型B:"<<typeid(B).name()<<endl;
cout<<"类型int:"<<typeid(int).name()<<endl;
cout<<"类型string:"<<typeid(string).name()<<endl;
return 0;
}
// 在MSVC上编译结果:
// 类型A:struct A
// 类型B:class B
// 类型int:int
// 类型string:class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
// 在gcc上编译结果:
// 类型A:1A
// 类型B:1B
// 类型int:i
// 类型string:NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE
15 并发¶
title: Go和C++并发不同点
- Go并发使用的是协程goroutine,而C++可以使用[[线程]]或进程
- Go协程使用简单,只需<font color=#ED7001>go+函数</font>就可以开启协程
15.1 channel¶
- 使用场景:
- 在go协程中,用于父子协程间数据传输
- 使用方法:
- 创建channel:`make(chan 类型 [,缓存大小])`
- 写channel:`chan变量 <- 数据`
- 读channel:
- `数据变量 <- chan变量`
- for range 遍历:如:`for i := range c`
- Go代码
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
// 会结束,从而在接收第 11 个数据的时候就阻塞了。
for i := range c {
fmt.Println(i)
}
}
// s输出结果:
// 0
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
16 socket¶
- Go代码
package main
import (
"fmt"
"net"
)
func main() {
ip := "127.0.0.1"
port := 8888
address := fmt.Sprintf("%s:%d", ip, port)
netKind := "tcp"
//(net.Conn, error)
conn, err := net.Dial(netKind, address)
if err != nil {
fmt.Println("connect error ,error info:", err)
return
}
fmt.Println("connect success")
//(n int, err error)
_, err = conn.Write([]byte("hello"))
if err != nil {
fmt.Println("Write error ,error info:", err)
return
}
buf := make([]byte, 1024)
//(n int, err error)
len, err := conn.Read(buf)
if err != nil {
fmt.Println("Read error ,error info:", err)
return
}
data := string(buf[0:len])
fmt.Printf("read data from server [%s], data is:%s",conn.RemoteAddr(),data)
}
- 执行结果:
$ go run client.go
connect success
read data from server [127.0.0.1:8888], data is:HELLO
````ad-info
title: 服务端
```go
package main
import (
"fmt"
"net"
"strings"
)
func main() {
// (net.Listener, error)
ip := "127.0.0.1"
port := 8888
address := fmt.Sprintf("%s:%d", ip, port)
netKind := "tcp"
fmt.Println("start listen...")
listener, err := net.Listen(netKind, address)
if err != nil {
fmt.Println("listen error ,error info:", err)
return
}
fmt.Println("start accept...")
// (net.Conn, error)
conn, err := listener.Accept()
if err != nil {
fmt.Println("accept error ,error info:", err)
return
}
fmt.Println("")
buf := make([]byte, 1024)
//(n int, err error)
len, err := conn.Read(buf)
if err != nil {
fmt.Println("Read error ,error info:", err)
return
}
data := string(buf[0:len])
fmt.Printf("read data from client [%s], data is:%s",conn.RemoteAddr(),data)
//(n int, err error)
len, err = conn.Write([]byte(strings.ToUpper(data)))
if err != nil {
fmt.Println("Write error ,error info:", err)
return
}
conn.Close()
listener.Close()
}
```
- 执行结果:
```shell
$ go run server.go
start listen...
start accept...
read data from client [127.0.0.1:4641], data is:hello
```