c++ new一个结构体_Go 中的结构体和 JSON 序列化

前面我们或多或少的都使用了结构体这种数据结构,本身结构体也有很多特性,我们一一来看。

结构体的作用是将一个或者多个任一类型的变量组合在一起的数据类型,类似于我们在 Java 中 class 的作用。在结构体重也可以嵌套结构体。结构体还可以有自己的方法。

1.定义结构体

我们先定义一个结构体:

结构体定义如下:

type 标识符 struct {
  field1 type
 field2 type
}

例子:

type Staff struct {
 UserId int16
 UserName string
 Sex byte
 Age int8
}

2. 使用结构体

有三种方式可以使用结构体:

var staff Staff
staff1 := new(Staff)
staff2 := &Staff{}

上面 2 和 3 的效果是一样的,返回的都是指向结构体的指针。

Go 中的结构体不像 class 一样有构造函数,一般会使用上述第三种方式来构造出一个结构体对象,简称 Go 中的工厂模式:

package main

import "fmt"

type Staff struct {
 UserId int16
 UserName string
 Sex byte
 Age int8
}

func NewStaff(userId int16, userName string, sex byte, age int8) *Staff {
 return &Staff{
  UserId:userId,
  UserName:userName,
  Sex:sex,
  Age:age,
 }
}

func main() {
 staff := NewStaff(123,"xiaoming",byte(1),13)
 fmt.Println(staff)
}

3. 带标签的结构体

结构体中的字段除了有名字和类型外,还可以有一个可选的标签(tag):它是一个附属于字段的字符串,可以是文档或其他的重要标记。 标签的内容不可以在一般的编程中使用,只有通过反射机制才能能获取它

我们现在有这样一段程序:从 json 文件中读取 json 字符串,然后转为 json 对象:

json 文件内容:

{
    "port": "7788",
    "address": "47.95.34.2"
}

代码如下:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
)

type MainConfig struct {
    port                string
    address             string

}


func LoadConfig(path string) *MainConfig {
    buf, err := ioutil.ReadFile(path)
    if err != nil {
        log.Panicln("load config conf failed: ", err)
    }
    mainConfig := &MainConfig{}
    err = json.Unmarshal(buf, mainConfig)
    if err != nil {
        log.Panicln("decode config file failed:", string(buf), err)
    }
    fmt.Println(mainConfig.address,mainConfig.port)

    return mainConfig
}

func main() {
    LoadConfig("c:/test.json")

}

执行以上程序可以发现打印出来的结果是空的,原因是:Go 开发规范认为:只有开头是大写字母的对象,方法才被认为是公开的,可以在包外访问,否则就是私有的,外部对象无法访问。

那我们定义结构体大写之后,但是想让结构体中的字段 json 格式化为小写应该怎么做呢?这时候就可以通过 tag 来指定:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
)

type MainConfig struct {
    Port string `json:"port"`
    Address string `json:"address"`
}

//反射获取字段中的tag
func reflectTag(mg MainConfig)  {
    mgType := reflect.TypeOf(mg)
    tag0 := mgType.Field(0).Tag
    tag1 := mgType.Field(0).Tag
    fmt.Println(tag0,tag1)
}

func LoadConfig(path string) *MainConfig {
    buf, err := ioutil.ReadFile(path)
    if err != nil {
        log.Panicln("load config conf failed: ", err)
    }
    mainConfig := &MainConfig{}
    err = json.Unmarshal(buf, mainConfig)
    if err != nil {
        log.Panicln("decode config file failed:", string(buf), err)
    }
    bytes, err := json.Marshal(mainConfig)
    fmt.Println(string(bytes))
    return mainConfig
}

func main() {
    LoadConfig("c:/test.json")
    config := MainConfig{"1234", "123.221.134"}
    reflectTag(config)

}
打印结果:
{"port":"7788","address":"47.95.34.2"}
json:"port" json:"port"

上面的代码是一段从 json 文件中读取配置文件的例子,也使用了反射机制来获取 tag 标签,使用 json.Unmarshal来反序列化,使用json.Marshal将对象序列化为 json 字符串。

4.方法

其实 Go 中任何自定义的类型都可以有方法,不仅仅是 struct 才有。除了指针和 interface。

看一个自定义类型带方法的例子:

package main

import "fmt"

type MainConfig1 struct {
    Port string `json:"port"`
    Address string `json:"address"`
}

type Str string

func (s Str) Compact(str string,str1 string) string  {
    return str + str1
}

func (s *Str) Compact1(str string,str1 string) Str {
    return Str(str + str1)
}

//mf相当于其他语言中的this,self表示当前对象本身
func (mf MainConfig1) Compact2() Str {
    return Str(mf.Port + "|" + mf.Address)
}


func main() {
    var s Str
    compact1 := s.Compact("a", "b")
    str := s.Compact1("c", "d")
    fmt.Println(compact1,str)

    var mf MainConfig1
    mf.Port = "2"
    mf.Address = "3333"
    mf.Compact2()

}

声明 Str类型的 s,通过 s 可以调用这两个方法。另外还记得 Go 的访问控制规范吧,首字母大写表示公共方法,小写表示私有,自定义类型的方法同样遵循这个原则,试着把 Compact1方法的首字母改为小写,你会发现通过 s.Compact1("c", "d")找不到方法。

再看Compact2()方法的使用,使用当前接收者本身来获取属性,跟 Java 中的this关键字相似。

上面还有一个疑问点:

使用 Str 和使用 *Str作为接受者有什么不同呢?

区别就在于:在接收者是指针时,方法可以改变接收者的值(或状态)

我们来改造一下Compact2()方法:

func (mf MainConfig1) Compact2() Str {
    mf.Address = "rrrr"
    return Str(mf.Port + "|" + mf.Address)
}

var mf MainConfig1
mf.Port = "2"
mf.Address = "3333"
compact2 := mf.Compact2()
fmt.Println(compact2)
bytes, _ := json.Marshal(mf)
fmt.Println(string(bytes))

输出的结果是:

2|rrrr
{"port":"2","address":"3333"}

函数作用域内的 Address 值被改变了,但是实际上mf对象的属性值并没有被改变。

那么我们将 receiver 改为指针试一下:

func (mf *MainConfig1) Compact2() Str {
    mf.Address = "rrrr"
    return Str(mf.Port + "|" + mf.Address)
}

var mf MainConfig1
mf.Port = "2"
mf.Address = "3333"
compact2 := mf.Compact2()
fmt.Println(compact2)
bytes, _ := json.Marshal(mf)
fmt.Println(string(bytes))

输出的结果是:

2|rrrr
{"port":"2","address":"rrrr"}

会发现这次mf的属性值也会改变。因为本身我们使用的this对象就是 *MainConfig1自然可以改变对象内部的值。

5. 函数和方法的区别

函数将变量作为参数:Function1(recv)

方法在变量上被调用:recv.Method1()

在接收者是指针时,方法可以改变接收者的值(或状态)

receiver_type 叫做 (接收者)基本类型,这个类型必须在和方法同样的包中被声明。

在 Go 中,(接收者)类型关联的方法不写在类型结构里面,就像类那样;耦合更加宽松;类型和方法之间的关联由接收者来建立。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
结构体序列化和反序列化是将结构体数据转换为字节流,以便在网络传输或存储使用,然后再将字节流还原为原始结构体数据的过程。 在C++,可以通过以下几种方式实现结构体序列化和反序列化: 1. 使用二进制流进行序列化和反序列化:可以使用std::ofstream和std::ifstream类来将结构体数据写入文件或从文件读取结构体数据。例如: ```cpp struct MyStruct { int a; float b; char c; }; void serialize(const MyStruct& data, const std::string& filename) { std::ofstream file(filename, std::ios::binary); file.write(reinterpret_cast<const char*>(&data), sizeof(MyStruct)); file.close(); } void deserialize(MyStruct& data, const std::string& filename) { std::ifstream file(filename, std::ios::binary); file.read(reinterpret_cast<char*>(&data), sizeof(MyStruct)); file.close(); } ``` 2. 使用JSON进行序列化和反序列化:可以使用第三方库(如RapidJSON、nlohmann/json等)将结构体数据转换为JSON格式的字符串,然后再将JSON字符串转换回结构体数据。例如: ```cpp #include <iostream> #include <string> #include <nlohmann/json.hpp> struct MyStruct { int a; float b; char c; }; void serialize(const MyStruct& data, const std::string& filename) { nlohmann::json json_data; json_data["a"] = data.a; json_data["b"] = data.b; json_data["c"] = data.c; std::ofstream file(filename); file << json_data.dump(4); // 4为缩进级别 file.close(); } void deserialize(MyStruct& data, const std::string& filename) { std::ifstream file(filename); nlohmann::json json_data; file >> json_data; file.close(); data.a = json_data["a"]; data.b = json_data["b"]; data.c = json_data["c"]; } int main() { MyStruct original_data {42, 3.14f, 'A'}; serialize(original_data, "data.json"); MyStruct restored_data; deserialize(restored_data, "data.json"); std::cout << "Restored data: a = " << restored_data.a << ", b = " << restored_data.b << ", c = " << restored_data.c << std::endl; return 0; } ``` 以上是两种常见的结构体序列化和反序列化的方式,具体选择哪种方式取决于你的需求和场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值