温故而知新

也许很多人将孔子所说的“温故而知新”理解为:温习学过的知识,从中获得新的领悟。但是,我更倾向于另一种解释:温习学过的知识,继续学习新的知识。本文借助一个更加平易和现实的示例用以温习文档 [1-3] 中的 GObject 知识,并进一步学习类与对象的方法。

Bibtex 文献数据格式

使用 TeX 写论文,免不了要用 Bibtex 处理参考文献,像 google scholar 这样的网站,直接提供 bibtex 格式的文献数据,比如 Knuth 所写的《The TeXbook》这本书,其 Bibtex 文献数据格式如下:

?
1
2
3
4
5
6
@book {texbook,
     title={{The {\TeX}book}},
     author={Knuth, D. E.},
     year={1984},
     publisher={Addison-Wesley Professional}
}

有了 Bibtex 文献数据,在 TeX 源文档中便可以进行文献引用了,例如:

?
1
Knuth 写的《The { \TeX }book》 \cite [texbook]这本书非常有趣!

TeX 源文档的排版结果大致像下面这个样子:

本文不关心 TeX 是如何处理参考文献的,只关注 Bibtex 文献数据格式。

Bibtex 文献数据格式非常简单,可描述如下:

?
1
2
3
4
5
6
@文献类型 {引用名
     标识1 = {字符串1},
     标识2 = {字符串2},
     ...   = { ... },
     标示 n = {字符串 n}
}

Bibtex 文献类的设计,代号Kill Bill

这个才是本文的主题。我们将此次行动(或者称为项目也可)命名为“Kill Bill”,缩写“Kb”,其中并无深意,只是因为我正在看这部电影。

1. 类的声明

建立 kb-bibtex.h 头文件,以 GObject 类作为基类,声明 KbBibtex 类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef KB_BIBTEX_H
#define KB_BIBTEX_H
 
#include <glib-object.h>
 
#define KB_TYPE_BIBTEX (kb_bibtex_get_type ())
 
#define KB_BIBTEX(object) \
         G_TYPE_CHECK_INSTANCE_CAST ((object), KB_TYPE_BIBTEX, KbBibtex)
 
typedef struct _KbBibtex KbBibtex;
struct _KbBibtex {
     GObject parent;
};
 
typedef struct _KbBibtexClass KbBibtexClass;
struct _KbBibtexClass {
     GObjectClass parent_class;
};
 
GType kb_bibtex_get_type ( void );
 
#endif

2. 类型注册

建立 kb-bibtex.c 源文件,对 KbBibtex 类进行定义,首先向 GObject 库的类型系统注册 KbBibtex 类型,使之成为 GObject 库认可的一类公民:

?
1
2
3
#include "kb-bibtex.h"
 
G_DEFINE_TYPE (KbBibtex, kb_bibtex, G_TYPE_OBJECT);

3. 对象的私有属性

在此,必须要澄清两个概念,它们在文档 [1-3] 中的描述有些混淆:

  • 首先是对象属性,它指代隶属于实例结构体的数据,文档 [2] 主要讲述的是对象属性的隐藏。
  • 其次是类属性,它指代隶属于类结构体的数据,可被所有的对象所共享,在文档 [3] 中我们利用了这一点,实现了通过类属性来访问对象属性。

下面定义 KbBibtex 对象的私有属性:

?
1
2
3
4
5
6
7
8
9
10
#define KB_BIBTEX_GET_PRIVATE(object) (\
         G_TYPE_INSTANCE_GET_PRIVATE ((object), KB_TYPE_BIBTEX, KbBibtexPrivate))
 
typedef struct _KbBibtexPrivate KbBibtexPrivate;
struct _KbBibtexPrivate {
         GString *title;
         GString *author;
         GString *publisher;
         guint   year;
};

4. 类结构体与实例结构体的构造函数(初始化函数)

在 KbBibtex 类结构体构造函数中安装对象的私有属性:

?
1
2
3
4
5
6
static void
kb_bibtex_class_init (KbBibtexClass *klass)
{
         g_type_class_add_private (klass,
                                   sizeof (KbBibtexPrivate));
}

kb_bibtex_class_init 参数名之所以使用 klass 而不是 class,是因为 class 是 c++ 语言的关键字,如果使用 class,那么 KbBibtex 类如是被 C++ 程序调用,那么程序编译时就杯具了。

KbBibtex 实例结构体的构造函数则什么也不做:

?
1
2
3
4
static void
kb_bibtex_init (KbBibtex *self)
{
}

因为我们打算采用文档 [3] 中的方式,通过类属性来访问对象属性。

5. 对象私有属性的外部访问

首先要实现类属性与对象属性之间的映射,即 kb_bibtex_set_property 与 kb_bibtex_get_property 函数:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
static void
kb_bibtex_set_property (GObject *object, guint property_id,
                         const GValue *value, GParamSpec *pspec)
{      
         KbBibtex *self = KB_BIBTEX (object);
         KbBibtexPrivate *priv = KB_BIBTEX_GET_PRIVATE (self);
          
         switch (property_id) {
         case PROPERTY_TITLE:
                 if (priv->title)
                         g_string_free (priv->title, TRUE);
                 priv->title = g_string_new (g_value_get_string (value));
                 break ;
         case PROPERTY_AUTHOR:
                 if (priv->author)
                         g_string_free (priv->author, TRUE);
                 priv->author = g_string_new (g_value_get_string (value));
                 break ;
         case PROPERTY_PUBLISHER:
                 if (priv->publisher)
                         g_string_free (priv->publisher, TRUE);
                 priv->publisher = g_string_new (g_value_get_string (value));
                 break ;
         case PROPERTY_YEAR:
                 priv->year = g_value_get_uint (value);
                 break ;
         default :
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                 break ;
         }
}
 
static void
kb_bibtex_get_property (GObject *object, guint property_id,
                         GValue *value, GParamSpec *pspec)
{
         KbBibtex *self = KB_BIBTEX (object);
         KbBibtexPrivate *priv = KB_BIBTEX_GET_PRIVATE (self);
         GString *similar = NULL;
          
         switch (property_id) {
         case PROPERTY_TITLE:
                 g_value_set_string (value, priv->title->str);
                 break ;
         case PROPERTY_AUTHOR:
                 g_value_set_string (value, priv->author->str);
                 break ;
         case PROPERTY_PUBLISHER:
                 g_value_set_string (value, priv->publisher->str);
                 break ;
         case PROPERTY_YEAR:
                 g_value_set_uint (value, priv->year);
                 break ;
         default :
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                 break ;
         }
}

为了是上述代码能够工作,需要在 kb-bibtex.c 文件中定义 KbBibtex 各个类属性的 ID:

?
1
2
3
4
5
6
7
8
enum PROPERTY_BIBTEX {
         PROPERTY_0,
         PROPERTY_TITLE,
         PROPERTY_AUTHOR,
         PROPERTY_PUBLISHER,
         PROPERTY_YEAR,
         N_PROPERTIES
};

注意,enum 类型中,第一个成员 PROPERTY_0 仅仅是个占位符,它不会被使用,而最后一个成员 N_PROPERTIES 则用于表示类属性的个数,它在向类中安装属性的时候可以用到,详见下文。

6. 类属性的构造与安装

一旦完成了 KbBibtex 类属性与 KbBibtex 对象属性之间的映射,即 kb_bibtex_set_property 与 kb_bibtex_get_property 函数,那么便可以在 KbBibtex 类结构体构造函数中使用 GObject 类的 setter 与 getter 指针指向它们:

?
1
2
3
4
5
6
7
8
9
static void
kb_bibtex_class_init (KbBibtexClass *klass)
{
         g_type_class_add_private (klass, sizeof (KbBibtexPrivate));
 
         GObjectClass *base_class = G_OBJECT_CLASS (klass);
         base_class->set_property = kb_bibtex_set_property;
         base_class->get_property = kb_bibtex_get_property;
/* 未完 */

然后,构造类属性,与文档 [3] 有所区别,本文采用的是 ParamSpec 类型的指针数组,一遍在类属性构造完毕后,使用 g_object_class_install_properties 函数一并安装到类结构体中,而不是像文档 [3] 那样使用 g_object_class_install_property 函数一个一个的安装。

类属性的构造代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* 接上面的代码 */
         GParamSpec *properties[N_PROPERTIES] = {NULL,};
         properties[PROPERTY_TITLE] =
                 g_param_spec_string ( "title" ,
                                      "Title" ,
                                      "Bibliography title" ,
                                      NULL,
                                      G_PARAM_READWRITE);
         properties[PROPERTY_AUTHOR] =
                 g_param_spec_string ( "author" ,
                                      "Author" ,
                                      "Bibliography author" ,
                                      NULL,
                                      G_PARAM_READWRITE);
         properties[PROPERTY_PUBLISHER] =
                 g_param_spec_string ( "publisher" ,
                                      "Publisher" ,
                                      "Bibliography Publisher" ,
                                      NULL,
                                      G_PARAM_READWRITE);
         properties[PROPERTY_YEAR] =
                 g_param_spec_uint ( "year" ,
                                    "Year" ,
                                    "Bibliography year" ,
                                    0,
                                    G_MAXUINT,
                                    0,
                                    G_PARAM_READWRITE);
/* 未完 */

最后,安装类属性:

?
1
2
3
/* 接上面代码 */
         g_object_class_install_properties (base_class, N_PROPERTIES, properties);
}

使用 KbBibtex 类

建立 main.c 源文件,内容为:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "kb-bibtex.h"
 
int
main ( void )
{
         g_type_init ();
 
         KbBibtex *entry = g_object_new (KB_TYPE_BIBTEX,
                                         "title" , "The {\\TeX}Book" ,
                                         "author" , "Knuth, D. E." ,
                                         "publisher" , "Addison-Wesley Professional" ,
                                         "year" , 1984,
                                         NULL);
 
         gchar *title, *author, *publisher;
         guint year;
 
         g_object_get (G_OBJECT (entry),
                       "title" , &title,
                       "author" , &author,
                       "publisher" , &publisher,
                       "year" , &year,
                       NULL);
 
         g_printf ( "    Title: %s\n"
                   "   Author: %s\n"
                   "Publisher: %s\n"
                   "     Year: %d\n" , title, author, publisher, year);
 
         g_free (title);
         g_free (author);
         g_free (publisher);
         
         g_object_unref (entry);
         return 0;
}

编译这个程序的命令为:

?
1
$ gcc $(pkg-config --cflags --libs gobject-2.0) kb-bibtex.c main.c  -o test

程序运行结果:

?
1
2
3
4
5
$ . /test
     Title: The {\TeX}Book
    Author: Knuth, D. E.
Publisher: Addison-Wesley Professional
      Year: 1984

为类和对象添加方法

在面向对象程序设计方法中,类和对象皆由“属性”与“方法“构成。文档 [1-3] 所讲的均是基于 GObject 子类化的数据抽象与封装,而未有涉及到类和对象的方法。其实,这样说并不正确,因为我们已经接触到了 GObject 子类的类结构体与实例结构体的构造函数,它们分别是类的方法和对象的方法。

类的方法,形式如下:

?
1
2
3
4
5
返回值
函数名 (参数, ...)
{
 
}

对象的方法,形式如下:

?
1
2
3
4
5
返回值
函数名 (参数, ...)
{
 
}

#@¥%……开玩笑呢吧?这两种方法有什么区别?它们不就是普通的 C 函数么?

嗯,你以为呢?就是普通的 C 函数。

下面为 KbBibtex 类增加一个对象方法,这个函数的功能是可以在终端输出文献信息。

首先,在 kb-bibtex.h 头文件的底部增加函数声明:

?
1
2
/* 对象的方法 */
void kb_bibtex_printf (KbBibtex *self);

然后在 kb-bibtex.c 源文件中实现该函数,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void
kb_bibtex_printf (KbBibtex *self)
{
         gchar *title, *author, *publisher;
         guint year;
 
         g_object_get (G_OBJECT (self),
                       "title" ,     &title,
                       "author" ,    &author,
                       "publisher" , &publisher,
                       "year" ,      &year,
                       NULL);
 
         g_printf ( "    Title: %s\n"
                   "   Author: %s\n"
                   "Publisher: %s\n"
                   "     Year: %d\n" , title, author, publisher, year);
 
         g_free (title);
         g_free (author);
         g_free (publisher);
}

这样,main.c 源文件便可以被简化为:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "kb-bibtex.h"
 
int
main ( void )
{
         g_type_init ();
 
         KbBibtex *entry = g_object_new (KB_TYPE_BIBTEX,
                                         "title" , "The {\\TeX}Book" ,
                                         "author" , "Knuth, D. E." ,
                                         "publisher" , "Addison-Wesley Professional" ,
                                         "year" , 1984,
                                         NULL);
 
         kb_bibtex_printf (entry);
         
         g_object_unref (entry);
         return 0;
}

~ End ~

参考文档

[1使用 GObject 库模拟类的数据封装形式

[2GObject 子类私有属性模拟

[3GObject 子类私有属性的外部访问

转载时,希望不要链接文中图片,另外请保留本文原始出处:http://garfileo.is-programmer.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 1 University students can understand innovation through learning from the past. 2. Students can better review by breaking down complex concepts into smaller components and studying the material in an organized way. 3. When learning from the past to understand innovation, it is important to focus on understanding the big picture and to not get bogged down in the details. ### 回答2: 1. 大学生如何理解温故而知新温故而知新是一种学习方法,它要求我们在学习新知识之前先回顾和巩固已经学过的知识。大学生理解温故而知新意味着要在学习新知识之前,先回顾和复习以前学过的相关知识或基础知识。通过温故,我们能够加深对已有知识的理解和记忆,从而更好地理解和掌握新的知识。 2. 学生如何更好地去复习? 学生要更好地复习,可以采取以下策略: 首先,制定一个合理的复习计划,将要复习的内容分配到不同的时间段,确保每个科目都有足够的时间。 其次,采用多种复习方法,如阅读教材、做练习题、参加讨论等,以帮助加深理解和牢固记忆。 另外,与同学或老师一起讨论复习内容,通过讲解和互动来加深理解。 此外,保持良好的学习习惯,比如及时复习、做好笔记等,能够帮助学生更好地掌握和复习知识。 3. 温故而知新的过程需要注意什么? 在温故而知新的过程中,需要注意以下几点: 首先,要有针对性,根据自己的学习需求和复习目标,选择性地回顾和复习相关知识点。 其次,要有系统性,将复习内容进行分类整理,形成一个清晰的知识框架,有助于加深理解和记忆。 另外,要关注重难点,重点复习那些相对较难或容易遗忘的知识点,加强对这些内容的学习和理解。 还要有耐心和恒心,温故而知新是一个持续的过程,需要长期坚持和不断巩固。 最后,要善于总结和归纳,通过整理和回顾复习过程中的笔记和练习,提炼出关键概念和思维模式,便于记忆和应用。 ### 回答3: 1. 大学生如何理解温故而知新? 大学生可以理解为通过回顾过去的知识和经验,来获取新的见解和理解。温故是指回顾已经学过的知识,了解其中的原理、概念和重要点。而知新则是指通过对新知识的学习,扩展和更新自己的知识体系。温故而知新相辅相成,是一个持续学习和发展的过程。 2. 学生如何更好地去复习? 学生可以通过以下方式更好地进行复习: - 制定合理的复习计划:根据时间安排和课程难度,合理分配复习时间,确保每个学科都有足够的复习时间。 - 多种复习方法结合:采用不同的学习方式,如阅读教材、做练习题、参与讨论、制作思维导图等,帮助巩固记忆和理解知识。 - 主动参与课堂:积极参与讨论和提问,与同学和老师交流,加深对知识的理解和记忆。 - 不断反思和总结:及时检查自己的复习情况,发现不足和问题,并及时调整学习方法和计划。 3. 温故而知新的过程需要注意什么? 在温故而知新的过程中,学生需要注意以下几点: - 有目的性地温故:针对具体的知识点或者问题进行回顾,明确自己的学习目标和重点。 - 理解和记忆结合:不仅要理解概念和原理,还要通过多次的复习和记忆,帮助信息在大脑中形成长期记忆。 - 理论联系实际:将学到的知识应用到实际情境中,加深对知识的理解和记忆。 - 及时巩固复习成果:通过做练习题、整理笔记、与同学讨论等方式,巩固复习的成果,确保知识掌握得更牢固。 - 长期持续学习:温故而知新是一个持续的过程,要保持学习的热情和动力,不断更新自己的知识体系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值