引言
继承是面向对象的另一个重要特性,是代码可复用的重要手段。通过继承派生类可以拥有其他类的属性和方法,我们称派生类为子类,而这个“其他类”我们称之为基类。继承的方式有三种,他们分别是共有(public)继承,私有(private)继承,保护(protected)继承。
根据继承原理,同对象封装一样,我们依然借助struct和指针实现对象继承机制,即通过struct持有其他struct实现对象的继承,由于protocted需语言级别的支持,所以我们依然只讨论private和public继承。
本文将介绍一种类C++的继承模式,具体而言就是借助C++的new和delete的设计理念实现C语言的面向对象。由于篇幅限制,当前博客仅介绍C++的new和delete的设计理念和public继承实现,《继承(二)》会详细介绍多继承和private继承实现。
C++的new和delete
C++存在3个new和2个delete,3个new分别是operator new, new operator和placement new;2个delete分别是 operator delete和delete operator。
operator new/operator delete
operator new实现对象内存的申请,而不进行对象构造,函数返回void*类型heap内存地址,operator new有两个重载版本。
void *operator new(size_t);
void *operator new[](size_t);
operator new总是以标准的malloc完成,虽然C++标准没有明确说明,例如Glib C的实现:
void *operator new(std::size_t size)
{
void *p = NULL;
if (size == 0)
{
size = 1;
}
while (__builtin_expect ((p = malloc (size)) == 0, false))
{
new_handler handler = std::get_new_handler ();
if (!handler)
_GLIBCXX_THROW_OR_ABORT(bad_alloc());
handler ();
}
return p;
}
void *operator new[](std::size_t size)
{
return ::operator new(size);
}
operator delete实现对象内存的释放,而不调用对象的析构函数,operator delete同样存在两个重载版本
void *operator delete(void*);
void *operator delete[](void*);
operator delete也总是以标准得C free()来实现,在不考虑异常处理的情况下,实现类似如下代码
void operator delete(void *ptr )
{
if(ptr)
free((char*)ptr);
}
void operator delete[](void *ptr)
{
::operator delete(ptr)
}
new operator/delete operator
new operator就是大家常见的new Foo中的new操作符,
class Foo {...};
Foo* fp = new Foo();
delete fp;
new operator内含两个操作阶段:(1)调用::operator new分配内存;(2)调用对象的构造函数Foo::Foo ()构造对象内容。delete operator同样包含两个操作阶段:(1)调用Foo::~Foo()将对象析构;(2)调用::operator delete释放内存。
placement new
placement new中文名称定位构造,定位构造完成在已分配内存中初始化一个对象,placement new不申请内存,只是在已分配内存基础上调用对象的构造函数,实现对象的创建。定位构造的调用形式:
new(place_address) type
new(place_address) type(initializer_list)
place_address是一个指针,而initializer_list为构造函数的初始化参数列表(可为空)。
class Foo {...};
char buffer[sizeof(Foo)] = {0};
Foo* fp = new (buffer) Foo();
fp->~Foo();
placement new创建的对象不能用delete operator释放,因为placement new不申请内存,而只能调用对象的析构函数Foo::~Foo()释放对象,我们可称调用析构函数的过程为placement delete,虽然这个称呼在C++中并不存在。
共有继承
共有继承要求子类可以访问父类的常用变量和方法。现实中Employee都是Person,我们可以说Employee共有继承Person,下面通过Employee和Person定义详细介绍一种类C++的共有继承模式。
基类Person
基类Person的接口定义:
#define MAX_NAME_LENGTH 255
typedef struct Person
{
char name[MAX_NAME_LENGTH];
unsigned int age;
char* (*getName)(struct Person* self);
void (*setName)(struct Person* self, char* name);
unsigned int(*getAge)(struct Person* self);
void(*setAge)(struct Person* self, unsigned int age);
} Person;
Person* newPerson(char* name, unsigned int age);
Person* placementNewPerson(Person* self, char* name, unsigned int age);
void placementDeletePerson(Person* self);
void deletePerson(Person* self);
基类Person的具体实现
char* getName(Person* self)
{
return self->name;
}
void setName(Person* self, char* name)
{
strcpy_s(self->name, MAX_NAME_LENGTH, name);
}
unsigned int getAge(Person* self)
{
return self->age;
}
void setAge(Person* self, unsigned int age)
{
self->age = age;
}
Person* newPerson(char* name, unsigned int age)
{
Person* person = (Person*)operatorNew(sizeof(Person));
assert(NULL != person);
person = placementNewPerson(person, name, age);
return person;
}
Person* placementNewPerson(Person* self, char* name, unsigned int age)
{
assert(NULL != self);
self->getAge = getAge;
self->getName = getName;
self->setAge = setAge;
self->setName = setName;
self->setName(self, name);
self->setAge(self, age);
return self;
}
void placementDeletePerson(Person* self)
{
self->setAge(self, 0);
self->setName(self, "");
self->getAge = NULL;
self->getName = NULL;
self->setAge = NULL;
self->setName = NULL;
}
void deletePerson(Person* self)
{
placementDeletePerson(self);
operatorDelete(self);
self = NULL;
}
子类Employee
派生类Employee的接口定义
#define MAX_DEPARTMENT_LENGTH 255
typedef struct Employee
{
Person person;
char department[MAX_DEPARTMENT_LENGTH];
void (*setDepartment)(struct Employee* self, char* department);
char* (*getDepartment)(struct Employee* self);
}Employee;
Employee* newEmployee(char* name, unsigned int age, char* department);
Employee* placementNewEmployee(Employee* self, char* name, unsigned int age, char* department);
void placementDeleteEmployee(Employee* self);
void deleteEmployee(Employee* self);
派生类Employee的具体实现
void setDepartment(Employee* self, char* department)
{
strcpy_s(self->department, MAX_DEPARTMENT_LENGTH, department);
}
char* getDepartment(Employee* self)
{
return self->department;
}
Employee* newEmployee(char* name, unsigned int age, char* department)
{
Employee* employee = (Employee*)operatorNew(sizeof(Employee));
assert(NULL != employee);
employee = placementNewEmployee(employee, name, age, department);
return employee;
}
Employee* placementNewEmployee(Employee* self, char* name, unsigned int age, char* department)
{
placementNewPerson(&self->person, name, age);
self->setDepartment = setDepartment;
self->getDepartment = getDepartment;
self->setDepartment(self, department);
return self;
}
void placementDeleteEmployee(Employee* self)
{
self->setDepartment(self, "");
self->setDepartment = NULL;
self->getDepartment = NULL;
placementDeletePerson(&self->person);
}
void deleteEmployee(Employee* self)
{
placementDeleteEmployee(self);
operatorDelete(self);
self = NULL;
}
辅助Util
辅助接口util.h实现
typedef void (*NewHandler)();
void setNewHandler(NewHandler newHandler);
NewHandler getNewHandler();
void* operatorNew(int size);
void operatorDelete(void * ptr);
辅助接口util.c实现
static NewHandler s_newHandler = NULL;
void setNewHandler(NewHandler newHandler)
{
s_newHandler = newHandler;
}
NewHandler getNewHandler()
{
return s_newHandler;
}
void* operatorNew(int size)
{
void *p = NULL;
if (size == 0)
{
size = 1;
}
while ((p = malloc(size)) == NULL)
{
NewHandler handler = getNewHandler();
if (NULL != handler)
{
handler();
}
}
return p;
}
void operatorDelete(void * ptr)
{
if (NULL != ptr)
{
free(ptr);
}
ptr = NULL;
}
应用举例
int main()
{
printf("Create Person(\"employee\", 35)\r\n");
Person* person = newPerson("person", 35);
printf("Person name = %s\r\n", person->name);
printf("Person age = %d\r\n\r\n", person->age);
printf("Create Employee(\"employee\", 35, \"CEO\")\r\n");
Employee* employee = newEmployee("employee", 35, "CEO");
printf("Employee name = %s\r\n", employee->person.name);
printf("Employee age = %d\r\n", employee->person.age);
printf("Employee department = %s\r\n\r\n", employee->department);
printf("Employee(\"employee\", 35, \"CEO\")\r\n");
Person* newPerson = (Person*)employee;
printf("Person name = %s\r\n", newPerson->name);
printf("Person age = %d\r\n\r\n", newPerson->age);
deletePerson(person);
deleteEmployee(employee);
printf("stEmployee(\"stEmployee\", 30, \"CFO\")\r\n");
Employee stEmployee;
placementNewEmployee(&stEmployee, "stEmployee", 30, "CFO");
printf("Employee name = %s\r\n", stEmployee.person.name);
printf("Employee age = %d\r\n", stEmployee.person.age);
printf("Employee department = %s\r\n\r\n", stEmployee.department);
Person* stPerson = (Person*)(&stEmployee);
printf("Employee name = %s\r\n", stPerson->name);
printf("Employee age = %d\r\n\r\n", stPerson->age);
placementDeleteEmployee(&stEmployee);
}
运行结果:
Create Person("employee", 35)
Person name = person
Person age = 35
Create Employee("employee", 35, "CEO")
Employee name = employee
Employee age = 35
Employee department = CEO
Employee("employee", 35, "CEO")
Person name = employee
Person age = 35
stEmployee("stEmployee", 30, "CFO")
Employee name = stEmployee
Employee age = 30
Employee department = CFO
Employee name = stEmployee
Employee age = 30
实现说明
根据上述实现,共有继承可总结如下:
- 子类Employee内部包含一个基类Person,而且Person必须为Employee的第一个成员变量。这是由内存中数据布局是由低字节向高字节布局而导致的。
- 子类Employee使用父类Person的数据时,必须采用employee.person.name模式,而不能直接采用employee.name这种方式。
持续改进
在应用举例中,子类Employee可直接访问父类Person数据成员name。从封装的角度,这是很不好的方式。因为一旦Person实现发生了变化,就会导致Employee的实现也要跟着调整,这违反了开闭原则。哪有没有办法限制子类对父类的成员变量和方法进行访问呢?
我们可以采用《C语言:对象封装》介绍的前向声明,限制子类对父类成员变量和方法的访问。
改进的Person
改进的Person基类接口:
struct PrivatePersonDesc;
struct PrivatePersonFuncs;
typedef struct Person
{
struct PrivatePersonDesc* personDesc;
struct PrivatePersonFuncs* personFuncs;
char* (*getName)(struct Person* self);
void (*setName)(struct Person* self, char* name);
unsigned int(*getAge)(struct Person* self);
void(*setAge)(struct Person* self, unsigned int age);
} Person;
Person* newPerson(char* name, unsigned int age);
Person* placementNewPerson(Person* self, char* name, unsigned int age);
void placementDeletePerson(Person* self);
void deletePerson(Person* self);
改进 的Person基类实现:
#define MAX_NAME_LENGTH 255
typedef struct PrivatePersonDesc
{
char name[MAX_NAME_LENGTH];
unsigned int age;
}PrivatePersonDesc;
typedef struct PrivatePersonFuncs
{
void (*init)(Person* self, char* name, unsigned int age);
void (*deinit)(Person* self);
}PrivatePersonFuncs;
static void initPerson(Person* self, char* name, unsigned int age)
{
self->personDesc = operatorNew(sizeof(PrivatePersonDesc));
assert(NULL != self->personDesc);
strcpy_s(self->personDesc->name, MAX_NAME_LENGTH, name);
self->personDesc->age = age;
}
static void deinitPerson(Person* self)
{
strcpy_s(self->personDesc->name, MAX_NAME_LENGTH, "");
self->personDesc->age = 0;
operatorDelete(self->personDesc);
self->personDesc = NULL;
}
char* getName(Person* self)
{
return self->personDesc->name;
}
void setName(Person* self, char* name)
{
strcpy_s(self->personDesc->name, MAX_NAME_LENGTH, name);
}
unsigned int getAge(Person* self)
{
return self->personDesc->age;
}
void setAge(Person* self, unsigned int age)
{
self->personDesc->age = age;
}
Person* newPerson(char* name, unsigned int age)
{
Person* person = (Person*)operatorNew(sizeof(Person));
assert(NULL != person);
person = placementNewPerson(person, name, age);
return person;
}
Person* placementNewPerson(Person* self, char* name, unsigned int age)
{
assert(NULL != self);
PrivatePersonFuncs* personFuncs = (PrivatePersonFuncs*)operatorNew(sizeof(PrivatePersonFuncs));
assert(NULL != personFuncs);
personFuncs->init = initPerson;
personFuncs->deinit = deinitPerson;
self->personFuncs = personFuncs;
self->getAge = getAge;
self->getName = getName;
self->setAge = setAge;
self->setName = setName;
personFuncs->init(self, name, age);
return self;
}
void placementDeletePerson(Person* self)
{
self->personFuncs->deinit(self);
self->getAge = NULL;
self->getName = NULL;
self->setAge = NULL;
self->setName = NULL;
operatorDelete(self->personFuncs);
}
void deletePerson(Person* self)
{
placementDeletePerson(self);
operatorDelete(self);
self = NULL;
}
改进的Employee
改进的Employee派生类接口:
struct PrivateEmployeeDesc;
struct PrivateEmployeeFuncs;
typedef struct Employee
{
Person person;
struct PrivateEmployeeDesc* employeeDesc;
struct PrivateEmployeeFuncs* employeeFuncs;
void (*setDepartment)(struct Employee* self, char* department);
char* (*getDepartment)(struct Employee* self);
}Employee;
Employee* newEmployee(char* name, unsigned int age, char* department);
Employee* placementNewEmployee(Employee* self, char* name, unsigned int age, char* department);
void placementDeleteEmployee(Employee* self);
void deleteEmployee(Employee* self);
改进的Employee派生类实现:
#define MAX_DEPARTMENT_LENGTH 255
typedef struct PrivateEmployeeDesc
{
char department[MAX_DEPARTMENT_LENGTH];
}PrivateEmployeeDesc;
typedef struct PrivateEmployeeFuncs
{
void(*init)(Employee* self, char* department);
void(*deinit)(Employee* self);
}PrivateEmployeeFuncs;
static void initEmployee(Employee* self, char* department)
{
self->employeeDesc = operatorNew(sizeof(PrivateEmployeeDesc));
assert(NULL != self->employeeDesc);
strcpy_s(self->employeeDesc->department, MAX_DEPARTMENT_LENGTH, department);
}
static void deinitEmployee(Employee* self)
{
strcpy_s(self->employeeDesc->department, MAX_DEPARTMENT_LENGTH, "");
operatorDelete(self->employeeDesc);
self->employeeDesc = NULL;
}
void setDepartment(Employee* self, char* department)
{
strcpy_s(self->employeeDesc->department, MAX_DEPARTMENT_LENGTH, department);
}
char* getDepartment(Employee* self)
{
return self->employeeDesc->department;
}
Employee* newEmployee(char* name, unsigned int age, char* department)
{
Employee* employee = (Employee*)operatorNew(sizeof(Employee));
assert(NULL != employee);
employee = placementNewEmployee(employee, name, age, department);
return employee;
}
Employee* placementNewEmployee(Employee* self, char* name, unsigned int age, char* department)
{
placementNewPerson(&self->person, name, age);
PrivateEmployeeFuncs* employeeFuncs = (PrivateEmployeeFuncs*)operatorNew(sizeof(PrivateEmployeeFuncs));
assert(NULL != employeeFuncs);
employeeFuncs->init = initEmployee;
employeeFuncs->deinit = deinitEmployee;
self->employeeFuncs = employeeFuncs;
self->setDepartment = setDepartment;
self->getDepartment = getDepartment;
employeeFuncs->init(self, department);
return self;
}
void placementDeleteEmployee(Employee* self)
{
self->employeeFuncs->deinit(self);
operatorDelete(self->employeeFuncs);
placementDeletePerson(&self->person);
}
void deleteEmployee(Employee* self)
{
placementDeleteEmployee(self);
operatorDelete(self);
self = NULL;
}
应用举例
int main()
{
printf("Create Person(\"employee\", 35)\r\n");
Person* person = newPerson("person", 35);
printf("Person name = %s\r\n", person->getName(person));
printf("Person age = %d\r\n\r\n", person->getAge(person));
printf("Create Employee(\"employee\", 35, \"CEO\")\r\n");
Employee* employee = newEmployee("employee", 35, "CEO");
printf("Employee name = %s\r\n", employee->person.getName(&employee->person));
printf("Employee age = %d\r\n", employee->person.getAge(&employee->person));
printf("Employee department = %s\r\n\r\n", employee->getDepartment(employee));
printf("Employee(\"employee\", 35, \"CEO\")\r\n");
Person* newPerson = (Person*)employee;
printf("Person name = %s\r\n", newPerson->getName(newPerson));
printf("Person age = %d\r\n\r\n", newPerson->getAge(newPerson));
deletePerson(person);
deleteEmployee(employee);
printf("stEmployee(\"stEmployee\", 30, \"CFO\")\r\n");
Employee stEmployee;
placementNewEmployee(&stEmployee, "stEmployee", 30, "CFO");
printf("Employee name = %s\r\n", stEmployee.person.getName(&(stEmployee.person)));
printf("Employee age = %d\r\n", stEmployee.person.getAge(&(stEmployee.person)));
printf("Employee department = %s\r\n\r\n", stEmployee.getDepartment(&stEmployee));
Person* stPerson = (Person*)(&stEmployee);
printf("Employee name = %s\r\n", stPerson->getName(stPerson));
printf("Employee age = %d\r\n\r\n", stPerson->getAge(stPerson));
placementDeleteEmployee(&stEmployee);
}
运行结果:
Create Person("employee", 35)
Person name = person
Person age = 35
Create Employee("employee", 35, "CEO")
Employee name = employee
Employee age = 35
Employee department = CEO
Employee("employee", 35, "CEO")
Person name = employee
Person age = 35
stEmployee("stEmployee", 30, "CFO")
Employee name = stEmployee
Employee age = 30
Employee department = CFO
实现说明
- C语言提供了前置声明的语法规则,我们可通过Pimpl前置声明惯用手法实现数据和成员函数的隐藏。
- struct PrivatePersonDesc前置声明,实现了Person的name和aget成员变量的隐藏;struct PrivatePersonFuncs前置声明,实现了Person的init和deinit成员函数的隐藏。Employee和Person的实现是同一原理,PrivateEmployeeDesc和PrivateEmployeeFuncs前置声明实现了Employee的成员变量和成员函数的隐藏。
- 采用这种改进的方式,我们实现了数据和成员函数的隐藏,但是在函数调用时employee->person.getName(&employee->person)会显得有些别扭。这一问题我们会在《C语言:多态》中给出解决方案。
总结
本博客,从C++的3个new和2个delete入手(3个new分别是operator new, new operator和placement new;2个delete分别是 operator delete和delete operator),实现了一种类C++的共有继承,同时实现了成员变量和成员函数的隐藏。希望可以对你有所帮助。
|