从派生中调用基函数
我在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
。 我希望你意识到这不是你的意思。
您的Employee
的Person
子对象实际上是由super
指出的。 当然你可以从newEmployee()
返回employee->super
,这将是Person
的正确实例,但这实际上是Person
, Person
的具体实例。 它不再是多态的,对象的员工特定部分将会丢失,无法恢复,无法访问和泄露 – 没有办法将Person转发给员工。
你有两个选择。
- 将
struct _Employee
的声明更改为
typedef struct _Employee { Person super;
通过这种方式,您可以立即进行上行和下行(简单地将Employee *
转换为Person *
,反之亦然)。 然后,可以通过其super
: employee->super.display = display
(其中分配的显示是Employee.c中定义的静态过程;访问对象的Employee特定部分,它)来访问Employee的所有Person属性。需要将它转发给Person)。
这种方法的明显警告是类型安全性的一些损失(给定一个指向Person的指针,你无法判断它是Person还是Employee;这可以通过在基类中显式定义具体类型描述符来解决:
struct _Person { enum { PERSON, EMPLOYEE, STRANGER, UNDERCOVER_AGENT } concreteClass;
现在您有一个运行时类型信息,但是您已将允许的子类集限制为Person,而不是通常实现多态类型的方式。)
- 坚持使用
void *derived
的原始设计指向对象的具体子类特定部分。
作为旁注,你的原始想法derived
指向结构本身以防它是一个Person
的实例是一种相当优雅的方式来区分可实例化的基类与那些抽象:):按照惯例,抽象类的构造函数集derived
为NULL
和将它留给派生类的ctor,将其设置为正确的值; 并且每个虚方法首先检查它是否为非NULL,否则抛出exception。