[NKU]C++理论课cours3数据抽象(封装->隐藏实现的手段,隐藏->封装的重要目标)
- 人工智能
- 2025-08-24 09:09:01
![[NKU]C++理论课cours3数据抽象(封装->隐藏实现的手段,隐藏->封装的重要目标)](/0pic/pp_63.jpg)
page 1 数据抽象 隐藏实现
Chapter 4: Data Abstraction Improvements of C++ Size of an object Inclusion guard Nested Structure Chapter 5: Hiding the implementation Access control: public, private, friends Declaring a nested structure as friend Object layout The keyword class Application of the access control Hiding the implementation
第4章:数据抽象C++ 的改进 对象的大小 sizeof 运算符可用于获取对象的大小。 包含防护(头文件保护). 为了避免头文件的重复包含,C++ 使用包含防护机制(如 #ifndef、#define、#endif)。 嵌套结构 在 C++ 中,可以在一个结构体或类中定义另一个结构体或类,这种结构称为嵌套结构。嵌套结构可以访问外部结构的成员。
第5章:隐藏实现访问控制:public、private、friend 将嵌套结构声明为友元 可以将嵌套结构声明为友元,使其能够访问外部类的私有成员。 对象布局 class 关键字 class 是 C++ 中用于定义类的关键字。与 struct 不同,默认情况下,class 的成员是私有的,而 struct 的成员是公有的。 访问控制的应用 通过合理使用访问控制,可以隐藏类的实现细节,只暴露必要的接口。这有助于提高代码的封装性和可维护性。 隐藏实现 隐藏实现是数据抽象的核心思想之一。通过将类的实现细节隐藏在私有成员中,只通过公共接口与外部交互,可以减少依赖关系,提高代码的灵活性和安全性。
封装 是一种更广泛的概念,指的是将数据和行为组合在一起,并通过访问控制隐藏数据。
隐藏实现 是封装的一个重要目标,更侧重于隐藏类的内部实现细节,只暴露必要的接口。
在实际编程中,封装是实现隐藏实现的手段,而隐藏实现是封装的一个重要目标。 通过封装,可以实现隐藏实现,从而提高代码的安全性、可维护性和复用性。
page 2
main objectives Object = characteristic + behavior C++ advantages against C
How to organize header files what should be put into a header file how to avoid multiple declaration how to use the header file
主要目标
对象=特征+行为
c++相对于C的优势
如何组织头文件
头文件里应该放些什么
如何避免多次申报
如何使用头文件
Main Objectives(主要目标) 数据抽象的主要目标:隐藏复杂性:将复杂的实现细节隐藏起来,只暴露简单的接口。
提供简洁的接口:通过封装数据和行为,提供易于使用的接口。
增强代码复用性:通过封装和抽象,创建通用的类库,便于在不同项目中复用。
提高代码安全性:隐藏内部实现细节,防止外部代码直接访问或修改内部数据,避免数据被滥用。
便于维护和扩展:由于内部实现对外隐藏,修改内部实现不会影响到使用该类的代码,只要接口保持不变。
Object = Characteristic + Behavior(对象 = 特征 + 行为) 对象的定义:
对象(Object) 是面向对象编程中的基本单位,它封装了数据和操作这些数据的方法。
特征(Characteristic):对象的状态,通常用成员变量(属性)表示。
行为(Behavior):对象的行为,通常用成员函数(方法)表示。
page 3 动态数组
dynamic array 动态数组 storage 存储 size 大小 next 下一个 quantity 数量 functionality: add, fetch, inflate 功能:添加,获取,膨胀
1. 库的设计原则封装性:将动态数组的实现细节隐藏起来,只暴露必要的接口(如add、fetch)。
接口设计:提供简洁的接口,让用户可以方便地使用动态数组,而不需要了解内部实现。
模块化:将动态数组的功能划分为不同的模块(如初始化、添加元素add、获取元素fetch、扩展容量inflate等)。
2. 动态数组的实现思路动态内存管理:使用malloc、realloc和free等函数动态分配和管理内存。
扩展性:当数组容量不足时,通过inflate函数扩展存储容量。
用户友好性:提供简单的接口(如add、fetch),让用户可以方便地操作动态数组。
malloc全称:Memory allocation(内存分配)
功能:分配一块指定大小的内存,并返回指向这块内存的指针。
原型:
c复制
void* malloc(size_t size);说明:
size 是需要分配的内存大小(以字节为单位)。
返回值是一个 void* 类型的指针,指向分配的内存块的起始位置。
如果分配失败,返回 NULL。
分配的内存内容是未初始化的,可能包含任意值。
2. realloc全称:Resize allocation(重新分配内存)
功能:调整已分配内存块的大小。如果需要更大的内存块,可能会移动内存块的位置。
原型:
c复制
void* realloc(void* ptr, size_t new_size);page4-9 动态数组
CLib.h
//CLib.h typedef struct CStashTag { int size; // Size of each space int quantity; // Number of storage spaces int next; // Next empty space // Dynamically allocated array of bytes: unsigned char* storage; } CStash; // a place to hide something. // } CStash;:在C语言中,CStash 是结构体的标签,为结构体定义了一个新的类型名。 void initialize (CStash* s, int size ); void cleanup (CStash* s ); int add (CStash* s, const void* element); void* fetch (CStash* s, int index ); int count (CStash* s ); void inflate (CStash* s, int increase ); 为什么 CStash 使用了 typedef?这主要是因为:
历史原因:
typedef struct 是C语言中的传统用法,C语言中必须使用 typedef 来简化结构体的声明。
C++继承了C语言的语法,但C++提供了更灵活的语法,C++允许直接使用结构体名。
风格和习惯:
在C语言中,typedef struct 是常见的写法。
在C++中,通常不使用 typedef,而是直接使用结构体名。这种方式更简洁,也更符合C++的语法风格。
这里的语法结构可以分解为以下几个部分:
typedef 关键字:用于创建类型别名。struct CStashTag:定义了一个结构体类型,其标签为 CStashTag。{ ... }:结构体成员的定义。CStash:通过 typedef,CStash 成为了 struct CStashTag 的别名。因此,在定义了这个结构体之后,你可以直接使用 CStash 来声明该类型的变量,而不需要再使用 struct CStashTag。例如:
CLib.CPP
//CLib.CPP #include "CLib.h" #include <iostream> #include <cassert> using namespace std; // Quantity of elements to add when increasing storage const int increment = 100; void initialize(CStash* s, int sz) { s->size = sz; // 设置每个存储单元的大小 s->quantity = 0; // 初始化存储容量为 0(表示没有分配内存) s->storage = 0; // 初始化存储指针为 NULL(表示没有分配内存) s->next = 0; // 初始化下一个可用位置为 0 } // // 初始化intStash initialize(&intStash, sizeof(int)); int add(CStash* s, const void* element) { if(s->next >= s->quantity) //Enough space left? inflate(s, increment); // Copy element into storage, // starting at next empty space: int startBytes = s->next * s->size; unsigned char* e = (unsigned char*)element; for(int i = 0; i < s->size; i++) s->storage[startBytes + i] = e[i]; s->next++; return(s->next - 1); // Index number } // to be continued void* fetch(CStash* s, int index) { // Check index boundaries: assert(0 <= index); if(index >= s->next) return 0; // To indicate the end // Produce pointer to desired element: return &(s->storage[index * s->size]); } int count(CStash* s) { return s->next; // Elements in CStash } // to be continued void inflate(CStash* s, int increase) { assert(increase > 0); int newQuantity = s->quantity + increase; int newBytes = newQuantity * s->size; int oldBytes = s->quantity * s->size; unsigned char* b = new unsigned char[newBytes]; for(int i = 0; i < oldBytes; i++) b[i] = s->storage[ i ]; // Copy old to new delete [ ](s->storage); // Old storage s->storage = b; // Point to new memory s->quantity = newQuantity; } // to be continued void cleanup(CStash* s) { if(s->storage != 0) { cout << "freeing storage" << endl; delete [ ]s->storage; } }如果你有一个结构体变量(而不是指针),你可以使用点操作符 (.) 来访问其成员。例如,如果 stash 是一个 CStash 类型的变量,那么你可以通过 stash.quantity 来访问 quantity 成员。
如果你有一个指向结构体的指针,你应该使用箭头操作符 (->) 来访问其成员。在你的例子中,s 是一个指向 CStash 的指针,所以 s->quantity 是正确的访问方式。 举例如下 (.)操作符访问结构体成员
#include <stdio.h> typedef struct { int quantity; } CStash; int main() { CStash stash; // 创建一个结构体变量 stash.quantity = 5; // 使用点操作符访问并修改成员 printf("stash.quantity = %d\n", stash.quantity); // 输出成员的值 return 0; }(->)操作符访问结构体成员
#include <stdio.h> typedef struct { int quantity; } CStash; int main() { CStash stashes[2]; // 创建一个结构体数组 stashes[0].quantity = 10; // 直接访问并修改数组第一个元素的成员 stashes[1].quantity = 20; // 直接访问并修改数组第二个元素的成员 // 创建一个指向结构体数组第一个元素的指针 CStash *s = &stashes[0]; // 使用箭头操作符访问并打印指针指向的结构体的成员 printf("s->quantity = %d\n", s->quantity); // 输出第一个元素的 quantity 值 // 移动指针到数组的第二个元素,并打印其成员 s = &stashes[1]; printf("s->quantity = %d\n", s->quantity); // 输出第二个元素的 quantity 值 return 0; }page 10
Heap new Type [ number_of_elements ] return a pointer to the Type. delete [ ] myArray int *p = new int [100]; delete [ ] p; memory leak, fragmented heap
堆 new Type [number_of_elements] 返回一个指向类型的指针。 删除[] myArray Int *p = new Int [100]; 删除[]p; 内存泄漏,碎片堆
1. 堆(Heap)堆是程序运行时用于动态内存分配的内存区域。 与栈(Stack)不同,堆中的内存分配和释放是手动管理的,通常通过C++中的new和delete操作符来完成。
2. 动态数组的分配与释放 new Type[number_of_elements]功能:在堆上分配一块连续的内存,用于存储一个动态数组。
返回值:返回一个指向数组首元素的指针。 int *p = new int [100];
示例:
int* p = new int[100]; // 分配一个包含100个int的动态数组 delete[] myArray功能:释放通过new[]分配的动态数组。delete[] p; // 释放动态数组
注意:必须使用delete[]来释放new[]分配的内存,而不是delete。 否则,可能会导致未定义行为。
示例:
1 举例 new delete int* p = new int; //cout << "Value: " << *p << endl; // 输出未定义值,可能每次运行程序时都不一样。例如:Value: -12345678 或者:Value: 0 int* p = new int() //cout << "Value: " << *p << endl; // 输出 0 int* p = new int(42); // new操作符的返回值类型与分配的内存类型一致 delete p;
#include <iostream> using namespace std; int main() { // 使用 new 分配单个 int int* p = new int(42); // 初始化为 42 // 使用对象 cout << "Value: " << *p << endl; // 使用 delete 释放单个对象 delete p; return 0; }2 举例 new delete[] int* arr = new int[5]; //数组中的值是未定义的,可能包含任意值。 arr[0]: -12345678 arr[1]: 42 arr[2]: 0 arr[3]: 314159 arr[4]: -1 int* arr = new int[5](); // 00000 int* arr = new int[5]{1,2,3,4,5}; delete[] arr;
#include <iostream> using namespace std; int main() { // 使用 new[] 分配一个包含 5 个 int 的数组 int* arr = new int[5]{1, 2, 3, 4, 5}; // 初始化数组 // 使用数组 for (int i = 0; i < 5; i++) { cout << "arr[" << i << "]: " << arr[i] << endl; } // 使用 delete[] 释放数组 delete[] arr; return 0; } 内存泄漏(Memory Leak)内存泄漏是指程序分配了动态内存,但在使用完毕后没有正确释放,导致内存无法被其他程序或系统回收。 内存泄漏会导致程序占用的内存不断增加,最终可能导致程序崩溃或系统资源耗尽。
int* p = new int[100]; // 忘记释放内存 // delete[] p; // 如果忘记这一步,就会导致内存泄漏 堆碎片化(Fragmented Heap)堆碎片化是指堆内存被频繁分配和释放后,导致堆空间变得碎片化。 碎片化的堆内存可能导致以下问题:
内存分配失败:即使堆中仍有足够的总内存,但由于碎片化,可能无法找到足够大的连续空间来满足新的分配请求。
性能下降:频繁的内存分配和释放会增加管理堆的开销。
void example() { for (int i = 0; i < 1000; i++) { int* p = new int[100]; delete[] p; // 正确释放内存 } } //在这个例子中,虽然每次分配的内存都被正确释放,但频繁的分配和释放可能导致堆碎片化。正确使用new[] delete[]避免 内存泄漏和堆碎片化
#include <iostream> using namespace std; int main() { // 动态分配一个包含100个int的数组 int* p = new int[100]; // 使用数组 for (int i = 0; i < 100; i++) { p[i] = i * i; // 初始化数组 } // 输出数组内容 for (int i = 0; i < 100; i++) { cout << "p[" << i << "]: " << p[i] << endl; } // 释放动态数组 delete[] p; return 0; }page 11-13
CLabTest.cpp
#include "CLib.h" //CLib.h:假设这是一个自定义的头文件,定义了 CStash 结构体以及相关的函数(initialize、add、fetch、cleanup 等)。 #include <fstream> //文件 #include <iostream> //控制台 #include <string>//字符 #include <cassert> //assert using namespace std; int main( ) { // Define variables at the beginning // of the block, as in C: CStash intStash, stringStash; //定义对象用于存储 int string int i; //循环计算 char* cp; // 字符指针,提取字符串 ifstream in; //打开文件读取,输入流对象 string line; // 字符串变量,读取文件美航 const int bufsize = 80; 定义常量 整形 字符串最大长度80 // to be continued // 初始化intStash initialize(&intStash, sizeof(int)); for(i = 0; i < 100; i++) add(&intStash, &i); for(i = 0; i < count(&intStash); i++) cout << "fetch(&intStash, " << i << ") = " << *(int*)fetch(&intStash, i) << endl; // to be continued // Holds 80-character strings: initialize(&stringStash, sizeof(char)*bufsize); in.open("CLibTest.cpp"); assert(in); while(getline(in, line)) add(&stringStash, line.c_str()); i = 0; while( (cp = (char*)fetch(&stringStash,i++) )!=0) cout << "fetch(&stringStash, " << i << ") = " << cp << endl; cleanup(&intStash); cleanup(&stringStash); }完整代码
//CLib.h typedef struct CStashTag { int size; // Size of each space int quantity; // Number of storage spaces int next; // Next empty space // Dynamically allocated array of bytes: unsigned char* storage; } CStash; // a place to hide something. // } CStash;:在C语言中,CStash 是结构体的标签,为结构体定义了一个新的类型名。 void initialize (CStash* s, int size ); void cleanup (CStash* s ); int add (CStash* s, const void* element); void* fetch (CStash* s, int index ); int count (CStash* s ); void inflate (CStash* s, int increase ); //CLib.CPP #include "CLib.h" #include <iostream> #include <cassert> using namespace std; // Quantity of elements to add when increasing storage const int increment = 100; //初始化 void initialize(CStash* s, int sz) { // CStash* s表示需要一个指向 CStash 类型结构体的指针 s->size = sz; // 设置每个存储单元的大小 s->quantity = 0; // 初始化存储容量为 0(表示没有分配内存) s->storage = 0; // 初始化存储指针为 NULL(表示没有分配内存) s->next = 0; // 初始化下一个可用位置为 0 } // // 初始化intStash initialize(&intStash, sizeof(int)); //相加 int add(CStash* s, const void* element) { if(s->next >= s->quantity) // Enough space left? inflate(s, increment); // Copy element into storage, 将element拷贝到storage // starting at next empty space: 在下一个empty空间开始 int startBytes = s->next * s->size; unsigned char* e = (unsigned char*)element; for(int i = 0; i < s->size; i++) s->storage[startBytes + i] = e[i]; s->next++; return(s->next - 1); // Index number } // add(&intStash, &i); void* fetch(CStash* s, int index) { // Check index boundaries: assert(0 <= index); if(index >= s->next) return 0; // To indicate the end // Produce pointer to desired element: return &(s->storage[index * s->size]); } int count(CStash* s) { return s->next; // Elements in CStash } // to be continued void inflate(CStash* s, int increase) { assert(increase > 0); int newQuantity = s->quantity + increase; int newBytes = newQuantity * s->size; int oldBytes = s->quantity * s->size; unsigned char* b = new unsigned char[newBytes]; for(int i = 0; i < oldBytes; i++) b[i] = s->storage[ i ]; // Copy old to new delete [ ](s->storage); // Old storage s->storage = b; // Point to new memory s->quantity = newQuantity; } // inflate(s, increment); void cleanup(CStash* s) { if(s->storage != 0) { cout << "freeing storage" << endl; delete [ ]s->storage; } } #include "CLib.h" //CLib.h:假设这是一个自定义的头文件,定义了 CStash 结构体以及相关的函数(initialize、add、fetch、cleanup 等)。 #include <fstream> //文件 #include <iostream> //控制台 #include <string>//字符 #include <cassert> //assert using namespace std; int main( ) { // Define variables at the beginning // of the block, as in C: CStash intStash, stringStash; //定义对象用于存储 int string int i; //循环计算 char* cp; // 字符指针char point,提取字符串 ifstream in; //打开文件读取,输入流对象 string line; // 字符串变量,读取文件美航 const int bufsize = 80; 定义常量 整形 字符串最大长度80 // to be continued // 初始化intStash initialize(&intStash, sizeof(int)); // &intStash 获取intStash变量的地址 for(i = 0; i < 100; i++) add(&intStash, &i); for(i = 0; i < count(&intStash); i++) cout << "fetch(&intStash, " << i << ") = " << *(int*)fetch(&intStash, i) << endl; // to be continued // Holds 80-character strings: initialize(&stringStash, sizeof(char)*bufsize); in.open("CLibTest.cpp"); assert(in); while(getline(in, line)) add(&stringStash, line.c_str()); i = 0; while( (cp = (char*)fetch(&stringStash,i++) )!=0) cout << "fetch(&stringStash, " << i << ") = " << cp << endl; cleanup(&intStash); cleanup(&stringStash); }[NKU]C++理论课cours3数据抽象(封装->隐藏实现的手段,隐藏->封装的重要目标)由讯客互联人工智能栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“[NKU]C++理论课cours3数据抽象(封装->隐藏实现的手段,隐藏->封装的重要目标)”