问题
I took this tutorial example Inheritance and Polymorphism in C and because I've customized it for my exact requirements it's throwing an error when I try to call base function.
Question: Why does it fail in line 8 of employee.c and Possible resolution
((Employee *)self)->super.display(self); // Sementation fault: 11
Download Project
main.c
#include "person.h"
#include "employee.h"
#include <stdio.h>
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 <stdlib.h>
typedef struct Person Person;
struct Person {
char* name;
void (*display)(Person*);
};
Person* newPerson(char* name);
#endif
Person.c
#include "person.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
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 <string.h>
#include <stdio.h>
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;
}
回答1:
Probably, for every class in the chain, starting with the base Person, you should make method implementations available under separate names:
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
And in 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;
}
Note that this is consistent with usual C++ virtual method contracts: calling display() from the base class ctor resolves to the base class's implementation and the derived class's method is only available after the base class subobject has been fully constructed.
回答2:
The problem was because embedded struct in Employee didn't have display function pointer initialized and pointed to a function
struct Employee {
Person super;
...
}
Solution: Change the embedded structure Person to pointer type and call newPerson for super
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;
}
回答3:
What you're currently trying to do is this.
First, you define a 'parent' structure:
typedef struct _Person {
void* derived;
char* first;
char* last;
void (*display)(Person*);
} Person;
Next, you define a 'derived' structure:
typedef struct _Employee {
Person* super;
char* company;
int salary;
void (*display)(Person*);
} Employee;
And finally you cast one type to the other:
return (Person*)employee;
which is wrong. It takes memory allocated for the Employee struct and tries to interpret it as Person. In other words, regards super
as derived
, company
as first
and bit pattern in salary
as last
. I hope you realize that's not quite what you meant.
The Person
subobject of your Employee
is actually pointed-to by super
. Of course you can return employee->super
from newEmployee()
, this will be a correct instance of Person
, but this is really a Person
, a concrete instance of Person
. It's not polymorphic anymore, and the employee-specific part of the object will then be lost, unrecoverable, unreachable and leaked — there's no way to downcast Person to Employee.
You have two options.
- Change the declaration of
struct _Employee
to
typedef struct _Employee {
Person super;
This way you have immediate up- and downcasts (simply cast Employee *
to Person *
, and vice versa). All the Person properties of an Employee will then be accessible via its super
: employee->super.display = display
(where the display being assigned is that static procedure defined in Employee.c; to access Employee-specific portion of the object, it needs to downcast it to Person).
The obvious caveat to this approach is some loss of type safety (given a pointer to Person you cannot tell whether it is a Person or an Employee; this can be worked around by explicitly defining a concrete type descriptor in the base class:
struct _Person {
enum { PERSON, EMPLOYEE, STRANGER, UNDERCOVER_AGENT } concreteClass;
Now you have a run-time type information, but you have limited the allowed set of subclasses to your Person which is not how polymorphic types are commonly implemented.)
- Stick to the original design with
void *derived
pointing to concrete subclass-specific part of the object.
As a side note, your original idea with derived
pointing to the struct itself in case it is an instance of Person
is a rather elegant way to distinguish instantiable base classes from those abstract :) : by convention, abstract class's constructor sets derived
to NULL
and leaves it to the derived class's ctor to set that to proper value; and each virtual method first checks whether it is non-NULL and throws an exception otherwise.
来源:https://stackoverflow.com/questions/48981982/calling-base-function-from-derived