跳转至

GO和C++的语法对比

1 基本常识

1.1 程序入口main

  • GO
    • 包名为main
    • 入口函数为main
  • C++
    • 入口函数为main
  • C++代码
//C++代码
int main(){

}
  • GO代码
//GO代码
package main

func main()  {

}

1.2 一般规则

  1. Go中声明变量时类型在变量名后面
  2. 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中并没有枚举
  • 但可以借助constiota来模仿实现个类似功能,而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代码
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
```