热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

GTK+浅谈之十五GObject面向对象的继承

本文介绍了GTK+中的GObject对象系统,该系统是基于GLib和C语言完成的面向对象的框架,提供了灵活、可扩展且易于映射到其他语言的特性。其中最重要的是GType,它是GLib运行时类型认证和管理系统的基础,通过注册和管理基本数据类型、用户定义对象和界面类型来实现对象的继承。文章详细解释了GObject系统中对象的三个部分:唯一的ID标识、类结构和实例结构。

一、简介

       GObject的对象系统是一个建立在GLib基础上的,用C语言完成的,提供了一种灵活的、可扩展的、并容易映射(到其它语言)的面向对象的C语言框架。在GLib和GTK+中使用C的语法模拟CPP的行为,实现继承。
       GLib中最有特色的是它的对象系统:GObject System,它是以Gtype为基础而实现的一套单根继承的C语言的面向对象的框架。GType是GLib运行时类型认证和管理系统。GType API是GObject的基础系统,所以理解GType是理解GObject的关键。Gtype提供了注册和管理所有基本数据类型、用户定义对象和界面类型的技术实现。(注意:在运用任一GType和GObject函数之前必需运行g_type_init()函数来初始化类型系统)。
       在GObject系统中,对象由三个部分组成:(1)对象的ID标识(唯一,无符号长整型,所有此类对象共同的标识);(2)对象的类结构(唯一,结构型,由对象的所有实例共同拥有);(3)对象的实例(多个,结构型,对象的具体实现)。

二、详解

1、继承GObject的对象Boy(父类是GObject)

(1)头文件boy.h:
#ifndef _BOY_H_
#define _BOY_H_
#include
/**BOY_TYPE宏封装了boy_get_type函数,可以直接取得并替代Boy对象的ID标识;
* BOY(obj)宏是G_TYPE_CHECK_INSTANCE_CAST宏的再一次封装,目的是将一个Gobject对象强制转换为Boy对象
**/
#define BOY_TYPE (boy_get_type())
#define BOY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BOY_TYPE, Boy))

typedef struct _Boy Boy;
typedef struct _BoyClass BoyClass;
/**结构类型_Boy是Boy对象的实例,就是说我们每创建一个Boy对象,也就同时创建了一个Boy结构。
* Boy对象中的parent表示此对象的父类,GObject系统中所有对象的共同的根都是GObject类,所以这是必须的;
* 其它的成员可以是公共的,这里包括表示年龄的age,表示名字的name和表示方法的函数指针cry,
* 外部代码可以操作或引用它们。
**/
struct _Boy
{
GObject parent;
gint age;
gchar *name;
void (*cry)(void);
};
/**结构类型_BoyClass是Boy对象的类结构,它是所有Boy对象实例所共有的。
* BoyClass中的parent_class是GObjectClass,同GObject是所有对象的共有的根一样,GObejctClass是所有对象的类结构的根。
* 在BoyClass中我们还定义了一个函数指针boy_born,也就是说这一函数指针也是所有Boy对象实例共有的,
* 所有的Boy实例都可以调用它;同样,如果需要的话,你也可以在类结构中定义其它数据成员。
**/
struct _BoyClass
{
GObjectClass parent_class;
void (*boy_born)(void);
};
/**其余的函数定义包括三种:一种是取得Boy对象的类型ID的函数boy_get_type,这是必须有的;
* 另一种是创建Boy对象实例的函数boy_new和boy_new_with_*,这是非常清晰明了的创建对象的方式,当然你也可以用g_object_new函数来创建对象;
* 第三种是设定或取得Boy对象属性成员的值的函数boy_get_*和boy_set_*。
* 正常情况下这三种函数都是一个对象所必需的,另外一个函数boy_info用来显示此对象的当前状态**/
GType boy_get_type(void);
Boy *boy_new(void);
gint boy_get_age(Boy *boy);
void boy_set_age(Boy *boy, int age);
char *boy_get_name(Boy *boy);
void boy_set_name(Boy *boy, char *name);
Boy *boy_new_with_name(gchar *name);
Boy * boy_new_with_age(gint age);
Boy * boy_new_with_name_and_age(gchar *name, gint age);
void boy_info(Boy *boy);
#endif // _BOY_H_
(2)实现文件boy.c:
#include "boy.h"/**用LAST_SIGNAL来表示最后一个信号(不用实现的信号)是一种非常良好的编程风格**/enum { BOY_BORN, LAST_SIGNAL };static gint boy_signals[LAST_SIGNAL] = { 0 };static void boy_cry (void);static void boy_born(void);static void boy_init(Boy *boy);static void boy_class_init(BoyClass *boyclass);/**g_type_register_static函数用来注册对象的类型,它的第一个参数是表示此对象的父类的对象类型,我们这里是G_TYPE_OBJECT,这个宏用来表示GObject的父类; * 第二个参数表示此对象的名称,这里为"Boy"; * 第三个参数是此对象的GTypeInfo结构型指针,这里赋值为&boyinfo; * 第四个参数是对象注册成功后返回此对象的整型ID标识。 **/GType boy_get_type(void){    static GType boy_type = 0;    if(!boy_type) {        static const GTypeInfo boy_info = {            sizeof(BoyClass),            NULL,NULL,            (GClassInitFunc)boy_class_init,            NULL,NULL,            sizeof(Boy),            0,            (GInstanceInitFunc)boy_init        };        boy_type = g_type_register_static(G_TYPE_OBJECT,"Boy",&boy_info,0);    }    return boy_type;}/**boy_init和boy_class_init,它们分别用来初始化实例结构和类结构。它们并不被在代码中明显调用, * 关键是将其用宏转换为地址指针,然后赋值到GTypeInfo结构中,然后由GType系统自行处理, * 同时将它们定义为静态的也是非常必要的。 **/static void boy_init(Boy *boy){    boy->age = 0;    boy->name = "none";    boy->cry = boy_cry;}static void boy_class_init(BoyClass *boyclass){    boyclass->boy_born = boy_born;    /**Boy对象定义了一个信号BOY_BORN,在对象创建时发出,表示Boy对象诞生。    *同时还需要定义静态的整型指针数组来保存信号的标识    **/    boy_signals[BOY_BORN] = g_signal_new("boy_born",                BOY_TYPE,                G_SIGNAL_RUN_FIRST,                G_STRUCT_OFFSET(BoyClass,boy_born),                NULL,NULL,                g_cclosure_marshal_VOID__VOID,                G_TYPE_NONE, 0, NULL);}Boy *boy_new(void){    Boy *boy;    boy = g_object_new(BOY_TYPE, NULL);    //g_signal_emit向指定义对象的实例发射信号    g_signal_emit(boy, boy_signals[BOY_BORN], 0);    return boy;}int boy_get_age(Boy *boy){    return boy->age;}void boy_set_age(Boy *boy, int age){    boy->age = age;}char *boy_get_name(Boy *boy){    return boy->name;}void boy_set_name(Boy *boy, char *name){    boy->name = name;}Boy*  boy_new_with_name(gchar *name){    Boy *boy;    boy = boy_new();    boy_set_name(boy, name);    return boy;}Boy*  boy_new_with_age(gint age){    Boy* boy;    boy = boy_new();    boy_set_age(boy, age);    return boy;}Boy *boy_new_with_name_and_age(gchar *name, gint age){    Boy *boy;    boy = boy_new();    boy_set_name(boy,name);    boy_set_age(boy,age);    return boy;}static void boy_cry (void){    g_print("---The Boy is crying......\n");}static void boy_born(void){    g_print("---Message : A boy was born.\n");}void boy_info(Boy *boy){    g_print("---The Boy name is %s\n", boy->name);    g_print("---The Boy age is %d\n", boy->age);}

2、继承自Boy的对象Man(父类是Boy)

(1)头文件man.h:
#ifndef _MAN_H_
#define _MAN_H_
/**********Man继承自Boy对象,Man对象在Boy对象的基础上又增加了一个属性job和一个方法bye***********/
#include "boy.h"

#define MAN_TYPE (man_get_type())
#define MAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MAN_TYPE, Man))
typedef struct _Man Man;
typedef struct _ManClass ManClass;
struct _Man
{
Boy parent;
gchar *job;
void (*bye)(void);
};
struct _ManClass
{
BoyClass parent_class;
};

GType man_get_type();
Man *man_new(void);
gchar *man_get_job(Man *man);
void man_set_job(Man *man, gchar *job);
Man *man_new_with_name_age_and_job(gchar *name, gint age, gchar *job);
void man_info(Man *man);

#endif // _MAN_H_
(2)实现文件man.c:
#include "man.h"static void man_bye(void);static void man_init(Man *man);static void man_class_init(Man *man);/**关键在于定义对象时将父对象实例定义为Boy,父类设定为BoyClass,在注册此对象时将其父对象类型设为BOY_TYPE。 **/GType man_get_type(void){    static GType man_type = 0;    if(!man_type) {        static const GTypeInfo man_info = {            sizeof(ManClass),            NULL, NULL,            (GClassInitFunc)man_class_init,            NULL, NULL,            sizeof(Man),            0,            (GInstanceInitFunc)man_init        };        /**在注册此对象时将其父对象类型设为BOY_TYPE**/        man_type = g_type_register_static(BOY_TYPE, "Man", &man_info, 0);    }    return man_type;}static void man_init(Man *man){    man->job = "none";    man->bye = man_bye;}static void man_class_init(Man *man){}Man*  man_new(void){    Man *man;    man = g_object_new(MAN_TYPE, 0);    return man;}gchar* man_get_job(Man *man){    return man->job;}void  man_set_job(Man *man, gchar *job){    man->job = job;}Man*  man_new_with_name_age_and_job(gchar *name, gint age, gchar *job){    Man *man;    man = man_new();    boy_set_name(BOY(man), name);    boy_set_age(BOY(man), age);    man_set_job(man, job);    return man;}static void man_bye(void){    g_print("+++Goodbye everyone!\n");}/**在设定对象属性时如用到父对象的属性要强制转换下,如取得对象的name属性,就必须用BOY(obj)->name, *因为Man本身没有name属性,而其父对象Boy有,所以用BOY宏将其强制为Boy类型的对象。**/void man_info(Man *man){    /**定义对象时将父对象实例定义为Boy,父类设定为BoyClass**/    g_print("+++the man name is %s\n", BOY(man)->name);    g_print("+++the man age is %d\n", BOY(man)->age);    g_print("+++the man job is %s\n", man->job);}

3、main文件和makefile及运行

main.c文件:
#include 
#include "boy.h"
#include "man.h"

int main(int argc, char *argv[])
{
Boy *tom, *peter;
Man *green, *brown;
g_type_init();//注意,初始化类型系统,必需
g_print("**********************\n");
tom = boy_new_with_name("Tom");
tom->cry();
boy_info(tom);
g_print("**********************\n");
peter = boy_new_with_name_and_age("Peter", 10);
peter->cry();
boy_info(peter);
g_print("**********************\n");
g_print("######################\n");
green = man_new();
/**设定Man对象的name属性用到其父对象Boy的方法**/
boy_set_name(BOY(green), "Green");
boy_set_age(BOY(green), 28);
man_set_job(green, "Doctor");
man_info(green);
green->bye();
g_print("######################\n");
brown = man_new_with_name_age_and_job("Brown", 30, "Teacher");
man_info(brown);
brown->bye();
g_print("######################\n");
return TRUE;
}
makefile文件:
CC = gcc
all:
$(CC) -c boy.c `pkg-config --cflags glib-2.0 gobject-2.0`
$(CC) -c man.c `pkg-config --cflags glib-2.0 gobject-2.0`
$(CC) -c main.c `pkg-config --cflags glib-2.0 gobject-2.0`
$(CC) -o simple boy.o man.o main.o `pkg-config --libs glib-2.0 gobject-2.0`
运行结果:

4、宏G_DEFINE_TYPE

       GObject里面提供了一些简化宏定义。其中包括*_init、*_class_init的声明,*_class_intern_init、*_get_type的定义,还有一个静态全局变量*_parent_class的声明。如G_DEFINE_TYPE(Boy, boy, G_TYPE_OBJECT);(定义在文件:/usr/include/glib-2.0/gobject/gtype.h)其中第三个参数用于表明父类的类型,在需要继承其他的类时特别重要。*_class_init函数对每个类只会调用一次,class结构对每个类来说也只有一个副本,这个函数有两个作用:一是对class结构中的域进行初始化,二是注册该类的属性、信号、私有数据。
#define G_DEFINE_TYPE(TN, t_n, T_P)  G_DEFINE_TYPE_EXTENDED (TN, t_n, T_P, 0, {})
#define G_DEFINE_TYPE_EXTENDED(TN, t_n, T_P, _f_, _C_)  _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, _f_) {_C_;} _G_DEFINE_TYPE_EXTENDED_END()
#define _G_DEFINE_TYPE_EXTENDED_BEGIN(TypeName, type_name, TYPE_PARENT, flags) \\static void     type_name##_init              (TypeName        *self); \static void     type_name##_class_init        (TypeName##Class *klass); \static gpointer type_name##_parent_class = NULL; \static void     type_name##_class_intern_init (gpointer klass) \{ \  type_name##_parent_class = g_type_class_peek_parent (klass); \  type_name##_class_init ((TypeName##Class*) klass); \} \\GType \type_name##_get_type (void) \{ \  static volatile gsize g_define_type_id__volatile = 0; \  if (g_once_init_enter (&g_define_type_id__volatile))  \    { \      GType g_define_type_id = \        g_type_register_static_simple (TYPE_PARENT, \                                       g_intern_static_string (#TypeName), \                                       sizeof (TypeName##Class), \                                       (GClassInitFunc) type_name##_class_intern_init, \                                       sizeof (TypeName), \                                       (GInstanceInitFunc) type_name##_init, \                                       (GTypeFlags) flags); \      { /* custom code follows */#define _G_DEFINE_TYPE_EXTENDED_END()\        /* following custom code */\      }\      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \    }\  return g_define_type_id__volatile;\} /* closes type_name##_get_type() */
在上述代码中可以使用G_DEFINE_TYPE(Boy, boy, G_TYPE_OBJECT);代替:
static void boy_init(Boy *boy);
static void boy_class_init(BoyClass *boyclass);

GType boy_get_type(void)
{
static GType boy_type = 0;
if(!boy_type) {
static const GTypeInfo boy_info = {
sizeof(BoyClass),
NULL,NULL,
(GClassInitFunc)boy_class_init,
NULL,NULL,
sizeof(Boy),
0,
(GInstanceInitFunc)boy_init
};
boy_type = g_type_register_static(G_TYPE_OBJECT,"Boy",&boy_info,0);
}
return boy_type;
}

5、定义私有成员

#ifndef _BOY_H_
#define _BOY_H_
#include
/**BOY_TYPE宏封装了boy_get_type函数,可以直接取得并替代Boy对象的ID标识;
* BOY(obj)宏是G_TYPE_CHECK_INSTANCE_CAST宏的再一次封装,目的是将一个Gobject对象强制转换为Boy对象
**/
typedef struct _Boy Boy;
typedef struct _BoyClass BoyClass;
typedef struct _BoyPrivate BoyPrivate;

#define BOY_TYPE (boy_get_type())
#define BOY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BOY_TYPE, Boy))
#define BOY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), BOY_TYPE, BoyPrivate))

/**结构类型_Boy是Boy对象的实例,就是说我们每创建一个Boy对象,也就同时创建了一个Boy结构。
* Boy对象中的parent表示此对象的父类,GObject系统中所有对象的共同的根都是GObject类,所以这是必须的;
* 其它的成员可以是公共的,这里包括表示年龄的age,表示名字的name和表示方法的函数指针cry,
* 外部代码可以操作或引用它们。
**/
struct _Boy
{
GObject parent;
BoyPrivate *priv;
gint age;
gchar *name;
void (*cry)(void);
};
/**结构类型_BoyClass是Boy对象的类结构,它是所有Boy对象实例所共有的。
* BoyClass中的parent_class是GObjectClass,同GObject是所有对象的共有的根一样,GObejctClass是所有对象的类结构的根。
* 在BoyClass中我们还定义了一个函数指针boy_born,也就是说这一函数指针也是所有Boy对象实例共有的,
* 所有的Boy实例都可以调用它;同样,如果需要的话,你也可以在类结构中定义其它数据成员。
**/
struct _BoyClass
{
GObjectClass parent_class;
void (*boy_born)(void);
};

struct _BoyPrivate
{
gint weight;
};
/**其余的函数定义包括三种:一种是取得Boy对象的类型ID的函数boy_get_type,这是必须有的;
* 另一种是创建Boy对象实例的函数boy_new和boy_new_with_*,这是非常清晰明了的创建对象的方式,当然你也可以用g_object_new函数来创建对象;
* 第三种是设定或取得Boy对象属性成员的值的函数boy_get_*和boy_set_*。
* 正常情况下这三种函数都是一个对象所必需的,另外一个函数boy_info用来显示此对象的当前状态**/
GType boy_get_type(void);
Boy *boy_new(void);
gint boy_get_age(Boy *boy);
void boy_set_age(Boy *boy, int age);
char *boy_get_name(Boy *boy);
void boy_set_name(Boy *boy, char *name);
Boy *boy_new_with_name(gchar *name);
Boy * boy_new_with_age(gint age);
Boy * boy_new_with_name_and_age(gchar *name, gint age);
void boy_info(Boy *boy);
#endif // _BOY_H_
#include "boy.h"/**用LAST_SIGNAL来表示最后一个信号(不用实现的信号)是一种非常良好的编程风格**/enum { BOY_BORN, LAST_SIGNAL };static gint boy_signals[LAST_SIGNAL] = { 0 };static void boy_cry (void);static void boy_born(void);//static void boy_init(Boy *boy);//static void boy_class_init(BoyClass *boyclass);G_DEFINE_TYPE(Boy, boy, G_TYPE_OBJECT);/**g_type_register_static函数用来注册对象的类型,它的第一个参数是表示此对象的父类的对象类型,我们这里是G_TYPE_OBJECT,这个宏用来表示GObject的父类; * 第二个参数表示此对象的名称,这里为"Boy"; * 第三个参数是此对象的GTypeInfo结构型指针,这里赋值为&boyinfo; * 第四个参数是对象注册成功后返回此对象的整型ID标识。 **///static void boy_init(Boy *boy);//static void boy_class_init(BoyClass *boyclass);//GType boy_get_type(void)//{//    static GType boy_type = 0;//    if(!boy_type) {//        static const GTypeInfo boy_info = {//            sizeof(BoyClass),//            NULL,NULL,//            (GClassInitFunc)boy_class_init,//            NULL,NULL,//            sizeof(Boy),//            0,//            (GInstanceInitFunc)boy_init//        };//        boy_type = g_type_register_static(G_TYPE_OBJECT,"Boy",&boy_info,0);//    }//    return boy_type;//}/**boy_init和boy_class_init,它们分别用来初始化实例结构和类结构。它们并不被在代码中明显调用, * 关键是将其用宏转换为地址指针,然后赋值到GTypeInfo结构中,然后由GType系统自行处理, * 同时将它们定义为静态的也是非常必要的。 **/static void boy_init(Boy *boy){    boy->age = 0;    boy->name = "none";    boy->cry = boy_cry;    BoyPrivate *d;    d = boy->priv = BOY_GET_PRIVATE(boy);    memset(d, 0, sizeof(*d));    d->weight = 100;}static void boy_class_init(BoyClass *boyclass){    boyclass->boy_born = boy_born;    /**Boy对象定义了一个信号BOY_BORN,在对象创建时发出,表示Boy对象诞生。    *同时还需要定义静态的整型指针数组来保存信号的标识    **/    boy_signals[BOY_BORN] = g_signal_new("boy_born",                BOY_TYPE,                G_SIGNAL_RUN_FIRST,                G_STRUCT_OFFSET(BoyClass,boy_born),                NULL,NULL,                g_cclosure_marshal_VOID__VOID,                G_TYPE_NONE, 0, NULL);    g_type_class_add_private(boyclass, sizeof(BoyPrivate));}Boy *boy_new(void){    Boy *boy;    boy = g_object_new(BOY_TYPE, NULL);    //g_signal_emit向指定义对象的实例发射信号    g_signal_emit(boy, boy_signals[BOY_BORN], 0);    return boy;}int boy_get_age(Boy *boy){    return boy->age;}void boy_set_age(Boy *boy, int age){    boy->age = age;}char *boy_get_name(Boy *boy){    return boy->name;}void boy_set_name(Boy *boy, char *name){    boy->name = name;}Boy*  boy_new_with_name(gchar *name){    Boy *boy;    boy = boy_new();    boy_set_name(boy, name);    return boy;}Boy*  boy_new_with_age(gint age){    Boy* boy;    boy = boy_new();    boy_set_age(boy, age);    return boy;}Boy *boy_new_with_name_and_age(gchar *name, gint age){    Boy *boy;    boy = boy_new();    boy_set_name(boy,name);    boy_set_age(boy,age);    return boy;}static void boy_cry (void){    g_print("---The Boy is crying......\n");}static void boy_born(void){    g_print("---Message : A boy was born.\n");}void boy_info(Boy *boy){    g_print("---The Boy name is %s\n", boy->name);    g_print("---The Boy age is %d\n", boy->age);    g_print("---The Boy weight is %d\n", boy->priv->weight);}
      将struct _BoyPrivate加入struct _Boy中,先将私有成员加到类中g_type_class_add_private(boyclass, sizeof(BoyPrivate));然后使用boy->priv = BOY_GET_PRIVATE(boy);返回指向私有结构体的指针(第一次调用时会分配内存空间,创建对象后直接返回指针)。

三、总结 (1)GObject对象是如何实现作为基类和实现继承操作的方法还需以后的学习,以GLIB为基础的GTK+/GNOME开发环境所具有的独特的编程风格和独到的开发思想也需在长期的编程实践中更深刻的体验
(2)若有建议,请留言,在此先感谢!


推荐阅读
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
author-avatar
ya的sky
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有