4_14_GLib库入门与实践_数据集

简介

(本段内容翻译自官方文档)
数据集将数据元素组与特定的内存位置相关联。如果您需要将数据与从外部库返回的结构相关联,这些非常有用。由于您无法修改结构,因此您可以将其在内存中的位置用作数据集的键,您可以在其中将任意数量的数据元素与其关联。
大多数数据集函数有两种形式。第一种形式使用字符串来标识与位置关联的数据元素。第二种形式使用 GQuark 标识符,这些标识符是通过调用g_quark_from_string()或g_quark_from_static_string()创建的。第二种形式更快,因为它不需要在 GQuark 标识符的哈希表中查找字符串。

  • 没有创建数据集的功能。只要您向其中添加元素,它就会自动创建。
  • 要将数据元素添加到数据集,请使用 g_dataset_id_set_data()、g_dataset_id_set_data_full()、g_dataset_set_data() 和 g_dataset_set_data_full()。
  • 要从数据集中获取数据元素,请使用 g_dataset_id_get_data()和 g_dataset_get_data()。
  • 要遍历数据集中的所有数据元素,请使用 g_dataset_foreach()非线程安全)。
  • 要从数据集中删除数据元素,请使用 g_dataset_id_remove_data()和g_dataset_remove_data()。
  • 要销毁数据集,请使用 g_dataset_destroy()。

数据结构

数据集无固定的数据结构,任意指针都可被视作一个数据集。

函数列表

#define 	g_dataset_id_set_data()
void 	g_dataset_id_set_data_full ()
gpointer 	g_dataset_id_get_data ()
#define 	g_dataset_id_remove_data()
gpointer 	g_dataset_id_remove_no_notify ()
#define 	g_dataset_set_data()
#define 	g_dataset_set_data_full()
#define 	g_dataset_get_data()
#define 	g_dataset_remove_data()
#define 	g_dataset_remove_no_notify()
void 	g_dataset_foreach ()
void 	g_dataset_destroy ()

函数功能分类

dataset和datalist非常相似,其功能函数也可以分为分为两大类。

  • g_dataset_id_xxx:将一个GQuark与一个任意元素数据相关联
  • g_dataset_xxx:将一个字符串与一个任意元素数据相关联

插入元素

g_dataset_id_set_data()
g_dataset_id_set_data_full ()
g_dataset_set_data()
g_dataset_set_data_full()

移除元素

g_dataset_id_remove_data()
g_dataset_id_remove_no_notify ()
g_dataset_remove_data()
g_dataset_remove_no_notify()

遍历

g_dataset_foreach

销毁

g_dataset_destroy

函数功能说明及综合演示

创建

dataset没有创建函数,可以定义一个任意类型的变量作为dataset。
当添加元素时,dataset会被自动创建。

插入元素

set_data和set_data_full均可插入元素
g_dataset_id_set_xxx用GQuark作为索引
g_dataset_id_set_xxx用字符串作为索引
带full的函数,包含一个数据自定义释放函数变量,可以释放自定义的函数
如果插入的key已经存在,且存在元素自定义释放函数,该key对应的元素会被释放函数释放,新的元素将被插入。这一点与datalist特性相同。

g_dataset_id_set_data()
g_dataset_id_set_data_full ()
g_dataset_set_data()
g_dataset_set_data_full()
获取元素

g_dataset_id_get_data通过GQuark获取其对应的值。
g_dataset_get_data通过字符串获取其对应的值。

gpointer g_dataset_id_get_data ()
#define g_dataset_get_data(l, k)

下面程序,将一个字符串与一个结构体相关联,组成数据集,展示了使用g_dataset_id_set_data_full函数向字符集插入元素及替换原有元素,使用g_dataset_id_get_data获取数据集中的元素。
示例代码如下:
源码见glib_examples\glib_dataset\glib_dataset_set_get_data

#include <glib.h>

typedef struct my_data_tag {
    gint id;
    gchar *name;
}my_data_t;

static void _dataset_foreach_func(GQuark key_id, gpointer data, gpointer user_data)
{
    my_data_t *p = (my_data_t *)data;

    g_print("[foreach] key:%s, id: %d, name: %s, user_data: %s \n",  \
        g_quark_to_string(key_id), p->id, p->name, (gchar *)user_data);
}

static void _dataset_id_destroy_func(gpointer data)
{
    my_data_t *p = (my_data_t *)data;
    if(NULL == p) {
        return;
    }

    g_print("destroy %s! \n", p->name);

    g_free(p->name);
    g_free(p);
}

static void glib_dataset_set_get_test (void)
{
    gpointer location = (gpointer)glib_dataset_set_get_test;

    my_data_t *data1 = NULL;
    my_data_t *data2 = NULL;
    my_data_t *data3 = NULL;
    my_data_t *data4 = NULL;
    my_data_t *ret = NULL;

    data1 = g_new0(my_data_t, 1);
    data1->id = 101;
    data1->name = g_strdup_printf("name:%d", data1->id);
    data2 = g_new0(my_data_t, 1);
    data2->id = 102;
    data2->name = g_strdup_printf("name:%d", data2->id);
    data3 = g_new0(my_data_t, 1);
    data3->id = 103;
    data3->name = g_strdup_printf("name:%d", data3->id);
    data4 = g_new0(my_data_t, 1);
    data4->id = 104;
    data4->name = g_strdup_printf("name:%d", data4->id);

    g_print("data2->id(%p): %d \n", &data2->id, data2->id);
    g_print("data2->name(%p): %s \n", data2->name, data2->name);

    g_dataset_id_set_data_full(location, g_quark_from_string ("one"), data1, _dataset_id_destroy_func);
    g_dataset_id_set_data_full(location, g_quark_from_string ("two"), data2, _dataset_id_destroy_func);
    g_dataset_id_set_data_full(location, g_quark_from_string ("three"), data3, _dataset_id_destroy_func);

    g_dataset_foreach(location, _dataset_foreach_func, (gpointer)"foreach");
    
    g_print("before set_data_full: two \n");
    g_dataset_id_set_data_full(location, g_quark_from_string ("two"), data4, _dataset_id_destroy_func);
    g_print("after set_data_full: two \n");

    ret = g_dataset_id_get_data (location, g_quark_from_string ("two"));

    g_print("ret->id(%p): %d \n", &ret->id, ret->id);
    g_print("ret->name(%p): %s \n", ret->name, ret->name);

    g_dataset_destroy (location);
}

gint main (gint argc, gchar** argv)
{
    glib_dataset_set_get_test();

    return 0;
}

运行结果:

[root@centos7_6 build]# ./glib_dataset_set_get_data
data2->id(0x1e4ee70): 102
data2->name(0x1e4ee90): name:102
[foreach] key:one, id: 101, name: name:101, user_data: foreach
[foreach] key:two, id: 102, name: name:102, user_data: foreach
[foreach] key:three, id: 103, name: name:103, user_data: foreach
before set_data_full: two
destroy name:102!
after set_data_full: two
ret->id(0x1e4eef0): 104
ret->name(0x1e4ef10): name:104
destroy name:101!
destroy name:104!
destroy name:103!
移除元素

no_notify意思是删除元素不自动释放,返回值为已从dataset删除的元素的指针地址,调用者需自行释放该内存。

g_dataset_id_remove_data()
g_dataset_id_remove_no_notify ()
g_dataset_remove_data()
g_dataset_remove_no_notify()
遍历

在进行数据集元素遍历时,需要提供一个遍历函数。
注意:本函数并非线程安全。

g_dataset_foreach ()
销毁

调用g_dataset_destroy函数时,若存在元素释放函数,则释放函数会被自动调用,若不存在元素释放函数且元素不需要被释放,g_dataset_destroy可以不调用。
当不存在元素释放函数且元素不需要被释放时,使用valgrind工具运行对比下面两个程序,可以看到无论是否调用g_dataset_destroy函数,都不会造成内存泄露。

g_dataset_destroy ()

调用释放函数
示例代码如下:
源码见glib_examples\glib_dataset\glib_dataset_with_destroy

#include <glib.h>

typedef struct my_data_tag {
    gint id;
    gchar *name;
}my_data_t;

static void _dataset_foreach(GQuark key_id, gpointer data, gpointer user_data)
{
    g_print("key:%s data: %s \n", g_quark_to_string(key_id), (gchar *)data);
}

static void glib_dataset_test (void)
{
    gpointer location = (gpointer)glib_dataset_test;
    gpointer data1 = "test1";
    gpointer data2 = "test2";
    gpointer ret;
    
    g_dataset_set_data (location, "key_test1", data1);
    g_dataset_set_data (location, "key_test2", data2);

    g_dataset_foreach(location, _dataset_foreach, location);
    g_print("Call destroy function \n");
    g_dataset_destroy(location);
}

gint main (gint argc, gchar** argv)
{
    glib_dataset_test();

    return 0;
}

运行结果:

[root@centos7_6 build]# ./glib_dataset_with_destroy
key:key_test1 data: test1
key:key_test2 data: test2
Call destroy function

内存泄露检查:

[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_dataset_with_destroy
==9277== Memcheck, a memory error detector
==9277== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9277== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==9277== Command: ./glib_dataset_with_destroy
==9277==
key:key_test1 data: test1
key:key_test2 data: test2
Call destroy function
==9277==
==9277== HEAP SUMMARY:
==9277==     in use at exit: 23,062 bytes in 16 blocks
==9277==   total heap usage: 40 allocs, 24 frees, 123,164 bytes allocated
==9277==
==9277== LEAK SUMMARY:
==9277==    definitely lost: 0 bytes in 0 blocks
==9277==    indirectly lost: 0 bytes in 0 blocks
==9277==      possibly lost: 0 bytes in 0 blocks
==9277==    still reachable: 23,062 bytes in 16 blocks
==9277==         suppressed: 0 bytes in 0 blocks
==9277== Reachable blocks (those to which a pointer was found) are not shown.
==9277== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==9277==
==9277== For counts of detected and suppressed errors, rerun with: -v
==9277== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

不调用释放函数
示例代码如下:
源码见glib_examples\glib_dataset\glib_dataset_without_destroy

#include <glib.h>

typedef struct my_data_tag {
    gint id;
    gchar *name;
}my_data_t;

static void _dataset_foreach(GQuark key_id, gpointer data, gpointer user_data)
{
    g_print("key:%s data: %s \n", g_quark_to_string(key_id), (gchar *)data);
}

static void glib_dataset_test (void)
{
    gpointer location = (gpointer)glib_dataset_test;
    gpointer data1 = "test1";
    gpointer data2 = "test2";
    gpointer ret;
    
    g_dataset_set_data (location, "key_test1", data1);
    g_dataset_set_data (location, "key_test2", data2);

    g_dataset_foreach(location, _dataset_foreach, location);
    g_print("Not call destroy function \n");
    //g_print("Call destroy function \n");
    //g_dataset_destroy(location);
}

gint main (gint argc, gchar** argv)
{
    glib_dataset_test();

    return 0;
}

运行结果:

[root@centos7_6 build]# ./glib_dataset_without_destroy
key:key_test1 data: test1
key:key_test2 data: test2
Not call destroy function

内存泄露检查:

[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_dataset_without_destroy
==8979== Memcheck, a memory error detector
==8979== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8979== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8979== Command: ./glib_dataset_without_destroy
==8979==
key:key_test1 data: test1
key:key_test2 data: test2
Not call destroy function
==8979==
==8979== HEAP SUMMARY:
==8979==     in use at exit: 23,134 bytes in 18 blocks
==8979==   total heap usage: 40 allocs, 22 frees, 123,172 bytes allocated
==8979==
==8979== LEAK SUMMARY:
==8979==    definitely lost: 0 bytes in 0 blocks
==8979==    indirectly lost: 0 bytes in 0 blocks
==8979==      possibly lost: 0 bytes in 0 blocks
==8979==    still reachable: 23,134 bytes in 18 blocks
==8979==         suppressed: 0 bytes in 0 blocks
==8979== Reachable blocks (those to which a pointer was found) are not shown.
==8979== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==8979==
==8979== For counts of detected and suppressed errors, rerun with: -v
==8979== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

上述对比运行结果可见,无论是否调用释放函数,均无内存泄露发生,因为整个过程数据集元素都是常量字符串,存储在内存的静态存储区,无需手动释放内存。而对于自定义的数据结构,由于有在堆上进行内存申请,必须调用释放函数,否则会造成内存泄露。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值