第六节 指针、Map、结构体

本文详细介绍了Go语言中的指针、Map和结构体的使用。指针不能进行偏移和运算,可以使用&和*操作。Map是无序的key-value数据结构,需要初始化后使用。结构体提供了自定义类型的功能,可以定义匿名字段和嵌套结构体。new和make函数分别用于值类型和引用类型的内存分配,make专门用于初始化slice、map和channel。文章还探讨了方法和接收者,包括指针和值类型的接收者,并讨论了如何实现结构体的序列化和JSON转换。

指针

区别于C/C++中的指针,Go语言中的指针不能进行偏移和运算,是安全指针。
Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。

指针地址和指针类型

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int、*int64、*string等。

取变量指针:
    ptr := &v    // v的类型为T
    v:代表被取地址的变量,类型为T
    ptr:用于接收地址的变量,ptr的类型就为*T,
    称做T的指针类型。*代表指针。

注:
1.对变量进行取地址(&)操作,可以获得这个变量的指针变量。
2.指针变量的值是指针地址。
3.对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。

空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil

new和make

在Go语言中对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间,否则我们的值就没办法存储。
Go语言中new和make是内建的两个函数,主要用来分配内存
注:
值类型的变量:
存储当前类型的存储的数据。值类型包括:数值类型、bool、string、数组、struct结构体
引用类型的变量:
存储地址,该地址对应的空间才是真正存储的数据。引用类型包括:指针、slice切片、管道chan、map、以及interface等类型

new

new是一个内置的函数,它的函数签名如下:
    func new(Type) *Type
    1.Type表示类型,new函数只接受一个参数,这个参数是一个类型
    2.*Type表示类型指针,new函数返回一个指向该类型内存地址的指针

new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值

make

make也是用于内存分配的,区别于new,它只用于slice、map以及chan的内存创建,而且它返回的类型就是这三个类型本身。
这三种类型就是引用类型,所以就没有必要返回他们的指针了。

make函数的函数签名如下:
func make(t Type, size ...IntegerType) Type

make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作

new与make的区别

1.二者都是用来做内存分配的
2.make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身
3.而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针

Map

map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用

map定义

Go语言中 map的定义语法如下:
    map[KeyType]ValueType
注:
KeyType:表示键的类型
ValueType:表示键对应的值的类型
map类型的变量默认初始值为nil,需要使用make()函数来分配内存
语法为:
make(map[KeyType]ValueType, cap)
cap表示map的容量,该参数虽然不是必须的,
但是我们应该在初始化map的时候就为其指定一个合适的容量

map的基本使用

map支持在声明的时候填充元素

func main() {
    userInfo := map[string]string{
        "username": "pprof.cn",
        "password": "123456",
    }
    fmt.Println(userInfo)
}

判断某个键是否存在

Go语言中有个判断map中键是否存在的特殊写法,格式如下:

    value, ok := map[key]

map的遍历

for range遍历map:

func main() {
    scoreMap := make(map[string]int)
    scoreMap["张三"] = 90
    scoreMap["小明"] = 100
    scoreMap["王五"] = 60
    for k, v := range scoreMap {
        fmt.Println(k, v)
    }
}

只遍历key:

func main() {
    scoreMap := make(map[string]int)
    scoreMap["张三"] = 90
    scoreMap["小明"] = 100
    scoreMap["王五"] = 60
    for k := range scoreMap {
        fmt.Println(k)
    }
}

遍历map时的元素顺序与添加键值对的顺序无关

使用delete()函数删除键值对

使用delete()内建函数从map中删除一组键值对
delete()函数的格式如下:

    delete(map, key)

新增提示

rand.Seed(time.Now().UnixNano()) //初始化随机数种子
rand.Intn(100)          //生成0~99的随机整数

按照指定顺序遍历map

 func main() {
    rand.Seed(time.Now().UnixNano()) //初始化随机数种子

    var scoreMap = make(map[string]int, 200)

    for i := 0; i < 100; i++ {
        key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串
        value := rand.Intn(100)          //生成0~99的随机整数
        scoreMap[key] = value
    }
    //取出map中的所有key存入切片keys
    var keys = make([]stri
<think>首先,用户的问题是:“我想查询C++中map容器第三个参数的作用及用法 请问C++ map constructor third parameter comparator explanation”。翻译成英文是:“I want to inquire about the role and usage of the third parameter of the map container in C++. Please explain the comparator in the C++ map constructor.” 关键点: - C++ 中的 `std::map` 容器。 - 构造函数的第三个参数:比较器(comparator)。 在 C++ 中,`std::map` 是一个关联容器,存储键值对,按键排序。标准形式是: ```cpp template < class Key, class T, class Compare = std::less<Key>, class Allocator = std::allocator<std::pair<const Key, T>> > class map; ``` - 第三个模板参数是 `Compare`,默认是 `std::less<Key>`。 - 在构造函数中,你可以传递一个比较器对象作为第三个参数。 比较器的作用: - 它定义了键的排序顺序。 - 默认是升序(使用 `std::less<Key>`),但你可以自定义比较函数或函数对象来改变顺序,例如降序或基于自定义逻辑。 用法: - 比较器必须是一个可调用对象,接受两个键类型的参数,并返回一个布尔值,表示第一个参数是否应该在第二个参数之前。 - 它可以是函数指针、函数对象(functor)或 lambda 表达式。 示例: 1. 使用默认比较器(升序): ```cpp std::map<int, std::string> myMap; // 默认使用 std::less<int> ``` 2. 使用自定义比较器实现降序: ```cpp struct CompareDesc { bool operator()(int a, int b) const { return a > b; // 降序 } }; std::map<int, std::string, CompareDesc> myMap; ``` 3. 使用 lambda 表达式(在 C++11 及以后): ```cpp auto comp = [](int a, int b) { return a > b; }; std::map<int, std::string, decltype(comp)> myMap(comp); ``` 在构造函数中,第三个参数是用于比较器的实例。例如: ```cpp std::map<Key, Value, Compare> myMap(Compare()); ``` 但通常,我们直接在模板参数中指定比较器类型,并在需要时传递比较器对象。 现在,根据系统级指令: - 所有行内数学表达式用 `$...$`,但这里没有数学,所以可能不需要。 - 独立公式用 `$$...$$`,但这里主要是代码。 - LaTeX 语法正确:在代码块中,使用 Markdown 代码块。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾添加引用标识:但用户没有提供引用,所以可能不需要添加。参考站内引用说“上述引用仅供参考”,但这里没有引用。我应该确保回答真实可靠。 回答结构: - 清晰解释第三个参数的作用。 - 提供用法示例。 - 用中文。 - 最后生成相关问题。 生成相关问题: - 基于主题,如比较器的其他用法、性能影响、常见错误等。 最终回答结构: 1. 解释第三个参数的作用。 2. 描述用法和示例。 3. 代码示例。 4. 相关问题。</think>### C++ map 容器第三个参数(比较器)的作用及用法 在 C++ 中,`std::map` 是一个关联容器,用于存储键值对(key-value pairs)。其构造函数支持多个参数,其中**第三个参数是用于定义键(key)排序规则的比较器(comparator)**。默认情况下,`std::map` 使用 `std::less<Key>` 实现升序排序,但通过自定义比较器,您可以控制键的排序逻辑(如降序或自定义规则)。 #### 作用 1. **定义排序顺序**:比较器决定了键在容器中的排列方式(如升序、降序或基于自定义逻辑)。 2. **确保唯一性**:`std::map` 要求键唯一,比较器用于判断键是否相等(如果 `comp(a, b) == false` 且 `comp(b, a) == false`,则视为 `a == b`)。 3. **支持自定义类型**:当键是自定义类或结构体时,比较器可指定排序依据(如按成员变量排序)。 #### 用法 比较器必须是一个**可调用对象**(callable object),接受两个键类型的参数,并返回 `bool` 值: - 如果返回 `true`,表示第一个参数应排在第二个参数之前。 - 如果返回 `false`,表示第一个参数不应排在第二个参数之前(可能相等或靠后)。 比较器可以是: - **函数对象(Functor)**:定义一个重载 `operator()` 的类。 - **函数指针**:指向普通函数的指针。 - **Lambda 表达式**(C++11 及以上)。 ##### 基本语法 ```cpp std::map<KeyType, ValueType, ComparatorType> mapName(ComparatorInstance); ``` - `ComparatorType`:比较器的类型(如函数对象类名)。 - `ComparatorInstance`:比较器的实例(可省略,如果比较器有默认构造函数)。 #### 示例代码 以下示例演示如何自定义比较器实现降序排序和自定义排序: ```cpp #include <iostream> #include <map> #include <string> // 示例 1:使用函数对象实现降序排序 struct DescendingComparator { bool operator()(int a, int b) const { return a > b; // 降序:a > b 时返回 true,表示 a 应排在 b 前 } }; // 示例 2:函数指针实现自定义排序(按字符串长度排序) bool LengthComparator(const std::string& a, const std::string& b) { return a.length() < b.length(); // 按字符串长度升序 } int main() { // 示例 1:降序排序(使用函数对象) std::map<int, std::string, DescendingComparator> map1; map1.insert({3, "Apple"}); map1.insert({1, "Banana"}); map1.insert({2, "Cherry"}); // 输出:3:Apple, 2:Cherry, 1:Banana(降序) // 示例 2:按字符串长度排序(使用函数指针) std::map<std::string, int, decltype(&LengthComparator)> map2(LengthComparator); map2.insert({"Apple", 5}); map2.insert({"Fig", 3}); map2.insert({"Banana", 6}); // 输出:Fig:3, Apple:5, Banana:6(长度升序:3<5<6) // 示例 3:Lambda 表达式实现自定义排序(C++11 及以上) auto lambdaComp = [](int a, int b) { return a % 3 < b % 3; }; // 按模 3 的值排序 std::map<int, std::string, decltype(lambdaComp)> map3(lambdaComp); map3.insert({5, "Five"}); map3.insert({2, "Two"}); map3.insert({8, "Eight"}); // 输出:2:Two(模 3=2), 5:Five(模 3=2), 8:Eight(模 3=2)— 注意:键需唯一,此处仅为演示逻辑 } ``` #### 注意事项 1. **性能影响**:比较器的复杂度影响 `std::map` 的操作效率(如插入、查找为 $O(\log n)$)。 2. **严格弱序**:比较器必须满足[严格弱序(Strict Weak Ordering)](https://en.cppreference.com/w/cpp/named_req/Compare)[^1]: - 不可反身:`comp(a, a) == false`。 - 可传递:若 `comp(a, b) == true` 且 `comp(b, c) == true`,则 `comp(a, c) == true`。 - 反对称:若 `comp(a, b) == true`,则 `comp(b, a) == false`。 3. **默认行为**:如果省略第三个参数,`std::map` 使用 `std::less<Key>`(升序)。 通过自定义比较器,您可以灵活控制 `std::map` 的行为,适应各种排序需求[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值