GTK3 提供了使强制转换成为必要的函数。如在
gtk_grid_attach(GTK_GRID(grid), button, 0,0,0,0)
但是,此方法将始终采用网格。那么为什么演员表不在函数内部呢?调用将变为:
gtk_grid_attach(grid, button, 0,0,0,0)
因此会更短,更易于阅读(因为没有冗余(。
编辑:
由于评论中的讨论,我将尝试使用下面大卫·拉涅利的例子使我的问题更清晰
给定代码:
typedef struct {char *sound;} animal_t;
typedef struct {animal_t animal; char *name;} dog_t;
typedef struct {animal_t animal;} cat_t;
#define DOG(animal) (dog_t *)animal
#define CAT(animal) (cat_t *)animal
有两种方法可以在不丢失类型检查机制的情况下实现函数dog_print_name
。在下面的示例中,我将交出一个cat
,其中需要一只狗。
(1(
void dog_print_name(dog_t *dog)
{
puts(dog->name);
}
int main(){
// ...
dog_print_name(DOG(cat)); // perfom type check here
// and fail on 'cat'
}
(二(
void dog_print_name(void *dog)
{
dog_t *dog_ = DOG(dog); // check performed
// will fail, if 'dog' is a cat.
puts(dog->name);
}
int main(){
// ...
dog_print_name(cat); // no check performed here
}
给出这两段代码,人们选择实现(1(而不是实现(2(的原因是什么? 目标是在预期dog
但给出cat
的呼叫中收到警告/错误。
这是必需的,因为 C 不处理多态性。它无法根据类层次结构检查该参数的类型是否有效。因此,宏会触发对此的检查,并确保即使您将指针存储在正确的指针类型中,它指向的内容(GObject 派生对象(也使用正确的类型。
在这里,grid
具有正确的类型,但没有正确的内容。进行显式检查使调试变得更加容易。
GtkGrid *grid = gtk_image_new();
GtkButton *button = gtk_button_new();
gtk_grid_attach(grid, button, 0,0,0,0); // Won't catch early the fact that grid is a GtkImage, not a GtkGrid.
此外,大多数时候,您只需将所有内容声明为指向 GtkWidget 的指针。这是因为根据您调用的方法,它们来自的类可能会有所不同,因此无论如何您都需要强制转换。
GtkWidget *grid = gtk_grid_new();
GtkWidget *button = gtk_button_new();
gtk_grid_attach(GTK_GRID(grid), button, 0,0,0,0); // WILL check at runtime that grid is a GtkGrid.
gtk_grid_remove_row(GTK_GRID(grid), 0);
gtk_container_add(GTK_CONTAINER(grid), button);
只是为了扩展一下@liberfforce的好答案:
唯一允许在没有强制转换的情况下接收指向不同类型的指针的类型是void *
,所以你需要强制转换,因为某些函数(如grid_attach
(期望接收派生对象(GtkGrid
(,而不是基对象(GtkWidget
一个小例子说明了继承在 gtk 中的工作原理:
typedef struct {char *sound;} animal_t;
typedef struct {animal_t animal; char *name;} dog_t;
typedef struct {animal_t animal; int age;} cat_t;
#define DOG(animal) (dog_t *)animal
#define CAT(animal) (cat_t *)animal
static animal_t *new_dog(char *name)
{
dog_t *dog = malloc(sizeof *dog);
dog->animal.sound = "worf";
dog->name = name;
return &dog->animal;
}
static animal_t *new_cat(int age)
{
cat_t *cat = malloc(sizeof *cat);
cat->animal.sound = "meow";
cat->age = age;
return &cat->animal;
}
void animal_print_sound(animal_t *animal)
{
puts(animal->sound);
}
void dog_print_name(dog_t *dog)
{
puts(dog->name);
}
void cat_print_age(cat_t *cat)
{
printf("%dn", cat->age);
}
int main(void)
{
animal_t *dog = new_dog("Bobbie");
animal_t *cat = new_cat(5);
animal_print_sound(dog);
animal_print_sound(cat);
dog_print_name(DOG(dog));
cat_print_age(CAT(cat));
return 0;
}
那么为什么不实现:
void dog_print_name(animal_t *dog) { puts(DOG(dog)->name); } void cat_print_age(animal_t *cat) { printf("%dn", CAT(cat)->age); }
?
从"用户"的角度来看,
dog_print_name(dog);
看起来更多 我可以理解。
因为这样编译器无法保护你检查类型,想想如果你使用你的方法传递dog_print_name(cat);
会发生什么。