Calling base function from derived

自作多情 提交于 2019-12-13 10:27:14

问题


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.

  1. 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.)

  1. 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!