标签 MFC 下的文章

MFC超链接扩展类:Chris Maunder的CHyperLink类

这个CHyperLink超链接类非常实用,完全能够满足我的需求。CHyperLink扩展自CStatic。直接贴出.h和.cpp文件。

这个文件应该是最新的版本,最后更新是200年2月29日。

这个CHyperLink类是加拿大人Chris Maunder所写。

此项目主页:http://www.codeproject.com/Articles/34/Hyperlink-control

P.S. CButtonST项目也是使用的这个类。

HyperLink.h

// HyperLink.h : header file
//
//
// HyperLink static control. Will open the default browser with the given URL
// when the user clicks on the link.
//
// Copyright Chris Maunder, 1997-1999 (cmaunder@mail.com)
// Feel free to use and distribute. May not be sold for profit.

// 2/29/00 -- P. Shaffer standard font mod.

#if !defined(AFX_HYPERLINK_H__D1625061_574B_11D1_ABBA_00A0243D1382__INCLUDED_)
#define AFX_HYPERLINK_H__D1625061_574B_11D1_ABBA_00A0243D1382__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

/////////////////////////////////////////////////////////////////////////////
// CHyperLink window

class CHyperLink : public CStatic
{
// Construction/destruction
public:
CHyperLink();
virtual ~CHyperLink();

public:
enum UnderLineOptions { ulHover = -1, ulNone = 0, ulAlways = 1};

// Attributes
public:
void SetURL(CString strURL);
CString GetURL() const;

void SetColours(COLORREF crLinkColour, COLORREF crVisitedColour,
COLORREF crHoverColour = -1);
COLORREF GetLinkColour() const;
COLORREF GetVisitedColour() const;
COLORREF GetHoverColour() const;

void SetVisited(BOOL bVisited = TRUE);
BOOL GetVisited() const;

void SetLinkCursor(HCURSOR hCursor);
HCURSOR GetLinkCursor() const;

void SetUnderline(int nUnderline = ulHover);
int GetUnderline() const;

void SetAutoSize(BOOL bAutoSize = TRUE);
BOOL GetAutoSize() const;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CHyperLink)
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL DestroyWindow();
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL

// Implementation
protected:
HINSTANCE GotoURL(LPCTSTR url, int showcmd);
void ReportError(int nError);
LONG GetRegKey(HKEY key, LPCTSTR subkey, LPTSTR retdata);
void PositionWindow();
void SetDefaultCursor();

// Protected attributes
protected:
COLORREF m_crLinkColour, m_crVisitedColour; // Hyperlink colours
COLORREF m_crHoverColour; // Hover colour
BOOL m_bOverControl; // cursor over control?
BOOL m_bVisited; // Has it been visited?
int m_nUnderline; // underline hyperlink?
BOOL m_bAdjustToFit; // Adjust window size to fit text?
CString m_strURL; // hyperlink URL
CFont m_UnderlineFont; // Font for underline display
CFont m_StdFont; // Standard font
HCURSOR m_hLinkCursor; // Cursor for hyperlink
CToolTipCtrl m_ToolTip; // The tooltip
UINT m_nTimerID;

// Generated message map functions
protected:
//{{AFX_MSG(CHyperLink)
afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
//}}AFX_MSG
afx_msg void OnClicked();
DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_HYPERLINK_H__D1625061_574B_11D1_ABBA_00A0243D1382__INCLUDED_)

HyperLink.cpp

// HyperLink.cpp : implementation file
//
// HyperLink static control. Will open the default browser with the given URL
// when the user clicks on the link.
//
// Copyright (C) 1997 - 1999 Chris Maunder
// All rights reserved. May not be sold for profit.
//
// Thanks to P錶 K. T鴑der for auto-size and window caption changes.
//
// "GotoURL" function by Stuart Patterson
// As seen in the August, 1997 Windows Developer's Journal.
// Copyright 1997 by Miller Freeman, Inc. All rights reserved.
// Modified by Chris Maunder to use TCHARs instead of chars.
//
// "Default hand cursor" from Paul DiLascia's Jan 1998 MSJ article.
//
// 2/29/00 -- P. Shaffer standard font mod.

#include "stdafx.h"
#include "HyperLink.h"

#include "atlconv.h" // for Unicode conversion - requires #include <afxdisp.h> // MFC OLE automation classes

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define TOOLTIP_ID 1

/////////////////////////////////////////////////////////////////////////////
// CHyperLink

CHyperLink::CHyperLink()
{
m_hLinkCursor = NULL; // No cursor as yet
m_crLinkColour = RGB( 0, 0, 238); // Blue
m_crVisitedColour = RGB( 85, 26, 139); // Purple
m_crHoverColour = RGB(255, 0, 0); // Red
m_bOverControl = FALSE; // Cursor not yet over control
m_bVisited = FALSE; // Hasn't been visited yet.
m_nUnderline = ulHover; // Underline the link?
m_bAdjustToFit = TRUE; // Resize the window to fit the text?
m_strURL.Empty();
m_nTimerID = 100;
}

CHyperLink::~CHyperLink()
{
m_UnderlineFont.DeleteObject();
}

/////////////////////////////////////////////////////////////////////////////
// CHyperLink overrides

BOOL CHyperLink::DestroyWindow()
{
KillTimer(m_nTimerID);

return CStatic::DestroyWindow();
}

BOOL CHyperLink::PreTranslateMessage(MSG* pMsg)
{
m_ToolTip.RelayEvent(pMsg);
return CStatic::PreTranslateMessage(pMsg);
}


void CHyperLink::PreSubclassWindow()
{
// We want to get mouse clicks via STN_CLICKED
DWORD dwStyle = GetStyle();
::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyle | SS_NOTIFY);

// Set the URL as the window text
if (m_strURL.IsEmpty())
GetWindowText(m_strURL);

// Check that the window text isn't empty. If it is, set it as the URL.
CString strWndText;
GetWindowText(strWndText);
if (strWndText.IsEmpty())
{
ASSERT(!m_strURL.IsEmpty()); // Window and URL both NULL. DUH!
SetWindowText(m_strURL);
}

CFont* pFont = GetFont();
if (!pFont)
{
HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
if (hFont == NULL)
hFont = (HFONT) GetStockObject(ANSI_VAR_FONT);
if (hFont)
pFont = CFont::FromHandle(hFont);
}
ASSERT(pFont->GetSafeHandle());

// Create the underline font
LOGFONT lf;
pFont->GetLogFont(&lf);
m_StdFont.CreateFontIndirect(&lf);
lf.lfUnderline = (BYTE) TRUE;
m_UnderlineFont.CreateFontIndirect(&lf);

PositionWindow(); // Adjust size of window to fit URL if necessary
SetDefaultCursor(); // Try and load up a "hand" cursor
SetUnderline();

// Create the tooltip
CRect rect;
GetClientRect(rect);
m_ToolTip.Create(this);
m_ToolTip.AddTool(this, m_strURL, rect, TOOLTIP_ID);

CStatic::PreSubclassWindow();
}

BEGIN_MESSAGE_MAP(CHyperLink, CStatic)
//{{AFX_MSG_MAP(CHyperLink)
ON_WM_CTLCOLOR_REFLECT()
ON_WM_SETCURSOR()
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
ON_CONTROL_REFLECT(STN_CLICKED, OnClicked)
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CHyperLink message handlers

void CHyperLink::OnClicked()
{
m_bOverControl = FALSE;
int result = (int)GotoURL(m_strURL, SW_SHOW);
m_bVisited = (result > HINSTANCE_ERROR);
if (!m_bVisited)
{
MessageBeep(MB_ICONEXCLAMATION); // Unable to follow link
ReportError(result);
}
else
SetVisited(); // Repaint to show visited colour
}

HBRUSH CHyperLink::CtlColor(CDC* pDC, UINT nCtlColor)
{
ASSERT(nCtlColor == CTLCOLOR_STATIC);

if (m_bOverControl)
pDC->SetTextColor(m_crHoverColour);
else if (m_bVisited)
pDC->SetTextColor(m_crVisitedColour);
else
pDC->SetTextColor(m_crLinkColour);

// transparent text.
pDC->SetBkMode(TRANSPARENT);
return (HBRUSH)GetStockObject(NULL_BRUSH);
}

void CHyperLink::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bOverControl) // Cursor has just moved over control
{
m_bOverControl = TRUE;

if (m_nUnderline == ulHover)
SetFont(&m_UnderlineFont);
Invalidate();

SetTimer(m_nTimerID, 100, NULL);
}
CStatic::OnMouseMove(nFlags, point);
}

void CHyperLink::OnTimer(UINT nIDEvent)
{
CPoint p(GetMessagePos());
ScreenToClient(&p);

CRect rect;
GetClientRect(rect);
if (!rect.PtInRect(p))
{
m_bOverControl = FALSE;
KillTimer(m_nTimerID);

if (m_nUnderline != ulAlways)
SetFont(&m_StdFont);
rect.bottom+=10;
InvalidateRect(rect);
}

CStatic::OnTimer(nIDEvent);
}

BOOL CHyperLink::OnSetCursor(CWnd* /*pWnd*/, UINT /*nHitTest*/, UINT /*message*/)
{
if (m_hLinkCursor)
{
::SetCursor(m_hLinkCursor);
return TRUE;
}
return FALSE;
}

BOOL CHyperLink::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(rect);
pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));

return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CHyperLink operations

void CHyperLink::SetURL(CString strURL)
{
m_strURL = strURL;

if (::IsWindow(GetSafeHwnd())) {
PositionWindow();
m_ToolTip.UpdateTipText(strURL, this, TOOLTIP_ID);
}
}

CString CHyperLink::GetURL() const
{
return m_strURL;
}

void CHyperLink::SetColours(COLORREF crLinkColour, COLORREF crVisitedColour,
COLORREF crHoverColour /* = -1 */)
{
m_crLinkColour = crLinkColour;
m_crVisitedColour = crVisitedColour;

if (crHoverColour == -1)
m_crHoverColour = ::GetSysColor(COLOR_HIGHLIGHT);
else
m_crHoverColour = crHoverColour;

if (::IsWindow(m_hWnd))
Invalidate();
}

COLORREF CHyperLink::GetLinkColour() const
{
return m_crLinkColour;
}

COLORREF CHyperLink::GetVisitedColour() const
{
return m_crVisitedColour;
}

COLORREF CHyperLink::GetHoverColour() const
{
return m_crHoverColour;
}

void CHyperLink::SetVisited(BOOL bVisited /* = TRUE */)
{
m_bVisited = bVisited;

if (::IsWindow(GetSafeHwnd()))
Invalidate();
}

BOOL CHyperLink::GetVisited() const
{
return m_bVisited;
}

void CHyperLink::SetLinkCursor(HCURSOR hCursor)
{
m_hLinkCursor = hCursor;
if (m_hLinkCursor == NULL)
SetDefaultCursor();
}

HCURSOR CHyperLink::GetLinkCursor() const
{
return m_hLinkCursor;
}

void CHyperLink::SetUnderline(int nUnderline /*=ulHover*/)
{
if (m_nUnderline == nUnderline)
return;

if (::IsWindow(GetSafeHwnd()))
{
if (nUnderline == ulAlways)
SetFont(&m_UnderlineFont);
else
SetFont(&m_StdFont);

Invalidate();
}

m_nUnderline = nUnderline;
}

int CHyperLink::GetUnderline() const
{
return m_nUnderline;
}

void CHyperLink::SetAutoSize(BOOL bAutoSize /* = TRUE */)
{
m_bAdjustToFit = bAutoSize;

if (::IsWindow(GetSafeHwnd()))
PositionWindow();
}

BOOL CHyperLink::GetAutoSize() const
{
return m_bAdjustToFit;
}


// Move and resize the window so that the window is the same size
// as the hyperlink text. This stops the hyperlink cursor being active
// when it is not directly over the text. If the text is left justified
// then the window is merely shrunk, but if it is centred or right
// justified then the window will have to be moved as well.
//
// Suggested by P錶 K. T鴑der

void CHyperLink::PositionWindow()
{
if (!::IsWindow(GetSafeHwnd()) || !m_bAdjustToFit)
return;

// Get the current window position
CRect WndRect, ClientRect;
GetWindowRect(WndRect);
GetClientRect(ClientRect);

ClientToScreen(ClientRect);

CWnd* pParent = GetParent();
if (pParent)
{
pParent->ScreenToClient(WndRect);
pParent->ScreenToClient(ClientRect);
}

// Get the size of the window text
CString strWndText;
GetWindowText(strWndText);

CDC* pDC = GetDC();
CFont* pOldFont = pDC->SelectObject(&m_UnderlineFont);
CSize Extent = pDC->GetTextExtent(strWndText);
pDC->SelectObject(pOldFont);
ReleaseDC(pDC);

// Adjust for window borders
Extent.cx += WndRect.Width() - ClientRect.Width();
Extent.cy += WndRect.Height() - ClientRect.Height();

// Get the text justification via the window style
DWORD dwStyle = GetStyle();

// Recalc the window size and position based on the text justification
if (dwStyle & SS_CENTERIMAGE)
WndRect.DeflateRect(0, (WndRect.Height() - Extent.cy)/2);
else
WndRect.bottom = WndRect.top + Extent.cy;

if (dwStyle & SS_CENTER)
WndRect.DeflateRect((WndRect.Width() - Extent.cx)/2, 0);
else if (dwStyle & SS_RIGHT)
WndRect.left = WndRect.right - Extent.cx;
else // SS_LEFT = 0, so we can't test for it explicitly
WndRect.right = WndRect.left + Extent.cx;

// Move the window
SetWindowPos(NULL, WndRect.left, WndRect.top, WndRect.Width(), WndRect.Height(), SWP_NOZORDER);
}

/////////////////////////////////////////////////////////////////////////////
// CHyperLink implementation

// The following appeared in Paul DiLascia's Jan 1998 MSJ articles.
// It loads a "hand" cursor from the winhlp32.exe module
void CHyperLink::SetDefaultCursor()
{
if (m_hLinkCursor == NULL) // No cursor handle - load our own
{
// Get the windows directory
CString strWndDir;
GetWindowsDirectory(strWndDir.GetBuffer(MAX_PATH), MAX_PATH);
strWndDir.ReleaseBuffer();

strWndDir += _T("\\winhlp32.exe");
// This retrieves cursor #106 from winhlp32.exe, which is a hand pointer
HMODULE hModule = LoadLibrary(strWndDir);
if (hModule) {
HCURSOR hHandCursor = ::LoadCursor(hModule, MAKEINTRESOURCE(106));
if (hHandCursor)
m_hLinkCursor = CopyCursor(hHandCursor);
}
FreeLibrary(hModule);
}
}

LONG CHyperLink::GetRegKey(HKEY key, LPCTSTR subkey, LPTSTR retdata)
{
HKEY hkey;
LONG retval = RegOpenKeyEx(key, subkey, 0, KEY_QUERY_VALUE, &hkey);

if (retval == ERROR_SUCCESS) {
long datasize = MAX_PATH;
TCHAR data[MAX_PATH];
RegQueryValue(hkey, NULL, data, &datasize);
lstrcpy(retdata,data);
RegCloseKey(hkey);
}

return retval;
}

void CHyperLink::ReportError(int nError)
{
CString str;
switch (nError) {
case 0: str = "The operating system is out\nof memory or resources."; break;
case SE_ERR_PNF: str = "The specified path was not found."; break;
case SE_ERR_FNF: str = "The specified file was not found."; break;
case ERROR_BAD_FORMAT: str = "The .EXE file is invalid\n(non-Win32 .EXE or error in .EXE image)."; break;
case SE_ERR_ACCESSDENIED: str = "The operating system denied\naccess to the specified file."; break;
case SE_ERR_ASSOCINCOMPLETE: str = "The filename association is\nincomplete or invalid."; break;
case SE_ERR_DDEBUSY: str = "The DDE transaction could not\nbe completed because other DDE transactions\nwere being processed."; break;
case SE_ERR_DDEFAIL: str = "The DDE transaction failed."; break;
case SE_ERR_DDETIMEOUT: str = "The DDE transaction could not\nbe completed because the request timed out."; break;
case SE_ERR_DLLNOTFOUND: str = "The specified dynamic-link library was not found."; break;
case SE_ERR_NOASSOC: str = "There is no application associated\nwith the given filename extension."; break;
case SE_ERR_OOM: str = "There was not enough memory to complete the operation."; break;
case SE_ERR_SHARE: str = "A sharing violation occurred. ";
default: str.Format(_T("Unknown Error (%d) occurred."), nError); break;
}
str = "Unable to open hyperlink:\n\n" + str;
AfxMessageBox(str, MB_ICONEXCLAMATION | MB_OK);
}

HINSTANCE CHyperLink::GotoURL(LPCTSTR url, int showcmd)
{
TCHAR key[MAX_PATH + MAX_PATH];

// First try ShellExecute()
HINSTANCE result = ShellExecute(NULL, _T("open"), url, NULL,NULL, showcmd);

// If it failed, get the .htm regkey and lookup the program
if ((UINT)result <= HINSTANCE_ERROR) {

if (GetRegKey(HKEY_CLASSES_ROOT, _T(".htm"), key) == ERROR_SUCCESS) {
lstrcat(key, _T("\\shell\\open\\command"));

if (GetRegKey(HKEY_CLASSES_ROOT,key,key) == ERROR_SUCCESS) {
TCHAR *pos;
pos = _tcsstr(key, _T("\"%1\""));
if (pos == NULL) { // No quotes found
pos = _tcsstr(key, _T("%1")); // Check for %1, without quotes
if (pos == NULL) // No parameter at all...
pos = key+lstrlen(key)-1;
else
*pos = '\0'; // Remove the parameter
}
else
*pos = '\0'; // Remove the parameter

lstrcat(pos, _T(" "));
lstrcat(pos, url);

USES_CONVERSION;
result = (HINSTANCE) WinExec(T2A(key),showcmd);
}
}
}

return result;
}

MFC中stdafx中afx前缀是什么意思有什么含义?

afx表示Application Framework eXtensions, 在Wikipedia页面中Application Framework eXtensions显示的标题却是Microsoft Foundation Class Library,说明他们是同一个东西。

以下来自维基百科的内容写道:

One interesting quirk of MFC is the use of "Afx" as the prefix for many functions, macros and the standard precompiled header name "stdafx.h". During early development what became MFC was called "Application Framework Extensions" and abbreviated "Afx". The name Microsoft Foundation Classes (MFC) was adopted too late in the release cycle to change these references.

MFC一个奇怪的地方是在大量函数、宏、预定义头文件中使用“AFX”前缀。这是因为在MFC的早期开发中,MFC的名字叫“Application Framework Extensions”(程序框架扩展),缩写是“Afx”。在MFC的后期发行周期中将Afx前缀修改掉已经是“不可挽回”了。这就是“Afx”的由来。

The old Stingray MFC FAQ中写道 :

In the beginning, Microsoft created a group called the AFX group (stands for (A)pplication (F)ramework(X)).

起初微软建立了一个 AFX小组。

[...]

The AFX group was actually responsible for two things: the MFC library and the IDE's support for MFC (namely, the resource editor and the wizards). The AFX name was dropped in April 1994, and the group's members simply became part of smaller teams within the Visual C++ group. One of those smaller teams is today's MFC team.

AFX小组负责两个事情:MFC库和IDE对MFC的支持(也就是Resource Editor、App Wizard、Class Wizard等)。1994年后弃用AFX这个名字。AFX小组成为了Visual C++小组的下属部门。这个下属部门组成了部分的MFC团队。

更多资料参考:

http://en.wikipedia.org/wiki/Stdafx.h

error C2065: '_tcstoi64' : undeclared identifier

I aim to use _tcstoi64 to convert a char* buffer to __int64. but compile cannot pass.

error C2065: '_tcstoi64' : undeclared identifier _tcstoi64 is defined in TCHAR.h and I've already include TCHAR.h

I am using VC++ 6.0 SP6 with PSDK 2003 Feberary with _UNICODE defined.

my code:

#include <stdlib.h>
#include <TCHAR.H>
//..
__int64 iVal = _tcstoi64(szTest, NULL, 16);

众所周知CRT函数_ttoi64可以字符串转为数字,但仅限于十进制字符串。像0x34BF就没辙了。这个时候可以使用_tcstoi64第三个参数来指定进制。但是在VC6里面报错_tcstoi64未识别的标识符。原因是VC6的CRT库函数里面根本就没有这个函数! 不信请看下图。左边是VC6的TCHAR.H,右边是VC10的TCHAR.H凡是后面加了64的都没有支持。想想98年VC6出道的时候哪来神马64位哦。吼吼。。。

tcstoi64

参见MS Answer解答。

多次子类化造成Debug Assertion Failed! wincore.cpp Line: 320

一个普通的MFC程序Release下没有任何问题。其实可能还存在潜在的其他问题。今天为了调试内存泄漏的问题。把程序编译成Debug版然后开始调试就报断言失败。

这次的报错的地方在MFC的源文件wincore.cpp的320行。

BOOL CWnd::Attach(HWND hWndNew)
{
    ASSERT(m_hWnd == NULL);     // only attach once, detach on destroy
    ASSERT(FromHandlePermanent(hWndNew) == NULL);
        // must not already be in permanent map

    if (hWndNew == NULL)
        return FALSE;

    CHandleMap* pMap = afxMapHWND(TRUE); // create map if not exist
    ASSERT(pMap != NULL);

    pMap->SetPermanent(m_hWnd = hWndNew, this);

#ifndef _AFX_NO_OCC_SUPPORT
    AttachControlSite(pMap);
#endif

    return TRUE;
}

320行在:

ASSERT(m_hWnd == NULL);

一个普通的对话框程序要动态创建三个对话框。单步调试到第一个没问题。第二个走到 CXXDlg->Create(....)的时候就报Asserting Failed。

这次应该想到第二个对话框的一些特殊的代码。没错。我子类化了一个CEdit。

我写了一个CExtEdit类。并且用ClassWizard绑定了一个变量CExtEdit m_ctlExtEdit。完了还在CXXDlg::OnInitDialog()里面加入了m_ctlExtEdit.SubClassDlgItem(...)。这样就造成了m_ctlExtEdit的重复子类化。

解决的方法有两个:

  1. 用ClassWizard去掉变量绑定m_ctlExtEdit,动态的创建一个CExtEdit。
  2. 去掉多余的.SubClassDlgItem函数。

Debug Assertion Failed! File: dbgdel.cpp Line: 47 _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

---------------------------
Microsoft Visual C++ Debug Library
---------------------------
Debug Assertion Failed!

Program: D:\dev\out\test.exe
File: dbgdel.cpp
Line: 47

Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)
---------------------------
中止(A)   重试(R)   忽略(I)   
---------------------------

原因找到了。PTCHAR test = new TCHAR[1024]之后使用了test++之类的语句后delete[] test。 这样delete指向的并非test的头。所以报错。可以在new完之后用一个PTCHAR来保存head pointer: testHead = test。 然后delete[] testHead。