代码大全C语言如何使用ADT
面向对象的编程语言能自动支持对同一 ADT的多份实例的处理。如果你只是在面向对象的环境中工作,那你根本就不用自己操心处理多个实例的实现细节了恭喜你(你可以直接去读下一节“ADT和类”。)!
如果你是在像C语言这样的非面向对象的环境中工作,你就必须自己手工实现支持处理多个实例的技术。一般来说,这就意味着你要为ADT添加一些用来创建和删除实例的服务操作,同时需要重新设计 ADT的其他服务操作,使其能够支持多个实例。
前面字体那个 ADT 原来只是提供这些操作:
currentFont.getSize(sizeInPoints)
currentFont.SetBoldOn()
currentFont.SetBoldoff()
currentFont.setitalicOn()
currentFont.SetItalicOff()
currentFont,SetTypeFace(faceName)
在非面问对象的环境里,这些操作不能附着在某个类上,因此很可能要写成
SetCurrentFontSize(sizeInPoints)
SetCurrentFontBold0n()
SetCurrentFontBoldOff()
SetCurrentFontItalicOn()
SetCurrentFontItalic0ff()
SetCurrentFontTypeFace(faceName)
如果你想一次使用更多的字体,那么就需要增加一些服务操作来创建和删除字体的实例了,比如说这样:
CreateFont(fontId)
DeleteFont(fontId)
SetCurrentFont(fontId)
这里引入了一个fontIa变量,这是用来在创建和使用多个字体实例时分别控制每个实例的一种处理方法。对于其他操作,你可以采用下列三种方法之一对ADT的接口进行处理:
做法1:每次使用 ADT 服务子程序时都明确地指明实例。在这种情况下没有“当前字体”的概念。你把 foncia传给每个用来操作字体的子程序。FontADT的服务子程序负责跟踪所有底层的数据,而调用方代码只需使用不同的fontId即可区分多份实例。这种方法需要为每个Font 子程序都加上一个 fontid 参数。
在 C 语言中,实现第一种方法,即每次使用 ADT 服务子程序时都明确指定实例,可以通过为每个操作函数添加一个 `fontId` 参数来实现。这里,`fontId` 可以是一个索引值,用于在内部数据结构(如数组或链表)中查找对应的字体实例。 以下是一个简单的示例,展示了如何实现这种设计模式: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_FONTS 10 // 假设我们只支持10个字体实例 // 定义字体结构体 typedef struct { int size; int bold; int italic; char *typeface; } Font; // 定义字体管理器结构体 typedef struct { Font fonts[MAX_FONTS]; // 存储字体实例的数组 int count; // 当前创建的字体实例数量 } FontManager; // 函数声明 int createFont(FontManager *manager, const char *typeface, int size); void setFontStyle(FontManager *manager, int fontId, int bold, int italic); void setFontSize(FontManager *manager, int fontId, int size); void printFontInfo(FontManager *manager, int fontId); void deleteFont(FontManager *manager, int fontId); // 示例使用 ADT int main() { FontManager manager = {0}; // 创建几个字体实例 int fontId1 = createFont(&manager, "Arial", 12); int fontId2 = createFont(&manager, "Times New Roman", 14); // 修改字体样式和大小 setFontStyle(&manager, fontId1, 1, 0); // fontId1 设置为粗体 setFontSize(&manager, fontId2, 16); // fontId2 设置为更大的字号 // 打印字体信息 printFontInfo(&manager, fontId1); printFontInfo(&manager, fontId2); // 清理资源 deleteFont(&manager, fontId1); deleteFont(&manager, fontId2); return 0; } // 函数定义 int createFont(FontManager *manager, const char *typeface, int size) { if (manager->count >= MAX_FONTS) { return -1; // 没有足够的空间创建新的字体实例 } Font *newFont = &manager->fonts[manager->count++]; newFont->size = size; newFont->bold = 0; newFont->italic = 0; newFont->typeface = strdup(typeface); // 复制字符串 return manager->count - 1; // 返回新创建的字体实例的 ID } // 其他函数的定义与 createFont 类似,使用 fontId 来访问特定的字体实例 // 注意:你需要实现 setFontStyle、setFontSize、printFontInfo 和 deleteFont 函数 // 它们都将使用 fontId 来访问和管理特定的字体实例。 ``` 在这个示例中: - 我们定义了一个 `Font` 结构体来存储字体属性。 - 我们定义了一个 `FontManager` 结构体来管理多个 `Font` 实例。它包含一个 `Font` 数组和一个计数器来跟踪当前创建的实例数量。 - 我们声明了一系列函数来创建、修改、打印和删除字体实例。这些函数都接受一个 `FontManager` 指针和一个 `fontId` 作为参数。 - 在 `main` 函数中,我们创建了一个 `FontManager` 实例,并使用 ADT 的服务子程序来操作 `Font` 实例。 这种方法的优点是它提供了清晰的实例化管理,每个操作都明确指定了实例,没有隐含的“当前字体”概念。这使得代码更加明确和易于理解。然而,它的缺点是需要为每个操作传递 `fontId`,可能会增加代码的复杂性。此外,如果字体实例很多,管理 `fontId` 本身也可能变得复杂。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_FONTS 10 // 假设我们只支持10个字体实例 // 定义字体结构体 typedef struct { int size; int bold; int italic; char *typeface; } Font; // 定义字体管理器结构体 typedef struct { Font fonts[MAX_FONTS]; // 存储字体实例的数组 int count; // 当前创建的字体实例数量 } FontManager; // 函数声明 int createFont(FontManager *manager, const char *typeface, int size); void setFontStyle(FontManager *manager, int fontId, int bold, int italic); void setFontSize(FontManager *manager, int fontId, int size); void printFontInfo(FontManager *manager, int fontId); void deleteFont(FontManager *manager, int fontId); // 示例使用ADT int main() { FontManager manager = {0}; // 创建几个字体实例 int fontId1 = createFont(&manager, "Arial", 12); int fontId2 = createFont(&manager, "Times New Roman", 14); // 修改字体样式和大小 setFontStyle(&manager, fontId1, 1, 0); setFontSize(&manager, fontId2, 16); // 打印字体信息 printFontInfo(&manager, fontId1); printFontInfo(&manager, fontId2); // 删除字体实例 deleteFont(&manager, fontId1); return 0; } // 函数定义 int createFont(FontManager *manager, const char *typeface, int size) { if (manager->count >= MAX_FONTS) { return -1; // 超过最大字体实例数量 } int fontId = manager->count++; manager->fonts[fontId].size = size; manager->fonts[fontId].bold = 0; manager->fonts[fontId].italic = 0; manager->fonts[fontId].typeface = strdup(typeface); return fontId; } void setFontStyle(FontManager *manager, int fontId, int bold, int italic) { if (fontId < 0 || fontId >= manager->count) { return; // 无效的fontId } manager->fonts[fontId].bold = bold; manager->fonts[fontId].italic = italic; } void setFontSize(FontManager *manager, int fontId, int size) { if (fontId < 0 || fontId >= manager->count) { return; // 无效的fontId } manager->fonts[fontId].size = size; } void printFontInfo(FontManager *manager, int fontId) { if (fontId < 0 || fontId >= manager->count) { return; // 无效的fontId } Font *font = &manager->fonts[fontId]; printf("Font ID: %d\n", fontId); printf("Size: %d\n", font->size); printf("Bold: %d\n", font->bold); printf("Italic: %d\n", font->italic); printf("Typeface: %s\n", font->typeface); } void deleteFont(FontManager *manager, int fontId) { if (fontId < 0 || fontId >= manager->count) { return; // 无效的fontId } free(manager->fonts[fontId].typeface); // 将最后一个字体实例移到被删除的位置 manager->fonts[fontId] = manager->fonts[--manager->count]; }
做法2:明确地向ADT服务子程序提供所要用到的数据。采用这种方法时你要在调用 ADT服务的子程序里声明一个该ADT所要用到的数据。换句话说,你要声明一个 Font 数据类型,并把它传给 ADT 中的每一个服务子程序。你在设计时必须要让 ADT的每个服务子程序在被调用时都使用这个传入的Font 数据类型。用这种方法时,调用方代码无须使用ontId,因为它总是自己跟踪字体数据。(虽然从 Font数据类型即可直接取得所有数据,但你仍然应该仅通过 ADT的服务子程序来访问它。这称为保持结构体“封闭”。) 这种方法的优点是,ADT中的服务子程序不需要根据fontId来查询字体的信息。而它的缺点则是向程序的其余部分暴露了字体内部的数据,从而增加了调用方代码可能利用 ADT内部实现细节的可能性,而这些细节本应该隐藏在 ADT 的内部。
在 C 语言中,实现一个抽象数据类型(ADT)通常意味着要定义一组函数来操作一个结构体,同时隐藏结构体的内部实现细节。这种方法可以提高代码的封装性和可维护性。以下是一个示例,展示了如何根据您描述的方法实现一个简单的 `Font` ADT: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义字体结构体 typedef struct { int size; int bold; int italic; char *typeface; } Font; // 声明 ADT 服务子程序 void createFont(Font **font, const char *typeface, int size); void setFontStyle(Font *font, int bold, int italic); void setFontSize(Font *font, int size); void printFontInfo(Font *font); void deleteFont(Font *font); // 示例使用 ADT int main() { Font *myFont = NULL; // 使用 ADT 创建字体实例 createFont(&myFont, "Times New Roman", 12); // 使用 ADT 设置字体样式和大小 setFontStyle(myFont, 1, 0); // 粗体,非斜体 setFontSize(myFont, 14); // 使用 ADT 打印字体信息 printFontInfo(myFont); // 清理资源 deleteFont(myFont); return 0; } // 实现 ADT 服务子程序 void createFont(Font **font, const char *typeface, int size) { *font = (Font *)malloc(sizeof(Font)); if (*font) { (*font)->size = size; (*font)->bold = 0; (*font)->italic = 0; (*font)->typeface = strdup(typeface); } } void setFontStyle(Font *font, int bold, int italic) { if (font) { font->bold = bold; font->italic = italic; } } void setFontSize(Font *font, int size) { if (font) { font->size = size; } } void printFontInfo(Font *font) { if (font) { printf("Font: %s, Size: %d", font->typeface, font->size); if (font->bold) { printf(", Bold: Yes"); } else { printf(", Bold: No"); } if (font->italic) { printf(", Italic: Yes\n"); } else { printf(", Italic: No\n"); } } } void deleteFont(Font *font) { if (font) { free(font->typeface); free(font); } } ``` 在这个示例中: - 我们定义了一个 `Font` 结构体来存储字体的属性。 - 我们声明了一系列函数来创建、修改、打印和删除字体实例。这些函数作为 ADT 的服务子程序。 - 在 `main` 函数中,我们通过 ADT 的服务子程序来操作 `Font` 实例,而不是直接操作 `Font` 结构体的字段。 - 我们使用 `createFont` 函数来创建一个新的 `Font` 实例,并将其地址传递给 ADT 的其他服务子程序。 - 我们使用 `setFontStyle` 和 `setFontSize` 函数来修改字体的样式和大小。 - 我们使用 `printFontInfo` 函数来打印当前字体的详细信息。 - 最后,我们使用 `deleteFont` 函数来释放 `Font` 实例占用的资源。 这种方法的优点是 ADT 的服务子程序不需要根据 `fontId` 来查询字体信息,因为它们直接操作传递给它们的 `Font` 实例。然而,缺点是它可能会暴露 `Font` 结构体的内部数据,增加了调用方代码利用 ADT 内部实现细节的可能性。为了保持封装性,我们应该通过 ADT 的服务子程序来访问和修改 `Font` 数据,而不是直接访问结构体字段。
#include <stdio.h> #include <stdlib.h> #include <string.h> // 定义字体结构体 typedef struct { int size; int bold; int italic; char *typeface; } Font; // 声明 ADT 服务子程序 void createFont(Font **font, const char *typeface, int size); void setFontStyle(Font *font, int bold, int italic); void setFontSize(Font *font, int size); void printFontInfo(Font *font); void deleteFont(Font *font); // 示例使用 ADT int main() { Font *myFont = NULL; // 使用 ADT 创建字体实例 createFont(&myFont, "Times New Roman", 12); // 使用 ADT 设置字体样式和大小 setFontStyle(myFont, 1, 0); // 粗体,非斜体 setFontSize(myFont, 14); // 使用 ADT 打印字体信息 printFontInfo(myFont); // 清理资源 deleteFont(myFont); return 0; } // 实现 ADT 服务子程序 void createFont(Font **font, const char *typeface, int size) { *font = (Font *)malloc(sizeof(Font)); if (*font) { (*font)->size = size; (*font)->bold = 0; (*font)->italic = 0; (*font)->typeface = strdup(typeface); } } void setFontStyle(Font *font, int bold, int italic) { if (font) { font->bold = bold; font->italic = italic; } } void setFontSize(Font *font, int size) { if (font) { font->size = size; } } void printFontInfo(Font *font) { if (font) { printf("Font: %s, Size: %d", font->typeface, font->size); if (font->bold) { printf(", Bold: Yes"); } else { printf(", Bold: No"); } if (font->italic) { printf(", Italic: Yes"); } else { printf(", Italic: No"); } printf("\n"); } } void deleteFont(Font *font) { if (font) { free(font->typeface); free(font); } }
做法3::使用隐含实例(需要倍加小心)。设计一个新的服务子程序,通过调用它来让某一个特定的字体实例成为当前实例--比方说SetCurrentFont(fontId)。一旦设置了当前字体,其他所有服务子程序在被调用时都会使用这个当前字体。用这种方法也无须为其他服务子程序添加fontrā 参数。对于简单的应用程序而言,这么做可以让使用多个实例更为顺畅。然而对于复杂的应用程序来说,这种在系统范围内对状态的依赖性就 意味着,你必须在用到字体操作的所有代码中跟踪当前的字体实例。这样一来,复杂度有可能会急剧增长,对于任何规模的应用程序来说,还有一些更好的替代方案。 在抽象数据类型的内部,你还可以选择更多处理多个实例的方法:但在抽象数据类型的外部,如果你使用非面向对象的编程语言,能选择的方法也就是这些了。
在非面向对象的编程语言中,如 C 语言,管理具有隐含实例的抽象数据类型(ADT)需要谨慎设计,以避免全局状态带来的复杂性和潜在的错误。以下是一个简单的示例,演示如何在 C 语言中实现一个具有隐含实例的字体管理系统: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义字体结构体 typedef struct { int size; int bold; int italic; char *typeface; } Font; // 定义管理字体的 ADT 结构体 typedef struct { Font *currentFont; // 指向当前字体实例 } FontManager; // 初始化字体管理器 void initFontManager(FontManager *manager) { manager->currentFont = NULL; } // 创建字体实例 Font* createFont(const char *typeface, int size, int bold, int italic) { Font *newFont = (Font*)malloc(sizeof(Font)); if (newFont) { newFont->typeface = strdup(typeface); newFont->size = size; newFont->bold = bold; newFont->italic = italic; } return newFont; } // 设置当前字体实例 void setCurrentFont(FontManager *manager, Font *font) { manager->currentFont = font; } // 打印当前字体信息 void printCurrentFontInfo(FontManager *manager) { if (manager->currentFont) { printf("Current Font: %s, Size: %d", manager->currentFont->typeface, manager->currentFont->size); if (manager->currentFont->bold) { printf(", Bold: ON"); } else { printf(", Bold: OFF"); } if (manager->currentFont->italic) { printf(", Italic: ON\n"); } else { printf(", Italic: OFF\n"); } } else { printf("No current font set.\n"); } } // 释放字体资源 void deleteFont(Font *font) { if (font) { free(font->typeface); free(font); } } // 销毁字体管理器,并释放当前字体 void destroyFontManager(FontManager *manager) { deleteFont(manager->currentFont); manager->currentFont = NULL; } int main() { FontManager manager; initFontManager(&manager); // 创建并设置当前字体 Font *myFont = createFont("Arial", 12, 0, 0); setCurrentFont(&manager, myFont); printCurrentFontInfo(&manager); // 修改当前字体的属性 manager.currentFont->size = 14; manager.currentFont->bold = 1; manager.currentFont->italic = 1; // 打印修改后的当前字体信息 printf("After modifications:\n"); printCurrentFontInfo(&manager); // 销毁字体管理器 destroyFontManager(&manager); return 0; } ``` 在这个示例中: - 我们定义了一个 `Font` 结构体来存储字体属性。 - `FontManager` 结构体包含一个指向当前字体实例的指针。 - `initFontManager` 函数用于初始化字体管理器。 - `createFont` 函数用于创建新的字体实例。 - `setCurrentFont` 函数用于设置当前字体实例。 - `printCurrentFontInfo` 函数用于打印当前字体的详细信息。 - `deleteFont` 函数用于释放单个字体实例的资源。 - `destroyFontManager` 函数用于销毁字体管理器并释放当前字体实例。 这种设计允许我们通过 `FontManager` 来管理字体实例,同时避免了在每个函数中传递 `fontId`。然而,正如您所提到的,这种方法需要小心使用,因为它依赖于全局或管理器级别的状态。在复杂的应用程序中,这可能会导致状态管理变得复杂和难以跟踪。因此,应当谨慎考虑是否使用这种方法,并在必要时寻求替代方案。
做法3::使用隐含实例(需要倍加小心)。设计一个新的服务子程序,通过调用它来让某一个特定的字体实例成为当前实例--比方说SetCurrentFont(fontId)。一旦设置了当前字体,其他所有服务子程序在被调用时都会使用这个当前字体。用这种方法也无须为其他服务子程序添加fontrā 参数。对于简单的应用程序而言,这么做可以让使用多个实例更为顺畅。然而对于复杂的应用程序来说,这种在系统范围内对状态的依赖性就 意味着,你必须在用到字体操作的所有代码中跟踪当前的字体实例。这样一来,复杂度有可能会急剧增长,对于任何规模的应用程序来说,还有一些更好的替代方案。 在抽象数据类型的内部,你还可以选择更多处理多个实例的方法:但在抽象数据类型的外部,如果你使用非面向对象的编程语言,能选择的方法也就是这些了。 在非面向对象的编程语言中,如 C 语言,管理具有隐含实例的抽象数据类型(ADT)需要谨慎设计,以避免全局状态带来的复杂性和潜在的错误。以下是一个简单的示例,演示如何在 C 语言中实现一个具有隐含实例的字体管理系统: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义字体结构体 typedef struct { int size; int bold; int italic; char *typeface; } Font; // 定义管理字体的 ADT 结构体 typedef struct { Font *currentFont; // 指向当前字体实例 } FontManager; // 初始化字体管理器 void initFontManager(FontManager *manager) { manager->currentFont = NULL; } // 创建字体实例 Font* createFont(const char *typeface, int size, int bold, int italic) { Font *newFont = (Font*)malloc(sizeof(Font)); if (newFont) { newFont->typeface = strdup(typeface); newFont->size = size; newFont->bold = bold; newFont->italic = italic; } return newFont; } // 设置当前字体实例 void setCurrentFont(FontManager *manager, Font *font) { manager->currentFont = font; } // 打印当前字体信息 void printCurrentFontInfo(FontManager *manager) { if (manager->currentFont) { printf("Current Font: %s, Size: %d", manager->currentFont->typeface, manager->currentFont->size); if (manager->currentFont->bold) { printf(", Bold: ON"); } else { printf(", Bold: OFF"); } if (manager->currentFont->italic) { printf(", Italic: ON\n"); } else { printf(", Italic: OFF\n"); } } else { printf("No current font set.\n"); } } // 释放字体资源 void deleteFont(Font *font) { if (font) { free(font->typeface); free(font); } } // 销毁字体管理器,并释放当前字体 void destroyFontManager(FontManager *manager) { deleteFont(manager->currentFont); manager->currentFont = NULL; } int main() { FontManager manager; initFontManager(&manager); // 创建并设置当前字体 Font *myFont = createFont("Arial", 12, 0, 0); setCurrentFont(&manager, myFont); printCurrentFontInfo(&manager); // 修改当前字体的属性 manager.currentFont->size = 14; manager.currentFont->bold = 1; manager.currentFont->italic = 1; // 打印修改后的当前字体信息 printf("After modifications:\n"); printCurrentFontInfo(&manager); // 销毁字体管理器 destroyFontManager(&manager); return 0; } ``` 在这个示例中: - 我们定义了一个 `Font` 结构体来存储字体属性。 - `FontManager` 结构体包含一个指向当前字体实例的指针。 - `initFontManager` 函数用于初始化字体管理器。 - `createFont` 函数用于创建新的字体实例。 - `setCurrentFont` 函数用于设置当前字体实例。 - `printCurrentFontInfo` 函数用于打印当前字体的详细信息。 - `deleteFont` 函数用于释放单个字体实例的资源。 - `destroyFontManager` 函数用于销毁字体管理器并释放当前字体实例。 这种设计允许我们通过 `FontManager` 来管理字体实例,同时避免了在每个函数中传递 `fontId`。然而,正如您所提到的,这种方法需要小心使用,因为它依赖于全局或管理器级别的状态。在复杂的应用程序中,这可能会导致状态管理变得复杂和难以跟踪。因此,应当谨慎考虑是否使用这种方法,并在必要时寻求替代方案。
本文来自博客园,作者:易先讯,转载请注明原文链接:https://www.cnblogs.com/gongxianjin/p/18334737