IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言:继承(一) -> 正文阅读

[C++知识库]C语言:继承(一)

引言

继承是面向对象的另一个重要特性,是代码可复用的重要手段。通过继承派生类可以拥有其他类的属性和方法,我们称派生类为子类,而这个“其他类”我们称之为基类。继承的方式有三种,他们分别是共有(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;
	 // 这里保证像 new T[0] 这样得语句也是可行的 
	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的接口定义:

// person.h
#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;
}

// 对象的new operator:
//      (1) operatorNew申请内存,
//      (2) placementNew实现Person struct初始化
Person* newPerson(char* name, unsigned int age)
{
	Person* person = (Person*)operatorNew(sizeof(Person));
	assert(NULL != person);

	person = placementNewPerson(person, name, age);

	return person;
}

// 对象的placement new:
//      (1) 成员函数初始化
//      (2) 成员变量初始化
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;
}

// 对象的placement delete:
//      (1) 成员变量反初始化
//      (2) 成员函数反初始化
void placementDeletePerson(Person* self)
{
	self->setAge(self, 0);
	self->setName(self, "");

	self->getAge = NULL;
	self->getName = NULL;
	self->setAge = NULL;
	self->setName = NULL;
}

// 对象的delete operator:
//      (1) placementDelete反初始化struct person
//      (2) operatorDelete实现内存释放
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的new operator:
//      (1) operatorNew申请内存,
//      (2) placementNew实现struct Employee初始化
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对象的placement new:
//      (1) 调用基类Person的placement new,完成基类Person的初始化
//      (2) 初始化子类成员函数
//      (2) 初始化子类成员变量
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;
}

// Employee对象的placement delete:
//      (1) 子类成员变量反初始化
//      (2) 子类成员函数反初始化
//      (3) 调用基类Person的placement delete,基类Person的反初始化
void placementDeleteEmployee(Employee* self)
{
	self->setDepartment(self, "");
	self->setDepartment = NULL;
	self->getDepartment = NULL;

	placementDeletePerson(&self->person);
}

// 对象Employee的delete operator:
//      (1) placementDelete反初始化struct Employee
//      (2) operatorDelete实现内存释放
void deleteEmployee(Employee* self)
{
	placementDeleteEmployee(self);
	operatorDelete(self);

	self = NULL;
}

辅助Util

辅助接口util.h实现

// 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;
}

// operate new 实现
void* operatorNew(int size)
{
	void *p = NULL;
	// 这里保证像 new T[0] 这样得语句也是可行的
	if (size == 0)
	{
		size = 1;
	}

	while ((p = malloc(size)) == NULL)
	{
		NewHandler handler = getNewHandler();
		if (NULL != handler) 
		{
			handler();
		}
	}

	return p;
}

// operate delete 实现
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++的共有继承,同时实现了成员变量和成员函数的隐藏。希望可以对你有所帮助。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-07 11:49:05  更:2021-08-07 11:49:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/9 20:15:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码