📄 c++编程思想 -- 第2章 笔记(2).txt
字号:
作者:rick1126
email: rickzhang@sina.com
日期:2001-7-17 10:19:32
2.2 一个袖珍C库
【C函数库的数据封装方式】
. struct 用于处理一组特性
. C库 一组struct + 一组活动在这些struct上的函数
【一个变长数组模拟的例子】
. 涉及操作:
- 使用一个结构保存数据, 附加一组操作维护数据
- C对于内存的分配和使用及其为维护
- 动态连接库的导出
. 相关代码
//stash.h
#if!defined(STASH_H)
#define STASH_H
typedef struct STASHtag{
int size; //每一个元素的大小
int quantity; //索引总长
int next; //下一个索引
unsigned char *storage; //数值指针, 动态分配
}Stash;
extern "C" {
void WINAPI initialize( Stash* S, int Size );
void WINAPI cleanup( Stash* S );
int WINAPI add ( Stash* S, void* element );
void* WINAPI fetch( Stash* S, int index );
int WINAPI count( Stash* S );
void WINAPI inflate( Stash* s, int increase );
}
#endif//STASH_H
; ch2_stash.def : Declares the module parameters for the DLL.
LIBRARY "ch2_stash"
DESCRIPTION 'ch2_stash Windows Dynamic Link Library'
EXPORTS
initialize
cleanup
add
fetch
count
inflate
; Explicit exports can go here
===>
1) 有关结构
- 使用XXXTag或者tagXXX进行typedef
- 使用US_XXX, *PUS_XXX定义实际的结构名称和结构指针名称
* 当然都是从一位长期从事C编程的同事那里获得的经验 ^_^
2) 有关导出的函数
- 使用extern "C" 返回类型 WINAPI 函数名( ... )进行声明
- 相关的函数导出方式
==> 使用.DEF文件
在声明使用 WINAPI, extern "C" 表示使用C执行方式, 考虑到兼容性
在DEF文件的EXPORTS段导出这些函数
==> __declspec(dllexport) 导出数据, 函数, 类或者类成员函数
函数例子:
//导出函数
__declspec(dllexport) void __cdecl Function1(void);
//导出一个类中的接口--公共成员
class __declspec(dllexport) CExampleExport : public CObject
{ ... class definition ... };
通常使用宏定义给出一个好用的预定义
#define DllExport __declspec( dllexport )
将函数名称存储在DLL的导出列表中, 如果你要优化该表格的大小, 使用.DEF文件方式.
//stash.cpp
#include "stdafx.h"
#include "stash.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
#include "assert.h"
//描述: 初始化变长数组
void WINAPI initialize( Stash* S, int Size )
{
S->size = Size;
S->quantity = 0;
S->storage = 0;
S->next = 0;
}
//描述: 清空编程数组
void WINAPI cleanup( Stash* S )
{
if ( S->storage ){
puts( "free storage" );
free( S->storage );
}
}
//描述: 添加元素到变长数组
int WINAPI add ( Stash* S, void* element )
{
if ( S->next >= S->quantity )
inflate( S, 100 );
memcpy( &( S->storage[ S->next * S->size ] ), element, S->size );
S->next ++;
return ( S->next - 1 );
}
//描述: 获得指定索引的数组元素
void* WINAPI fetch( Stash* S, int index )
{
if ( index >= S->next || index < 0 )
return 0;
return &( S->storage[index * S->size ] );
}
//描述: 变长数组计数
int WINAPI count( Stash* S )
{
return S->next;
}
//描述: 数组变长
void WINAPI inflate( Stash* S, int increase )
{
void* v=realloc( S->storage, ( S->quantity + increase ) * S->size );
assert(v);
S->storage = (unsigned char*)v;
S->quantity += increase;
}
==>
1) 函数实现
- 其实这个函数主要使用一个连续的缓冲区和定长的间隔存放指定的元素, 整个库维护一个结构
2) 相关的概念
- C的内存分配(局部)函数
函数名称 用途
-----------------------------------------------------------------------------
_alloca 从数据栈分配内存
calloc 为数组分配缓存, 初始化每一个字节为0
_calloc_dbg 调试版本下的 calloc
_expand 在不移动的前提下扩展缩小内存
_expand_dbg 调试版本下的 _expand
free 释放内存
_free_dbg 调试版本下的 _free
_get_sbh_threshold 返回申请上限
_heapadd 添加内存到堆
_heapchk 检测堆的连续性
_heapmin 释放堆中的空闲内存
_heapset 使用指定值填充堆内存入口
_heapwalk 返回堆中的每一个入口的信息
malloc 从堆分配内存
_malloc_dbg 调试版本下的 malloc
_msize 返回申请内存块的大小
_msize_dbg 调试版本下的 _msize
_query_new_handler 返回当前子函数句柄的地址, 子函数由 _set_new_handler 设置
_query_new_mode _set_new_mode 的句柄模式
realloc 重新申请内存块新的大小
_realloc_dbg 调试版本下的 realloc
_set_new_handler 使得错误处理机制在 new 操作失败的时候使能并且使能 STL 库
_set_new_mode 设置 malloc 的新的句柄模式
_set_sbh_threshold 设置内存分配上限
// client.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "iostream.h"
#include "assert.h"
#include "windows.h"
#include "../stash.h"
#define BUFSIZE 80
int main(int argc, char* argv[])
{
Stash intStash, stringStash;
int i;
FILE *file;
char buf[BUFSIZE];
char* cp;
initialize( &intStash, sizeof(int) );
for ( i=0; i<100; i++ )
add( &intStash,&i );
initialize( &stringStash, sizeof(char)*BUFSIZE );
file = fopen( "..\\readme.txt", "r" );
assert( file );
while ( fgets( buf, BUFSIZE, file ) )
add( &stringStash, buf );
fclose( file );
for ( i=0; i<count( &intStash); i++ )
cout << (int)fetch( &intStash, i ) << endl;
i=0;
while ( ( cp = (char*)fetch( &stringStash, i++ ) )!=0 )
cout << (char*)fetch( &stringStash, i-1 ) << endl;
cleanup( &intStash );
cleanup( &stringStash );
return 0;
}
==> 客户端应用程序
1) void* 的含义
通过类型转换, void* 可以转换任何标准数据类型, 由此该变长数组可以不拘一格的支持相应的数据类型, 只是类型检查和转换的工作由客户端支持
〖个人理解〗
这里用到一个一些C里面的如何建立函数库, 使用函数库和相应的内存以及建库的常用技巧. 特别是内存管理, 有了一个堆的概念. 堆用于申请一个连续内存, 然后提供应用程序的动态内存分配函数申请堆内的内存, 同时应用程序需要维护堆内部的内存管理.
在C++特别是VC里面, 传统的内存分配函数已经为new/delete代替, 而且也无需先申请一个堆. 不过如果开发COM还是会发现一些传统内存分配函数的影子, 毕竟从兼容性考虑, 2#标准的COM还是需要偏向或者生成C兼容的中间产物或者结果的.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -