分类归档: C++

windows C语言网络编程快速入门

Windows下C语言网络编程快速入门

  
C语言的学习,一般的方式是,先学C,然后是C++,最好还要有汇编语言和微机原理基础,然后才是Visual C++。这样的方式,对学习者来说,要花费很多时间和耐力。而在学校教学中,也没有时间深入学习Windows编程的实用技术了。

其实,具有了C语言基础后,再有一些基本的C++类的概念,就可以直接学习Windows C编程了。

一、走近Windows C语言

很多语言都把显示一个“Hello,World!”做为第一个入门程序, C语言的第一个程序是这样的:

#include<stdio.h>
main()
{
printf(“Hello,World!”);
}

如果把main函数写成带参数的main函数,应该是:

#include<stdio.h>
main(int arge,char *argv[])
{
printf(“Hello,World!”);
}

Windows C的第一个程序和这个程序在形式和原理上都是一致的,只是有两点不同:

1. 主函数接收的形参不只是命令行中的字符串的个数和字符串的首地址。

2. C语言的很多函数在Windows C中都可以继续使用,但象printf()屏幕显示等函数就不能继续使用了。因为Windows是多任务操作系统,屏幕已不再为某一个应用程序所独有,Windows C应用程序要显示字符串,需要使用Windows提供的API函数,开自己的窗口

下面是一个最简单的,显示“Hello,World!”的Windows C程序:

#include<windows.h>
APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
MessageBox(NULL,"Hello,World!","第一个Windows C程序",MB_OK|MB_ICONASTERISK);
}

主函数的形参有四个:

1) Hinstance:接收程序运行时当前实例的句柄;
2) HprivInstance:前一个实例的句柄;
3) LpCmdLine:程序命令行指针;
4) NcmdShow:一个用来指定窗口显示方式的整数。

这几个参数的使用我们会在深入的学习中介绍的。

显示Hello,Word!字符串,我们使用了一个MessageBox函数,这个函数会在屏幕上显示一个对话框,它的原型是:

int MessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UNIT uType)

四个参数分别是:

1) HWnd:父窗口的句柄;
2) LpText:要显示字符串的指针;
3) LpCaption:对话框标题字符串的指针;
4) UType:显示在对话框上的小图标的类型。

使用这个函数要包含windows.h头文件。

调试一下,怎么样?窗口上弹出了一个“第一个Windows C程序”对话框,上面有一行字:“Hello,World!”。

世界真的很美好啊!!

深入编程:

在C语言中,函数的声明,如果没有指明返回值类型,缺省值为void,这个程序的主函数就没有返回值。不过,在Windows编程时,我们最好养成个好习惯,指明函数的返回值类型,因为在C++中,函数返回值类型是不可以缺省的。而我们在Windows C编程时,还是会用到C++的一些概念,这样做,有利于以后深入地学习。

规范一点的程序应该是这样的:

#include<windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
MessageBox(NULL,"Hello,World!","第一个Windows C程序",MB_OK|MB_ICONASTERISK);
return 0;
}

这里,我们声明的类型为int型,并且返回一个值0,这样的函数就可以使用在复杂一点的函数调用中了。

在这一节中,我们有几处都提到了句柄的概念,句柄和指针的概念不同,它是作为操作系统内部索引表中的一个值来使用的,这样可以防止应用程序直接访问名对象的内部结构,体现了Windows资源管理的优越性。譬如说,一个窗口找开之后,好对应内存中的一个内存块,这个窗口所在的内存快地址往往会由操作系统做动态的调整,但其却不会随之变化。不过,通过它可以访问这个窗口,所以在使用的时候,可以把它当做指针一样看待。
二、 获取本地计算机的主机名和IP地址

和C语言一样,函数是Windows C编程的最基本的单位。不过,Windows C主要使用API函数,而网络编程则主要使用Winsock提供的API函数。

Winsock是90年代初,为了方便网络编程,由Microsoft联合了其他几家公司共同制定的一套WINDOWS下的网络编程接口,它是通过C语言的动态链接库方式提供给用户及软件开发者的,主要由winsock.h头文件和动态链接库winsock.dll组成,目前有两个版本:Winsock1.1和Winsock2.0。

在Win32平台上,访问众多的基层网络协议,Winsock是首选接口。

用Visual C++6.0编译Windows C程序,使用Winsock API函数时,首先要把wsock32.lib添加到它的库模块中,否刚在链接的时候,会出现“error LNK2001”错误。添加wsock32.lib的具体步骤是:打开工程菜单,选择设置,在弹出的Project settings对话框中,点击link选项卡,然后在对象/库模块文本框中添加wsock32.lib。

最简单的网络编程是获取本机的主机名和IP地址,这个程序使用了WSAStart()、WSAClenaup()、gethostname()、gethostbyname()四个winsock API函数,这四个函数的功能和使用方法介绍如下:

1. WSAStartup():

【函数原型】

int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);

【使用说明】

每一个使用winsock的应用程序,都必须进行WSAStart函数调用,并且只有在调用成功之后才能使用其它的winsock网络操作函数。

WVersionRequired:<输入>表示欲使用的Winsock版本,这是一个WORD类型的整数,它的高位字节定义的是次版本号,低位字节定义的是主版本号。

LpWSAData:<输出>是一个指向WSADATA资料的指针。这个资料我们一般不使用。

返回值:调用成功返回0;否则,返回出错信息。

2. WSAClenaup():

【函数原型】

int PASCAL FAR WSACleanup(void);

【使用说明】

winsock使用后,要调用WSACleanup函数关闭网络设备,以便释放其占用的资源。

3.gethostname()

【函数原型】

int PASCAL FAR gethostname (char FAR * name, int namelen);

【使用说明】

该函数可以获取本地主机的主机名,其中:

name:<输出>用于指向所获取的主机名的缓冲区的指针。

Namelen:<输入>缓冲区的大小,以字节为单位。

返回值:若无错误,返回0;否则,返回错误代吗。

4.gethostbyname()

【函数原型】

struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name);

【使用说明】

该函数可以从主机名数据库中得到对应的“主机”。

该函数唯一的参数name就是前面调用函数gethostname()得到的主机名。若无错误,刚返回一个指向hostent结构的批针,它可以标识一个“主机”列表。

Hostent结构定义如下:

Struct hostent
{
char FAR * h_name;
char FAR FAR ** h_aliases;
short h_addrtype;
char FAR FAR ** h_addr_list;
}

其中:

h_name:<输入>主机名地址(PC)。
h_aliases:一个由主机备用名组成的空中止数组。
H_addrtype:返回地址的类型,对于Winsock,这个域总是PF_INET。
H_lenth:每个地址的长度(字节数),对应于PF_INET域应该为4。
H_addr_list:应该以空指针结尾的主机地址的列表,返回的地址是以网络顺序排列的。

其中,h_addr_list[0]存放的就是本地主机的4个字节的IP地址,即:

h_addr_list[0][0].h_addr_list[0][1].h_addr_list[0][2].h_addr_list[0][3]

一个简单的用消息框显示主机名和IP地址的源程序如下:

#include<winsock.h>

int WSA_return;
WSADATA WSAData;

HOSTENT *host_entry;
char host_name[256];
char host_address[256];

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
WSA_return=WSAStartup(0x0101,&WSAData);

if(WSA_return==0)
{
gethostname(host_name,256);
host_entry=gethostbyname(host_name);
if(host_entry!=0)
{
wsprintf(host_address,"%d.%d.%d.%d",
(host_entry->h_addr_list[0][0]&0x00ff),
(host_entry->h_addr_list[0][1]&0x00ff),
(host_entry->h_addr_list[0][2]&0x00ff),
(host_entry->h_addr_list[0][3]&0x00ff));

MessageBox(NULL,host_address,host_name,MB_OK);
}
}
WSACleanup();
return 0;
}

深入编程:

前面显示IP地址的时候,我们使用的是消息框,规范一点的编程应该使用对话框,如何编辑一个对话框,很多书中都有介绍,编辑的对话框可参考图5的运行界面。

头文件Get_IP.h如下:

BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam);

这个程序只使用了一个对话框过程,一般把这个过程的声明放在头文件中。

源程序Get_IP.c:

#include<winsock2.h>
#include"Get_IP.h"
#include"resource.h" //这个头文件在创建资源的时候会自动生成,
//并会在插入资源时自动生成控件标识号.
int WSA_return;
WSADATA WSAData;

HOSTENT *host_entry;
char host_name[256];
char host_address[256];

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
WSA_return=WSAStartup(0x0101,&WSAData);
if(WSA_return==0)
{
gethostname(host_name,256);
host_entry=gethostbyname(host_name);
if(host_entry!=0)
{
wsprintf(host_address,"%d.%d.%d.%d",
(host_entry->h_addr_list[0][0]&0x00ff),
(host_entry->h_addr_list[0][1]&0x00ff),
(host_entry->h_addr_list[0][2]&0x00ff),
(host_entry->h_addr_list[0][3]&0x00ff));
}
}
WSACleanup();
DialogBox(hInstance,"DIALOG1",NULL,(DLGPROC)Hostname_ipDlgPro);
return 0;
}

BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,
WPARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_INITDIALOG:
return(TRUE);
case WM_COMMAND:
if(LOWORD(wParam)==IDOK)
{
SetDlgItemText(hDlg,IDC_EDIT1,host_name);
SetDlgItemText(hDlg,IDC_EDIT2,host_address);
SetDlgItemText(hDlg,IDCANCEL,"确定");
}
if(LOWORD(wParam)==IDCANCEL)
EndDialog(hDlg,TRUE);
return(TRUE);
break;
}
return(FALSE);
}

Read: 708

VC小技巧15则

一、 一次只运行一个程序实例
下列两种方式都可以实现,建议采用第二种方式:
1、 if( FindWindow(NULL,"程序标题"))
         exit(0);
2、BOOL CDemoTBarEApp::InstanceIsRun()
{
HANDLE m_hMutex;
m_hMutex = ::CreateMutex(NULL, TRUE, _T("YourApplication"));
ASSERT(m_hMutex);
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
m_hMutex = NULL;
return TRUE;//实例已经运行
}
return FALSE;//实例未运行
}

二、 装载光标
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_WAIT));
其中::SetCursor()是全局函数,用来设置整个例程的光标参数是宏定义光标句柄。AfxGetApp ()是一个系统函数,它返回当前的一个CWinApp对象。其成员函数LoadStandardCursor()用来读取一个系统指针,每一种系统指针的具体宏定义如下:
IDC_APPSTARTING 带小沙漏的标准箭头
IDC_ARROW 标准箭头
IDC_CROSS 十字光标(用于定位)
IDC_HAND Windows 2000:手型
IDC_HELP 带问号的箭头
IDC_IBEAM I型标
IDC_ICON Obsolete for applications marked version 4.0 or later.
IDC_NO   禁止符号
IDC_SIZE Obsolete for applications marked version 4.0 or later. Use IDC_SIZEALL.
IDC_SIZEALL 十字箭头
IDC_SIZENESW 指向东北和西南的双向箭头
IDC_SIZENS 指向南和北的双向箭头
IDC_SIZENWSE 指向西北和东南的双向箭头
IDC_SIZEWE 指向东西的双向箭头
IDC_UPARROW 上箭头
IDC_WAIT 沙漏

三、获得主框架:
CMainFrame * pMainframe = (CMainFrame *) AfxGetApp()->m_pMainWnd;
.获取应用程序的实例句柄:
      Example: HANDLE hInstance=AfxGetInstanceHandle();

获得应用程序主窗口的指针:
      Example: AfxGetMainWnd() ->ShowWindow(SW_SHOWMAXMIZED); //使程序最大化

四、重新建立字体的代码
if(m_fontLogo.m_hObject)
m_fontLogo.Detach();

m_fontLogo.CreateFont(nHeight, 0, 0, 0, nWeight, bItalic, bUnderline,0,0,0,0,0,0, Name);

五、用指定颜色填充区域
dc.FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));

六、绘制立体字体效果的字体,很值得一看
void CTestView::OnPaint()
{
CPaintDC dc(this); // device context for painting

CRect rect;
GetWindowRect(rect);

CFont m_fontLogo;
m_fontLogo.CreateFont(24, 0, 0, 0, FW_BOLD, true,
FALSE,0,0,0,0,0,0, "Arial");
CString m_LogoText;
m_LogoText=_T("Benlux Pro3D System");
dc.SetBkMode(TRANSPARENT);

CFont * OldFont = dc.SelectObject(&m_fontLogo);

// draw text in DC
COLORREF OldColor = dc.SetTextColor( ::GetSysColor( COLOR_3DHILIGHT));

rect.right = rect.Width();
rect.bottom = rect.Height();
rect.left = rect.top = 0;
dc.FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));

dc.DrawText( m_LogoText, rect + CPoint(1,1), DT_SINGLELINE | DT_LEFT | DT_VCENTER);
dc.SetTextColor( ::GetSysColor( COLOR_3DSHADOW));
dc.DrawText( m_LogoText, rect, DT_SINGLELINE | DT_LEFT | DT_VCENTER);

// restore old text color
dc.SetTextColor( OldColor);
// restore old font
dc.SelectObject(OldFont);
// Do not call CView::OnPaint() for painting messages
}

七、简单的消息检索和抽取函数,能够让系统响应其它操作
BOOL PeekAndPump()
{
static MSG msg;

while (::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)) {
if (!AfxGetApp()->PumpMessage()) {
   ::PostQuitMessage(0);
   return FALSE;
}
}
return TRUE;
}

八、在你的程序中用动画光标替换默认的等待光标 (ANI光标的使用)
HCURSOR m_hAniCursor=NULL;
BeginWaitCursor();   //begin wait cursor for api function

//load ani cursor from file in current path
TCHAR cursorPath[MAX_PATH]; GetModuleFileName(NULL,cursorPath,MAX_PATH);
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
_splitpath(cursorPath, drive, dir, fname, ext );
sprintf(cursorPath,"%s%swait.ani",drive,dir); //ani cursor file name is wait.ani

m_hAniCursor= LoadCursorFromFile(cursorPath);
HCURSOR oldCursor;
if(m_hAniCursor != NULL)
oldCursor=SetCursor(m_hAniCursor);

for(long i=0;i<1000;i++)
Sleep(5);

oldCursor=NULL;
m_hAniCursor=NULL;
EndWaitCursor(); //end wait cursor for api function

九、如何限制编辑框中的准许字符
     如果用户在编辑控件中只允许接收数字,可以使用一个标准的编辑控件并指
定新的创建标志ES_NUMBERS,它是Windows 95新增加的标志,该标志限制 编辑控
件只按收数字字符。
如果用户需要复杂的编辑控件,可以使用Microsoft 的屏蔽编辑控件,它是一个很有用的OLE定制控件。
    如果希望不使用OLE 定制控件自己处理字符,可以派生一个CEdit 类并处理WM_CHAR消息,然后从编辑控件中过滤出特定的字符。首先,使用ClassWizard 建立一个 CEdit的派生类,其次,在对话类中指定一个成员变量将编辑控件分类在OnInitdialog 中调用CWnd: : SubclassDlgItem .

//In your dialog class declaration (.H file )
private :
    CMyEdit m_wndEdit ; // Instance of your new edit control .

//In you dialog class implementation (.CPP file )
BOOL CSampleDialog : : OnInitDialog ( )
{

    //Subclass the edit lontrod .
    m_wndEdit .SubclassDlgItem (IDC_EDIT,this );
    …
}
    使用ClassWizard处理WM_CHAR消息,计算nChar参量并决定所执行的操作,用户可以确定是否修改、传送字符。下例说明了如何显示字母字符,如果字符是字母字符,则调用CWnd ; OnChar,否则不调用OnChar.
//Only display alphabetic dharacters .
void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UITN nFlags )
{
    //Determine if nChar is an alphabetic character .
    if (: : IsCharAlpha ( ( TCHAR) nChar ) )
        CEdit : : OnChar (nChar, nRepCnt , nFlags );
}
    如果要修改字符,则不能仅仅简单地用修改过的nChar调用CEdit : : OnChar。要修改一个字符,需要首先修改nChar,然后用修改过的nChar调用CWnd: : DefWindowProc。下例说明了如何将字符转变为大写:
//Make all characters uppercase
void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UINT nFlags )
{
    //Make sure character is uppercase .
    if (: : IsCharAlpha ( .( TCHAR) nChar)
         nChar=: : CharUpper (nChar ) ;
    //Bypass default OnChar processing and directly call default window proc.
    DefWindProc (WM_CHAR, nChar , MAKELPARAM (nRepCnt , nFlags )) ;
}

十、串太长时如何在其末尾显示一个省略号
    调用CDC:: DrawText并指定DT_END_ELLIPSIS标志,这样就可以用小略号取代串末尾的字符使其适合于指定的边界矩形。如果要显示路径信息,指定DT_END_ELLIPSIS标志并省略号取代串中间的字符。
void CSampleView:: OnDraw (CDC* pDC)
{
     CTestDoc* pDoc=GetDocument ();
     ASSERT_VALID (pDoc);
                                                                                   
     //Add ellpsis to end of string if it does not fit
     pDC->Drawtext (CString ("This is a long string"),
         CRect (10, 10, 80, 30), DT_LEFT | DT_END_ELLIPSIS);

     //Add ellpsis to middle of string if it does not fit
     pDC->DrawText (AfxgetApp () ->m_pszhelpfilePath,
         CRect (10, 40, 200, 60), DT_LEFT | DT_PATH_ELLIPSIS);
}

十一、如何实现一个橡皮区矩形(具有踪迹矩形并可移动、缩放的矩形)
     CRectTracker是一个很有用的类,可以通过调用CRectTracker:: TrackRubberBand响应WM_LBUTTONDOWN消息来创建一个橡皮区矩形。下例表明使用CRectTracker移动和重置视窗中的蓝色椭圆的大小是很容易的事情。
    首先,在文档类中声明一个CRectTracker数据成员:
class CTestDoc: Public CDocument
{…
public:
CRectTracker m_tracker;

};
     其次,在文档类的构造函数中初始化CRectTracker 对象:
CTestDoc::CTestDoc()
{
m_tracker.m_rect.SetRect (10, 10, 300, 300);
m_tracker.m_nStyle=CRectTracker:: resizeInside |
CRectTracker:: dottedLine;
}
     然后,在视图类的OnDraw函数中画椭圆和踪迹矩形:
void CTestView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

//Select blue brush into device context.
     CBrush brush (RGB (0, 0, 255));
     CBrush* pOldBrush=pDC->SelectObject (&brush);

     //draw ellipse in tracking rectangle.
     CRect rcEllipse;
     pDoc->m_tracker.GetTrueRect (rcEllipse);
     pDC->Ellipse (rcEllipse);

     //Draw tracking rectangle.
     pDoc->m_tracker.Draw (pDC);
     //Select blue brush out of device context.
     pDC->SelectObject(pOldBrush);
}
    最后,视图类中处理WM_LBUTTONDOWN消息,并增加下述代码。该段代码根据鼠标击键情况可以拖放、移动或者重置椭圆的大小。

void CTestView::OnLButtonDown(UINT nFlags, CPoint point)
{
   //Get pointer to document.
     CTestDoc* pDoc=GetDocument();
     ASSERT_VALID (pDoc);

     //If clicked on ellipse, drag or resize it. Otherwise create a
     //rubber-band rectangle nd create a new ellipse.
     BOOL bResult=pDoc->m_tracker.HitTest (point)!=
         CRectTracker::hitNothing;

     //Tracker rectangle changed so update views.
     if (bResult)
     {
        pDoc->m_tracker.Track (this,point,TRUE);
        pDoc->SetModifiedFlag ();
        pDoc->UpdateAllViews (NULL);
     }
     else
        pDoc->m_tracker.TrackRubberBand (this,point,TRUE);

CView::OnLButtonDown(nFlags, point);
}

十二、如何在临时目录创建一个临时文件
如果你要在临时目录下创建临时文件,下面的代码能帮到你的忙。
bool GetuniqueTempName (CString& strTempName)
{
strTempName="";
     //Get the temporary files directory.
     TCHAR szTempPath [MAX_PATH];
     DWORD dwResult=:: GetTempPath (MAX_PATH, szTempPath);
     if (dwResult==0)
   return false;

     //Create a unique temporary file.
     TCHAR szTempFile[MAX_PATH];
     UINT nResult=GetTempFileName (szTempPath, _T ("~ex"),0,szTempFile);
     if (dwResult==0)
   return false;

     strTempName=szTempFile;
return true;
}

十三、如何限制窗口的最小范围
要限制窗体的大小,下面的代码能帮到你的忙。
在CMainFrame中增加WM_GETMAXMININFO消息的处理函数,然后在这个函数中写代码如下:
//限制主窗体的最小高度和宽度
void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
lpMMI->ptMinTrackSize.x=600;
lpMMI->ptMinTrackSize.y=400;
CNewFrameWnd::OnGetMinMaxInfo(lpMMI);
}

十四、怎样删除文件到回收站中
要删除文件到回收站,很简单。只要用SHFileOperation函数就行了,下面的代码我将为你演示了这一个函数的用法。当然你可以直接拷贝到你的项目中。
//删除文件到回收站中
//pszPath : 待删除的全路径文件名
//bDelete : TRUE 删除,不移到回收站,FALSE:移到回收站
一、 //返回    : TRUE 删除成功     FALSE 删除失败
BOOL CDelFileToRecycleDlg::Recycle(LPCTSTR pszPath, BOOL bDelete/*=FALSE*/)
{
SHFILEOPSTRUCT shDelFile;
memset(&shDelFile,0,sizeof(SHFILEOPSTRUCT));
shDelFile.fFlags |= FOF_SILENT;      // don’t report progress
shDelFile.fFlags |= FOF_NOERRORUI;     // don’t report errors
shDelFile.fFlags |= FOF_NOCONFIRMATION;    // don’t confirm delete
// Copy pathname to double-NULL-terminated string.
//
TCHAR buf[_MAX_PATH + 1]; // allow one more character
_tcscpy(buf, pszPath);   // copy caller’s pathname
buf[_tcslen(buf)+1]=0;   // need two NULLs at end

// Set SHFILEOPSTRUCT params for delete operation
shDelFile.wFunc = FO_DELETE;       // REQUIRED: delete operation
shDelFile.pFrom = buf;         // REQUIRED: which file(s)
shDelFile.pTo = NULL;          // MUST be NULL
if (bDelete)
{         // if delete requested..
shDelFile.fFlags &= ~FOF_ALLOWUNDO;    // ..don’t use Recycle Bin
}
else
{           // otherwise..
shDelFile.fFlags |= FOF_ALLOWUNDO;    // ..send to Recycle Bin
}
     return SHFileOperation(&shDelFile);    // do it!
}

十五、内存泄漏检查
    也许你已经知道,在C++和C语言中指针问题也就是内存申请与释放是一个令人头疼的事情,假如你申请了内存,但没有释放,并且你的程序需要长时间地运行,那么,系统的资源将逐渐减少,当系统的资源全部被用完时,系统将会崩溃。所以在开发程序的过程中一定要保证资源的完全释放。下面我们来介绍内存漏洞的检查。
示例如下:
// do your memory allocations and deallocations…
CString s = "This is a frame variable";
#ifdef _DEBUG
CMemoryState oldMemState, newMemState, diffMemState;
oldMemState.Checkpoint();
#endif
// the next object is a heap object
CString* p = new CString( "Smith Alan 581_0215" );
delete p;
p=NULL;
#ifdef _DEBUG
newMemState.Checkpoint();
BOOL b=diffMemState.Difference(oldMemState, newMemState);
if (b)
{
AfxMessageBox( "Memory leaked!n" );
}
#endif
     根据试验,由于我们无法释放掉象int CString char 申请的变量。只能释放指针型的变量。而检测内存时,照样会出现内存泄漏现象。所以,这种内存检测方式局限性还是很大。因为我们无法释放非指针型变量。

Read: 843

BC++B 可视化组件库VCL中的对象、组件与控件

     图2 – 1为可视组件库的分层结构的简图,图中显示出了树状分层结构的五个主要的分支。

C++ Builder 可视化组件库(VCL)中的对象、组件与控件

2.4.1 TObject分支
     下面几部分分别对包含于五个主要分支中的类做出简要地描述,为完整地说明VCL对象的分层结构,引用了包含于产品中的VCL对象分层结构图。
     所有的VCL对象都派生于TObject,其方法定义了构造、析构、消息处理等基本特征。VCL对象的许多强大的功能都建立在TObject给出的这些方法的基础之上。通过提供下列方法,TObject封装了所有VCL对象最基本的通用特征:
· 在对象被创建或销毁时响应的能力。
· 对象的类型和实例信息,及有关对象发布的属性的运行时类型信息(RTTI)
· 支持消息处理。
     TObject是许多简单类的直接祖先。在这一分支中所包含的类有一个共同的重要的特性,它们都是暂时的。这是指,这些类都没有用来在对象被销毁之前保存状态的方法,它们不是固有的。
     在这一分支中一个较主要的类为异常(Exception)类。它提供一组内嵌的异常类用于自动处理除数为零的错误、文件I/O错误、非法的类型匹配及许多其他的异常情况。
     包含于TObject分支的还有一些封装数据结构的类,例如:
· TBits,存储了一个布尔型“数组”的类。
· TList,链接列表的类。
· TStack ,包含一个“后进先出”的指针数组的类。
· TQueue,包含一个“先进先出”的指针数组的类。
     此外,你还可以发现一些封装了外部对象的类,如TPrinter,它封装了Windows打印机接口,TRegistry,它封装了底层系统注册及其函数。
     TStream是这一分支里其他类型的典型例子。TStream是流对象的基类,可以与如磁盘文件、动态内存等等各种存储介质进行读写操作。
     可以看到,这一分支中包含许多不同类,对于一个开发者来说,这些类都非常有用。

2.4.2 TPersistent分支
     在VCL分层结构中直接在TObject之下的是TPersistent。TPersistent给其下所有的类添加了两个重要的方法: SaveToStream和LoadFromStream。这些方法向对象提供固有特性。例如:当窗体设计器需要创建DFM文件(存储窗体及其组件信息的文件)时,它循环检查窗体的组件数组,并对其中所有的组件都调用SaveToStream方法。每个组件都“知道”如何将改变了的属性写入流中(如文本文件)。反过来,若窗体设计器需要从DFM文件中装入组件属性时,它循环检查窗体的组件数组,并对其中所有的组件都调用LoadFromStream方法。因此,任何TPersistent的派生类都有保存状态信息和需要时还原它们的能力。
     在这一分支中包含的类主要有:
· TGraphicsObject,图形对象的基类,封装了Windows图形对象:TBrush、TFont、TPen。
· TGraphic,图标、位图、图元文件等能存储和显示可视图像的对象的基类,封装了TBitmap、TIcon和TMetaFile。
· TStrings ,串列表对象的基类。
· TClipboard,封装了Windows剪贴板对象,它包含从应用程序中剪下或复制的文本或者图形。
· TCollection、TOwnedCollection和TCollectionItem,包含特殊预定义项的索引集。

2.4.3 TComponent分支
     TComponent是所有VCL组件的共同祖先。组件就是在设计时可在窗体中操纵的对象。虽然名为可视组件库,其实VCL中包含的大部分对象为非可视化的。
     VCL组件都是固有的对象,其功能主要有:
· 可出现在组件面板上并可在窗体设计器中修改。
· 可拥有和管理其他的组件。
· 增强的流及文件管理能力。
· 可通过New Objects对话框中ActiveX页的向导程序转换成ActiveX控件或其他COM对象。
     TComponent 扮演了一个所有组件都可插入的标准“总线”的角色。TComponent通过Name和Owner属性及一些方法规定组件在设计时的行为。所有派生于 TComponent的类都有Name和Owner属性。Owner属性值,也即组件的属主负责该组件对象的销毁。
     在这一分支中包含的类主要有:
· TMainMenu 为窗体提供菜单栏及相应的下拉菜单的类。
· TTimer 包含Windows API 中的计时器函数的类。
· TOpenDialog、TSaveDialog、TFontDialog、TFindDialog、TColorDialog等,是通用Windows对话框类。
· TActionList,包含组件或控件,如菜单项和按钮的动作列表的类。
· TScreen,负责管理应用程序创建的窗体和数据模块,当前的活跃窗体及其上的活跃控件、屏幕尺寸和分辨率,应用程序使用的光标和字体等内容的类。

2.4.4 TControl分支
     所有的控件都是可视化对象,即它们在运行时可见并可被用户操纵。且它们都有作为可视化控件所共有的属性、方法和事件,例如控件的位置、光标、与控件关联的提示、用来描绘或移动控件的方法、用来响应鼠标动作的事件等。
     TComponent定义所有组件的行为,TControl则进一步定义了所有可视化控件的行为。包含画图例程、标准Windows事件及容器特性。
     在这一分支中一组较重要的类为TGraphicControl。TGraphicControl控件类必须能描绘它们自己并且从不接受焦点。在这组控件中主要包含:
· TImage,一种显示图形类图像的控件。
· TLabel,一种在窗体中显示文字的控件。
· TBevel,一种表示边缘斜角的控件。
· TPaintBox,一种提供画布的控件,应用程序能够使用画布创建及生成显示图像。
     注意到这些控件包含通用的画图例程(画图、重画、使无效等),但C++Builder不必为它们分配窗口句柄,因为它们从不需要接受焦点。

2.4.5 TWinControl分支
     TWinControl是为所有的窗口控件的基类。下面的是窗口控件的几个特征:
· 窗口控件是应用程序运行时能接受焦点的控件。
· 也许还有其他控件可以显示数据,但是只有当该控件是一种窗口控件时,用户才能使用键盘来与之交互。
· 窗口控件能包含其他控件。
· 包含其他控件的控件是父代控件。只有窗口控件能够作为一个或多个子控件的父代控件。
· 窗口控件有窗口句柄。
     除了能接受焦点外,TWinControl与TControl基本相同。这意味着它们不但包含许多标准Windows事件,同时Windows还必须给它们分配窗口句柄。这一分支中包含了两大类控件,一种是由Windows自动描绘的(包括TEdit、TListBox、TComboBox、 TPageControl等),另一种是C++Builder自定义的(包括TDBNavigator、TMediaPlayer、TGauge等)。不过,你根本不必关心控件如何描绘它们自己或如何响应事件,这些都已被C++Builder所完全封装。

2.4.6 TControl的共同属性
     所有可视化控件(TControl的后裔)共享的属性包括:
· 位置、尺寸及排列属性。
· 显示属性。
· 父代属性。
· 导航属性。
· 拖放属性。
· 拖动-停靠属性。
· 动作(Action)属性。
     这些属性是从TControl继承而来的,它们都是发布属性,因此,只对于那些适用的组件它们才会出现在对象观察器中。例如:TImage未发布Color属性,因为它的颜色是由它所显示的图形决定的。
     1、Action(动作)属性
     Action可以让一些动作共享代码(例如:当工具栏按钮和菜单项所做的事相同),并且依赖于应用程序的状况,提供了简单的易实现的方法控制动作的有效与否。
· Action指定与控件相关的动作。
· ActionLink包含与控件相关的动作链接对象。
     2、位置、尺寸及排列属性
     这些属性用于定义控件在窗体或容器控件中位置和尺寸:
· Height,设置垂直尺寸。
· Width,设置水平尺寸。
· Top,设置上边界。
· Left,设置左边界。
· AutoSize,指定控件尺寸是否自动地适应其中的内容。
· Align,确定控件在容器(父代控件)内部如何排列。
· Anchor,指定控件相对于其父代控件的定位。
     下列属性确定控件整个客户区的高度、宽度、尺寸:
· ClientHeight,以像素为单位指定控件客户区的高度。
· ClientWidth,以像素为单位指定控件客户区的宽度。
     对于非可视化组件,这些属性都是不可访问的,但是C++Builder记录下了将组件图标放置在窗体的何处。大多数情况下可以通过操纵控件的图像或使用排列面板来设置和改变这些属性,同时,也能在运行时改变它们。

Read: 2001

VCL库函数

//—————————– Suntenghe 整理——————–
1.内存分配
2.文件操作
3.磁盘目录管理
4.字符串操作
5.类型转换
6.时间和日期管理

一.BORLAND C++ BUILDER VCL的内存管理函数
1. AllocMem
在队中分配指定字节的内存块,并将分配的每一个字节初始化为 0.函数原型如下:
void * __fastcall AllocMem(Cardinal Size);
2. SysFreeMem
释放所指定的内存块.函数原型如下:
int __fastcall SysFreeMem(void * P);
3. SysReallocMem
要求重新分配参数Size所指定的内存.函数原型如下:
void * __fastcall SysReallocMem(void * P , int Size);

二.Borland C++ Builder VCL的文件操作函数
1. FileOpen
打开指定的文件,如果返回为正数,表示操作成功,返回值为文件句柄:如果返回值为-1,表
示操作失败,函数原型如下:
int __fastcall FileOpen(const System::AnsiString FileName,int Mode);
2. FileCreate
以指定的文件名称创建一个新的文件,如果返回为正数,表示操作成功,返回值为文件句柄,
如果返回值为-1,表示操作失败.函数原型如下:
int __fastcall FileCreate(const System ::AnsiString FileName);
3. FileRead
从文件中读取指定字节的数据到缓冲区中,函数返回实际读取的字节数,函数原型如下;
int __fastcall FileRead(int Handle,void *Buffer,int Count);
4. FileWrite
将缓冲区的数据写入到指定的文件的当前位置中去如果操作成功,函数返回实际写入的字
节数,如果返回为-1,则表示操作产生错误,函数原型如下:
int __fastcall FileWrite(int Handle,const void *Buffer,int Count);
5. FileSeek
调整文件指针到新的位置,如果操作成功,则返回新的文件位置,如果操作失败,则函数返
回-1,函数原型如下:
int __fastcall FileSeek(int Handle,int Offset,int Origin);
6. FileClose
关闭指定的文件,函数原型如下:
void __fastcall FileClose(int Handle);
7. FileAge
返回指定文件的时间标签,如果操作失败,则返回-1,函数原型如下:
int __fastcall FileAge(const System::AnsiString FileName);
8. FileExists
用于测试指定的文件是否存在,如果存在返回真,否则返回假,函数原型如下:
bool __fastcall FileExists(const System::AnsiString FileName);
9. FileGetDate
返回指定文件的DOS时间标签,如果操作失败,则近回-1,函数原型如下:
int __fastcall FileGetDate(int Handle);
10.FileSetDate
更改指定文件的DOS时间标签,如果操作成功,返回0,否则返回错误代码,函数原型如下:
int __fastcall FileSetDate(int Handle,int Age);
11.FileGetAttr
返回指定文件的属性,如果操作失败,则函数返回-1,函数原型如下;
int __fastcall FileGetAttr(const System::AnsiString FileName);
12.FileSetAttr
更改指定文件的属性参数,如果操作成功,则返回0,函数原型如下;
int __fastcall FileSetAttr(const System::AnsiString FileName,int Attr);
13.FindFirst
在指定的文件目录内,搜寻符合特定属性参数的文件,如果成功地查找到符合条件的文件,
则函数返回0,否则函数返回一个错误代码,函数原型如下:
int __fastcall FindFirst(const System::AnsiString Path,int Attr,TSearchRec %26amp;F);
14.FindNext
继续搜寻FindFirst所指定属性参数的文件,如果成功地查找到符合条件的文件,则函数返
回0,否则函数返回一个错误代码,函数原型如下:
int __fastcall FindNext(TSearchRec %26amp;F);
15.FindClose
释放FindFirst操作所申请的内存资源,函数原型如下:
void __fastcall FindClose(TSearchRec %26amp;F);
16.DeleteFile
在计算机磁盘中删除指定的文件,如果操作成功,则函数返回真,函数原型如下:
bool __fastcall DeleteFile(const AnsiString FileName);
17.RenameFile
更改指定文件的名称,如果操作成功,则函数返回真,函数原型如下:
bool __fastcall RenameFile(const AnsiString OldName, const AnsiString NewName);
18.ChangeFileExt
更改指定文件的扩展名,函数原型如下:
AnsiString __fastcall ChangeFileExt(const AnsiString FileName,const AnsiString Extension);
19.ExtractFilePath
返回指定文件的工作路径,函数原型如下:
AnsiString __fastcall ExtractFilePath(const AnsiString FileName);
20.ExtractFileDir
返回指定文件的工作目录,函数原型如下:
AnsiString __fastcall ExtractFileDir(const AnsiString FileName);
21.ExtractFileDrive
返回指定文件的驱动器,函数原型如下:
AnsiString __fastcall ExtractFileDrive(const AnsiString FileName);
22.ExtractFileName
返回指定文件的文件名及扩展名,函数原型如下:
AnsiString __fastcall ExtractFileName(const AnsiString FileName);
23.ExtractFileExt
返回指定文件的扩展名,函数原型如下:
AnsiString __fastcall ExtractFileExt(const AnsiString FileName);

三,Borland C++ Builder VCL的磁盘管理函数
1. DiskFree
返回指定磁盘的剩余空间,如果操作成功,返回剩余磁盘空间,如果操作失败,则返回-1,
函数原型如下:
int __fastcall DiskFree(Byte Drive);
2. DiskSize
返回指定磁盘的空间,如果操作成功,返回磁盘空间,如果操作失败,则返回-1,函数原型如下:
int __fastcall DiskSize(Byte Drive);
3. GetCurrentDir
返回当前工作目录,函数原型如下:
AnsiString __fastcall GetCurrentDir();
4. SetCurrentDir
设置当前工作目录,如果操作成功,则返回真,函数原型如下:
bool __fastcall SetCurrentDir(const AnsiString Dir);
5. CreateDir
创建新的目录,如果操作成功,返回真,否则返回假,函数原型如下:
bool __fastcall CreateDir(const AnsiString Dir);
6. RemoveDir
删除指定的目录,如果操作成功,返回真,否则返回假,函数原型如下:
bool __fastcall RemoveDir(const AnsiString Dir);

四.Borland C++ Builder VCL的字符串函数
1. UpperCase
将指定的AnsiString字符串转换为大写形式,函数原型如下:
AnsiString __fastcall UpperCase(const AnsiString S);
2. LowerCase
将指定的AnsiString字符串转换为小写形式,函数原型如下:
AnsiString __fastcall LowerCase(const AnsiString S);
3. CompareStr
比较两个AnsiString字符串,函数原型如下:
int __fastcall CompareStr(const AnsiString S1, const AnsiString S2);
4. CompareText
比较两个AnsiString字符串,函数原型如下:
int __fastcall CompareText(const AnsiString S1, const AnsiString S2);
5. StrLen
返回字符串的长度,函数原型如下:
Cardinal __fastcall StrLen(const char * Str);
6. StrEnd
返回字符串结尾指针,函数原型如下:
char * __fastcall StrEnd(const char * Str);
7. StrMove
从源字符串向目的字符串拷贝指定数目的字符,函数原型如下:
char * __fastcall StrMove(char * Dest, const char * Source, Cardinal Count);
8. StrCopy
将源字符串拷贝到目的字符串中,函数原型如下:
char * __fastcall StrCopy(char * Dest, const char * Source);
9. StrECopy
将源字符串拷贝到目的字符串中,并返回目的字符串结尾指针,函数原型如下:
char * __fastcall StrECopy(char * Dest, const char * Source);
10.StrLCopy
将源字符串指定数目的字符拷贝到目的字符串中,并返回目的字符串指针,函数原型如下:
char * __fastcall StrLCopy(char * Dest, const char * Source, Cardinal MaxLen);
11.StrPCopy
将AnsiString类型的源字符串拷贝到目的字符串中,并返回目的字符串指针,函数原型如下:
char * __fastcall StrPCopy(char * Dest, const AnsiString Source);
12.StrPLCopy
将源字符串(AnsiString类型)指定数目的字符拷贝到目的字符串中,并返回目的字符串
指针,函数原型如下:
char * __fastcall StrPLCopy(char * Dest, const AnsiString Source, Cardinal MaxLen);
13.StrCat
连接两个字符串,并返回目的字符串指针,函数原型如下:
char * __fastcall StrCat(char * Dest, const char * Source);
14.StrLCat
将指定数目的源字符串连接到目的字符串,并返回目的字符串指针,函数原型如下:
char * __fastcall StrLCat(char * Dest, const char * Source, Cardinal MaxLen);
15.StrComp
两个字符串相到比较,返回比较的结果,函数原型如下:
int __fastcall StrComp(const char * Str1, const char * Str2);
16.StrIComp
两个字符串相互比较(不论大小写),返回比较的结果,函数原型如下:
int __fastcall StrIComp(const char * Str1, const char * Str2);
17.StrLComp
对两个字符串指定数目的字符进行比较操作,函数原型如下:
int __fastcall StrLComp(const char * Str1, const char * Str2, Cardinal MaxLen);
18.StrScan
在指定的字符串中寻找特定的字符,并返回字符串中第一个特定字符的指针,函数原型如下:
char * __fastcall StrScan(const char * Str, char Chr);
19.StrRScan
在指定的字符串中寻找特定的字符,并返回字符串中最后一个特定字符的指针,函数原型
如下:
char * __fastcall StrRScan(const char * Str, char Chr);
20.StrPos
在Strl所指定的字符串中寻找Str2所指定的子字符串,并返回Str2在Str2中第一个子字符的
指针,函数原型如下:
char * __fastcall StrPos(const char * Str1, const char * Str2);
21.StrUpper
将字符串转换为大写形式,函数原型如下:
char * __fastcall StrUpper(char * Str);
22.StrLower
将字符串转换为小写形式,函数原型如下:
char * __fastcall StrLower(char * Str);
23.StrPas
将指定的字符串转换为AnsiString类型字符串对象,函数原型如下:
AnsiString __fastcall StrPas(const char * Str);
24.StrAlloc
为字符串分配指定字节的内存,并返回内存指针,函数原型如下:
char * __fastcall StrAlloc(Cardinal Size);
25.StrBufSize
返回*Str所指向内存的大小,函数原型如下:
Cardinal __fastcall StrBufSize(const char * Str);
26.StrNew
在堆中为指定字符串分配空间,并将字符串拷贝到此空间中,函数原型如下:
char * __fastcall StrNew(const char * Str);

五.Borland C++ Builder VCL的数值转换函数
1. IntToStr
将整数转换为AnsiString字符串,函数原型如下:
AnsiString __fastcall IntToStr(int Value);
2. IntToHex
将整数转换为十六进制字符串,函数原型如下:
AnsiString __fastcall IntToHex(int Value, int Digits);
3. StrToInt
将AnsiString字符串转换为整数值,如果不能进行转换,则产生EConvertError异常,
函数原型如下:
int __fastcall StrToInt(const AnsiString S);
4. StrToIntDef
将AnsiString字符串转换为一个数值,函数原型如下:
int __fastcall StrToIntDef(const System::AnsiString S,int Default);
5. FloatToStr
将浮点数转换为AnsiString字符串,函数原型如下:
AnsiString __fastcall FloatToStr(Extended Value);
6. StrToFloat
将AnsiString字符串转换为一个浮点数值,函数原型如下:
Extended __fastcall StrToFloat(const AnsiString S);
7. FloatToStrF
将浮点数转换为指定格式的AnsiString字符串,函数原型如下:
AnsiString __fastcall FloatToStrF(Extended Value, TFloatFormat Format,int Precision, int Digits);

六.Borland C++ Builder VCL的时间函数
1. Date
返回TDateTime对象,包含当前的年月日信息,函数原型如下:
System::TDateTime __fastcall Date(void);
2. Time
返回TDateTime对象,包含当前的时间信息,函数原型如下:
System::TDateTime __fastcall Time(void);
3. Now
返回TDateTime对象,获取当前的日期和时间信息,函数原型如下:
System::TDateTime __fastcall Now(void);
4. DatetimeToString
将TDateTime对象转换为指定格式的字符串对象,函数原型如下:
void __fastcall DateTimeToString(AnsiString %26amp;Result, const AnsiString Format,System::TDateTime DateTime);
5. DateToStr
将TDateTime对象(包含当前年月日信息)转换为字符串对象,函数原型如下:
AnsiString __fastcall DateToStr(System::TDateTime Date);
6. TimeToStr
将当前日期转换为字符串对象,函数原型如下:
AnsiString __fastcall TimeToStr(System::TDateTime Time);
7. DateTimetoStr
将TDateTime对象转换为字符串对象,函数原型如下:
AnsiString __fastcall DateTimeToStr(System::TDateTime DateTime);
8. StrToDate
将字符串对象转换为年月日对象,函数原型如下:
System::TDateTime __fastcall StrToDate(const AnsiString S);
9. StrToTime
将字符串对象转换时间对象,函数原型如下:
System::TDateTime __fastcall StrToTime(const AnsiString S);
10.StrToDateTime
将字符串对象转换为年月日时间对象,函数原型如下:
System::TDateTime __fastcall StrToDateTime(const AnsiString S);
11.DateTimeToSystemTime
将TDateTime对象转换为操作系统时间,函数原型如下:
void __fastcall DateTimeToSystemTime(System::TDateTime DateTime, _SYSTEMTIME %26amp;SystemTime);
12.SystemTimeToDateTime
将操作系统时间转换为TDateTime对象,函数原型如下:
System::TDateTime __fastcall SystemTimeToDateTime(const _SYSTEMTIME %26amp;SystemTime);

// ———————– [email protected]———————-

Read: 683

C++ string(字符串)详解

好了,进入正题………
首先,为了在我们的程序中使用string类型,我们必须包含头文件 。如下:
#i nclude<string> //注意这里不是string.h string.h是C字符串头文件

1.声明一个C++字符串
声明一个字符串变量很简单:
string Str;
这样我们就声明了一个字符串变量,但既然是一个类,就有构造函数和析构函数。上面的声明没有传入参数,所以就直接使用了string的默认的构造函数,这个函数所作的就是把Str初始化为一个空字符串。String类的构造函数和析构函数如下:
a)string s;//生成一个空字符串s
b)string s(str) //拷贝构造函数 生成str的复制品
c)string s(str,stridx) //将字符串str内“始于位置stridx”的部分当作字符串的初值
d)string s(str,stridx,strlen) //将字符串str内“始于stridx且长度顶多strlen”的部分作为字符串的初值
e)string s(cstr) //将C字符串作为s的初值
f)string s(chars,chars_len) //将C字符串前chars_len个字符作为字符串s的初值。
g)string s(num,c) //生成一个字符串,包含num个c字符
h)string s(beg,end) //以区间beg;end(不包含end)内的字符作为字符串s的初值
i)s.~string() //销毁所有字符,释放内存
都很简单,我就不解释了。
2.字符串操作函数
这里是C++字符串的重点,我先把各种操作函数罗列出来,不喜欢把所有函数都看完的人可以在这里找自己喜欢的函数,再到后面看他的详细解释。
a) =,assign() //赋以新值
b) swap() //交换两个字符串的内容
c) +=,append(),push_back() //在尾部添加字符
d) insert() //插入字符
e) erase() //删除字符
f) clear() //删除全部字符
g) replace() //替换字符
h) + //串联字符串
i) ==,!=,<,<=,>,>=,compare()//比较字符串
j) size(),length()//返回字符数量
k) max_size() //返回字符的可能最大个数
l) empty()//判断字符串是否为空
m) capacity() //返回重新分配之前的字符容量
n) reserve() //保留一定量内存以容纳一定数量的字符
o) [ ], at() //存取单一字符
p) >>,getline() //从stream读取某值
q) <r) copy() //将某值赋值为一个C_string
s) c_str() //将内容以C_string返回
t) data() //将内容以字符数组形式返回
u) substr() //返回某个子字符串
v)查找函数
w)begin() end() //提供类似STL的迭代器支持
x) rbegin() rend() //逆向迭代器
y) get_allocator() //返回配置器
下面详细介绍:
2.1 C++字符串和C字符串的转换
C+ +提供的由C++字符串得到对应的C_string的方法是使用data()、c_str()和copy(),其中,data()以字符数组的形式返回字符串内容,但并不添加’’。c_str()返回一个以‘’结尾的字符数组,而copy()则把字符串的内容复制或写入既有的c_string或字符数组内。C++字符串并不以’’结尾。我的建议是在程序中能使用C++字符串就使用,除非万不得已不选用c_string。由于只是简单介绍,详细介绍掠过,谁想进一步了解使用中的注意事项可以给我留言(到我的收件箱)。我详细解释。
2.2 大小和容量函数
一个C++字符串存在三种大小:a) 现有的字符数,函数是size()和length(),他们等效。Empty()用来检查字符串是否为空。b)max_size() 这个大小是指当前C++字符串最多能包含的字符数,很可能和机器本身的限制或者字符串所在位置连续内存的大小有关系。我们一般情况下不用关心他,应该大小足够我们用的。但是不够用的话,会抛出length_error异常c)capacity()重新分配内存之前 string所能包含的最大字符数。这里另一个需要指出的是reserve()函数,这个函数为string重新分配内存。重新分配的大小由其参数决定,默认参数为0,这时候会对string进行非强制性缩减。

还有必要再重复一下C++字符串和C字符串转换的问题,许多人会遇到这样的问题,自己做的程序要调用别人的函数、类什么的(比如数据库连接函数 Connect(char*,char*)),但别人的函数参数用的是char*形式的,而我们知道,c_str()、data()返回的字符数组由该字符串拥有,所以是一种const char*,要想作为上面提及的函数的参数,还必须拷贝到一个char*,而我们的原则是能不使用C字符串就不使用。那么,这时候我们的处理方式是:如果此函数对参数(也就是char*)的内容不修改的话,我们可以这样Connect((char*)UserID.c_str(), (char*)PassWD.c_str()),但是这时候是存在危险的,因为这样转换后的字符串其实是可以修改的(有兴趣地可以自己试一试),所以我强调除非函数调用的时候不对参数进行修改,否则必须拷贝到一个char*上去。当然,更稳妥的办法是无论什么情况都拷贝到一个char*上去。同时我们也祈祷现在仍然使用C字符串进行编程的高手们(说他们是高手一点儿也不为过,也许在我们还穿开裆裤的时候他们就开始编程了,哈哈…)写的函数都比较规范,那样我们就不必进行强制转换了。

2.3元素存取
我们可以使用下标操作符[]和函数at()对元素包含的字符进行访问。但是应该注意的是操作符[]并不检查索引是否有效(有效索引0~str.length()),如果索引失效,会引起未定义的行为。而at()会检查,如果使用at()的时候索引无效,会抛出 out_of_range异常。
有一个例外不得不说,const string a;的操作符[]对索引值是a.length()仍然有效,其返回值是’’。其他的各种情况,a.length()索引都是无效的。举例如下:
const string Cstr(“const string”);
string Str(“string”);

Str[3];//ok
Str.at(3);//ok

Str[100]; //未定义的行为
Str.at(100);//throw out_of_range

Str[Str.length()]//未定义行为
Cstr[Cstr.length()] //返回 ‘’
Str.at(Str.length());//throw out_of_range
Cstr.at(Cstr.length()) ////throw out_of_range

我不赞成类似于下面的引用或指针赋值:
char& r=s[2];
char* p= &s[3];
因为一旦发生重新分配,r,p立即失效。避免的方法就是不使用。

2.4比较函数
C++字符串支持常见的比较操作符(>,>=,<,<=,==,!=),甚至支持string与C -string的比较(如 str<”hello”)。在使用>,>=,<,<=这些操作符的时候是根据“当前字符特性”将字符按字典顺序进行逐一得比较。字典排序靠前的字符小,比较的顺序是从前向后比较,遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小。同时,string (“aaaa”) 另一个功能强大的比较函数是成员函数compare()。他支持多参数处理,支持用索引值和长度定位子串来进行比较。他返回一个整数来表示比较结果,返回值意义如下:0-相等 〉0-大于 <0-小于。举例如下:
string s(“abcd”);

s.compare(“abcd”); //返回0
s.compare(“dcba”); //返回一个小于0的值
s.compare(“ab”); //返回大于0的值

s.compare(s); //相等
s.compare(0,2,s,2,2); //用”ab”和”cd”进行比较 小于零
s.compare(1,2,”bcx”,2); //用”bc”和”bc”比较。
怎么样?功能够全的吧!什么?还不能满足你的胃口?好吧,那等着,后面有更个性化的比较算法。先给个提示,使用的是STL的比较算法。什么?对STL一窍不通?靠,你重修吧!

2.5 更改内容
这在字符串的操作中占了很大一部分。
首先讲赋值,第一个赋值方法当然是使用操作符=,新值可以是string(如:s=ns) 、c_string(如:s=”gaint”)甚至单一字符(如:s=’j’)。还可以使用成员函数assign(),这个成员函数可以使你更灵活的对字符串赋值。还是举例说明吧:
s.assign(str); //不说
s.assign(str,1,3);//如果str是”iamangel” 就是把”ama”赋给字符串
s.assign(str,2,string::npos);//把字符串str从索引值2开始到结尾赋给s
s.assign(“gaint”); //不说
s.assign(“nico”,5);//把’n’ ‘I’ ‘c’ ‘o’ ‘’赋给字符串
s.assign(5,’x’);//把五个x赋给字符串
把字符串清空的方法有三个:s=””;s.clear();s.erase();(我越来越觉得举例比说话让别人容易懂!)。
string提供了很多函数用于插入(insert)、删除(erase)、替换(replace)、增加字符。
先说增加字符(这里说的增加是在尾巴上),函数有 +=、append()、push_back()。举例如下:
s+=str;//加个字符串
s+=”my name is jiayp”;//加个C字符串
s+=’a’;//加个字符

s.append(str);
s.append(str,1,3);//不解释了 同前面的函数参数assign的解释
s.append(str,2,string::npos)//不解释了

s.append(“my name is jiayp”);
s.append(“nico”,5);
s.append(5,’x’);

s.push_back(‘a’);//这个函数只能增加单个字符 对STL熟悉的理解起来很简单

也许你需要在string中间的某个位置插入字符串,这时候你可以用insert()函数,这个函数需要你指定一个安插位置的索引,被插入的字符串将放在这个索引的后面。
s.insert(0,”my name”);
s.insert(1,str);
这种形式的insert()函数不支持传入单个字符,这时的单个字符必须写成字符串形式(让人恶心)。既然你觉得恶心,那就不得不继续读下面一段话:为了插入单个字符,insert()函数提供了两个对插入单个字符操作的重载函数:insert(size_type index,size_type num,chart c)和insert(iterator pos,size_type num,chart c)。其中size_type是无符号整数,iterator是char*,所以,你这么调用insert函数是不行的:insert(0,1, ’j’);这时候第一个参数将转换成哪一个呢?所以你必须这么写:insert((string::size_type)0,1,’j’)!第二种形式指出了使用迭代器安插字符的形式,在后面会提及。顺便提一下,string有很多操作是使用STL的迭代器的,他也尽量做得和STL靠近。
删除函数erase()的形式也有好几种(真烦!),替换函数replace()也有好几个。举例吧:
string s=”il8n”;
s.replace(1,2,”nternationalizatio”);//从索引1开始的2个替换成后面的C_string
s.erase(13);//从索引13开始往后全删除
s.erase(7,5);//从索引7开始往后删5个

2.6提取子串和字符串连接

题取子串的函数是:substr(),形式如下:
s.substr();//返回s的全部内容
s.substr(11);//从索引11往后的子串
s.substr(5,6);//从索引5开始6个字符
把两个字符串结合起来的函数是+。(谁不明白请致电120)

2.7输入输出操作
1.>> 从输入流读取一个string。
2.<< 把一个string写入输出流。
另一个函数就是getline(),他从输入流读取一行内容,直到遇到分行符或到了文件尾。

2.8搜索与查找
查找函数很多,功能也很强大,包括了:
find()
rfind()
find_first_of()
find_last_of()
find_first_not_of()
find_last_not_of()
这些函数返回符合搜索条件的字符区间内的第一个字符的索引,没找到目标就返回npos。所有的函数的参数说明如下:
第一个参数是被搜寻的对象。第二个参数(可有可无)指出string内的搜寻起点索引,第三个参数(可有可无)指出搜寻的字符个数。比较简单,不多说 不理解的可以向我提出,我再仔细的解答。当然,更加强大的STL搜寻在后面会有提及。
最后再说说npos的含义,string::npos的类型是string::size_type,所以,一旦需要把一个索引与npos相比,这个索引值必须是string::size)type类型的,更多的情况下,我们可以直接把函数和npos进行比较(如:if(s.find(“jia”)== string::npos))。

Read: 666