从派生中调用基函数

我在C中使用了本教程示例inheritance和多态 ,因为我根据我的确切要求对其进行了自定义,当我尝试调用基函数时它会抛出错误。

问题:为什么它在employee.c的第8行和可能的解决方案中失败

((Employee *)self)->super.display(self); // Sementation fault: 11 

下载项目

main.c中

 #include "person.h" #include "employee.h" #include  int main() { Person* person = newPerson("John Doe"); Employee* employee = newEmployee("Jane Doe", "Acme", 40000); person->display(person); // displaying Person object puts("------"); employee->display((Person*)employee); // displaying employee info return 0; } 

Person.h

 #ifndef _PERSON_H #define _PERSON_H #include  typedef struct Person Person; struct Person { char* name; void (*display)(Person*); }; Person* newPerson(char* name); #endif 

Person.c

 #include "person.h" #include  #include  #include  static void display(Person* const self) { printf("Name: %s\n", self->name); } Person* newPerson(char* name) { Person* person = malloc(sizeof(Person)); person->name = name; person->display = display; return person; } 

Employee.h

 #include "person.h" typedef struct Employee Employee; struct Employee { Person super; char* company; int salary; void (*display)(Person*); }; Employee* newEmployee(char* name, char* company, int salary); 

Employee.c

 #include "employee.h" #include  #include  static void display(Person* const self) { puts(((Employee*)self)->super.name); // works // ((Employee *)self)->super.display(self); // Sementation fault: 11 printf("Company: %s\n", ((Employee *)self)->company); printf("Salary: %d\n", ((Employee*)self)->salary); } Employee* newEmployee(char* name, char* company, int salary) { Employee* employee = malloc(sizeof(Employee)); employee->super.name = name; employee->company = company; employee->salary = salary; employee->display = display; return employee; } 

也许,对于链中的每个类,从基础Person开始,您应该在不同的名称下提供方法实现:

Person.h

 typedef struct _Person Person; typedef struct _Person { void* derived; char* first; char* last; void (*display)(Person*); } Person; Person* newPerson(char* first, char* last); void Person_display(Person *); // analogous to Person::display in C++ 

Person.c

 Person* newPerson(char* first, char* last) { Person* person = (Person*)malloc(sizeof(Person)); person->derived = person; // pointing to itself person->first = first; person->last = last; person->display = Person_display; // Initializing interface for access to functions return person; } 

Employee.h

 void Employee_display(Person const *); // available to lower subclasses 

Employee.c中

 static void display(Person* const self) { Person_display(self); // calling the superclass implementation Employee *employee = self->derived; printf("Company: %s\n", employee->company); printf("Salary: %d\n", employee->salary); } Person* newEmployee(char* first, char* last, char* company, int salary) { Person* person = newPerson(first, last); // calling base class constructor Employee* employee = malloc(sizeof(Employee)); person->derived = employee; // pointing to derived object employee->company = company; // initialising derived class members employee->salary = salary; person->display = Employee_display; // Changing base class interface to access derived class functions return person; } 

请注意,这与通常的C ++虚方法契约一致:从基类ctor调用display()解析为基类的实现,派生类的方法仅完全构造基类子对象后才可用。

问题是因为Employee中的嵌入式结构没有初始化显示函数指针并指向函数

 struct Employee { Person super; ... } 

解决方案:将嵌入式结构Person更改为指针类型,并为super调用newPerson

employee.h

 typedef struct Employee Employee; struct Employee { Person *super; // change this pointer type char* company; int salary; void (*display)(Person*); }; Employee* newEmployee(char* name, char* company, int salary); 

employee.c

 static void display(Person* const self) { ((Employee*)self)->super->display(((Employee*)self)->super); printf("Company: %s\n", ((Employee *)self)->company); printf("Salary: %d\n", ((Employee*)self)->salary); } Employee* newEmployee(char* name, char* company, int salary) { Employee* employee = malloc(sizeof(Employee)); employee->super = newPerson(name); // call constructor here employee->company = company; employee->salary = salary; employee->display = display; return employee; } 

你目前要做的是这个。

首先,定义一个“父”结构:

 typedef struct _Person { void* derived; char* first; char* last; void (*display)(Person*); } Person; 

接下来,您定义一个’派生’结构:

 typedef struct _Employee { Person* super; char* company; int salary; void (*display)(Person*); } Employee; 

最后你将一种类型转换为另一种:

 return (Person*)employee; 

这是错的。 它需要为Employee结构分配内存并尝试将其解释为Person。 换句话说,将super视为derived ,将company视为first ,将salary位模式视为last 。 我希望你意识到这不是你的意思。

您的EmployeePerson子对象实际上是由super指出的。 当然你可以从newEmployee()返回employee->super ,这将是Person的正确实例,但这实际上是PersonPerson的具体实例。 它不再是多态的,对象的员工特定部分将会丢失,无法恢复,无法访问和泄露 – 没有办法将Person转发给员工。

你有两个选择。

  1. struct _Employee的声明更改为

typedef struct _Employee { Person super;

通过这种方式,您可以立即进行上行和下行(简单地将Employee *转换为Person * ,反之亦然)。 然后,可以通过其superemployee->super.display = display (其中分配的显示是Employee.c中定义的静态过程;访问对象的Employee特定部分,它)来访问Employee的所有Person属性。需要将它转发给Person)。

这种方法的明显警告是类型安全性的一些损失(给定一个指向Person的指针,你无法判断它是Person还是Employee;这可以通过在基类中显式定义具体类型描述符来解决:

 struct _Person { enum { PERSON, EMPLOYEE, STRANGER, UNDERCOVER_AGENT } concreteClass; 

现在您有一个运行时类型信息,但是您已将允许的子类集限制为Person,而不是通常实现多态类型的方式。)

  1. 坚持使用void *derived的原始设计指向对象的具体子类特定部分。

作为旁注,你的原始想法derived指向结构本身以防它是一个Person的实例是一种相当优雅的方式来区分可实例化的基类与那些抽象:):按照惯例,抽象类的构造函数集derivedNULL和将它留给派生类的ctor,将其设置为正确的值; 并且每个虚方法首先检查它是否为非NULL,否则抛出exception。