在黑暗中举起探索的火炬的网志
在喧闹、混杂的生活中
你应该与你的心灵和平相处
尽管这世上有很多假冒和欺骗
有很多单调乏味的工作
和众多破灭的梦幻
它仍然是一个美好的世界
记住:你应该努力去追求幸福。
是的,记住:你应该努力去追求幸福。
每个早晨灿烂的太阳升起的时候,
每个人都应
-
2004-04-29
[转载] 透明窗体的实现 - [C/C++开发专辑]
本文转载自VCROAD,作者南海昭信.
一、背景
FlashGet的透明效果大家羡慕吧.传统的Windows应用程序想实现半透明效果,一般来说需要处理自己的窗口的WM_Paint消息窗口,很麻烦.现在好了,SetLayeredWindowAttributes是windows的新api,win2000以上才支持,它能使使窗体拥有透明效果.我在Google搜了下,介绍SetLayeredWindowAttributes的文章大多是delphi的和vb的.好不容易找到一篇vc的,依法炮制后,vc的IDE却说我SetLayeredWindowAttributes没有定义!后来想想应该是我的sdk没有升级.于是我在vc安装目录搜索"SetLayeredWindowAttributes"的"*.h"文件,果然没有.怎么办?升级sdk吧.我去微软的网站一看,新的sdk就核心sdk就有二百多m呢(解压后更大),可怜我的硬盘没有一个分区大于200m的了!怎么办,这么好玩的api给看不给用:( 失望之余,我忽然想到了未公开api的使用的方法.这是个系统支持,自己sdk却没有的api,就把他当做windows未公开api试试!
二、简单介绍一下SetLayeredWindowAttributes:(详见msdn)
BOOL SetLayeredWindowAttributes(
HWND hwnd, // handle to the layered window
COLORREF crKey, // specifies the color key
BYTE bAlpha, // value for the blend function
DWORD dwFlags // action
);
Windows NT/2000/XP: Included in Windows 2000 and later.
Windows 95/98/Me: Unsupported.
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.
一些常量:
WS_EX_LAYERED = 0x80000;
LWA_ALPHA = 0x2;
LWA_COLORKEY=0x1
其中dwFlags有LWA_ALPHA和LWA_COLORKEY
LWA_ALPHA被设置的话,通过bAlpha决定透明度.
LWA_COLORKEY被设置的话,则指定被透明掉的颜色为crKey,其他颜色则正常显示.
注:要使使窗体拥有透明效果,首先要有WS_EX_LAYERED扩展属性(旧sdk也没有的).
三、例子代码:
在OnInitDialog()加入:
//加入WS_EX_LAYERED扩展属性
SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,
GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)^0x80000);
HINSTANCE hInst = LoadLibrary("User32.DLL");
if(hInst)
{
typedef BOOL (WINAPI *MYFUNC)(HWND,COLORREF,BYTE,DWORD);
MYFUNC fun = NULL;
//取得SetLayeredWindowAttributes函数指针
fun=(MYFUNC)GetProcAddress(hInst, "SetLayeredWindowAttributes");
if(fun)fun(this->GetSafeHwnd(),0,128,2);
FreeLibrary(hInst);
}
唉!如果装了最新sdk就不用那么麻烦了!
怎么样,效果不错吧!稍加修改还可以作出淡出淡入的效果. 注意第三个参数(128)不要取得太小了,为0的话完全透明,你就找不到窗体了!
小小心得,一吐为快.希望对初学者有所帮助.如有不妥,欢迎指正. -
2004-04-29
[转贴] C++常用排序算法 - [C/C++开发专辑]
本文转贴自CSDN文档中心.
//选择排序法SelectionSort(int arr[],int n)
template <typename T>
void SelectionSort(T arr[],int n)
{
int smallIndex; //表中最小元素的下标
int pass,j; //用来扫描子表的下标
T temp; //用来交换表元素的临时变量
//pass的范围是0~n-2
for (pass=0;pass<n-1;pass++)
{
//从下标pass开始扫描子表
smallIndex=pass;
//j遍历整个子表arr[pass+1]到arr[n-1]
for(j=pass+1;j<n;j++)
//如果找到更小的元素,则将该位置赋值给smallIndex
if(arr[j]<arr[smallIndex])
smallIndex=j;
//如果smallIndex和pass不在相同的位置
//则将子表中的最小项与arr[pass]交换
if(smallIndex!=pass)
{
temp=arr[pass];
arr[pass]=arr[smallIndex];
arr[smallIndex]=temp;
}
}
}
/************************************************************************
双端选择排序算法:是上面选择排序算法的变种,可以定位每个子表中最小和最大元素
并把它们分别放在子表的开头和结尾.
************************************************************************/
//双端选择排序算法函数deSelSort()的实现
template <typename T>
void deSelSort(T arr[],int n)
{
int smallIndex,largeIndex; //表中最小及最大元素的下标
int leftPass=0,rightPass=n-1,i,j; //用来从表左边及右边扫描子表的下标
T temp; //用于交换元素的临时变量
while (leftPass<=rightPass)
{
//从左边及右边开始扫描子表
smallIndex=leftPass;
largeIndex=rightPass;
//j和i遍历整个子表arr[LeftPass]~arr[rightPass]
for (i=leftPass+1;i<rightPass;i++)
//如果找到更小的元素,则将该位置赋值给smallIndex
if (arr[i]<arr[smallIndex])
smallIndex=i;
//如果smallIndex和leftPass不在相同的位置
//则将子表中的最小项与arr[pass]交换
if (smallIndex!=leftPass)
{
temp=arr[leftPass];
arr[leftPass]=arr[smallIndex];
arr[smallIndex]=temp;
}
for (j=rightPass-1;j>leftPass;j--)
if(arr[j]>arr[largeIndex])
largeIndex=j;
if(largeIndex!=rightPass)
{
temp=arr[rightPass];
arr[rightPass]=arr[largeIndex];
arr[largeIndex]=temp;
}
//从两头收缩子表
leftPass++;
rightPass--;
}
}
//自编冒泡法排序算法函数bubbleSort()的实现
template <typename T>
int bubbleSort(T arr[],int n)
{
bool exchanged=false; //是否发生交换
int i,j; //用于遍历子表的下标
T temp; //用于交换元素的临时变量
//开始遍历过程,以下标j构成子表,共有n-1个子表
for (j=n-1;j>=0;j--) //j从后往前收缩n-1~0,以构成子表0~n-1,0~n-2,0~n-3..0~1
{
exchanged=false;
for (i=0;i<j;i++) //遍历子表范围0~j
{
if (arr[i]>arr[i+1])
{
temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
exchanged=true;
}
}
if (!exchanged) return n-j-1;//如果在一次遍历中没有发生交换,则表示已经
//排序好,中断遍历过程
}
return n-1-j;
}
//冒泡法排序一般算法函数bubbleSortEx()的实现
template <typename T>
int bubbleSortEx(T arr[],int n)
{
int i,pass; //用于遍历子表的下标
T temp; //用于交换元素的临时变量
//开始遍历过程,以下标j构成子表,共有n-1个子表
for (pass=0;pass<n;pass++) //pass从后往后扩大0~n-1,以构成子表0~n-1,0~n-2,0~n-3..0~1
{
for (i=0;i<n-pass;i++) //遍历子表范围0~n-pass
{
if (arr[i]>arr[i+1])
{
temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
}
return pass;
} -
2004-04-29
[转载] Visual C++ 编程问答(三) - [VC开发专辑]
60.在用户环境中如何确定系统显示元素的颜色?
调用SDK函数GetSysColor可以获取一个特定显示元素的颜色。下例说明了如何在MFC函数CMainFrameWnd:: OnNcPaint中调用该函数设置窗口标题颜色。
void CMiniFrameWnd:: OnNcPaint ()
{
…
dc.SetTextColor (:: GetSysColor (m_bActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT))
…
61.如何查询和设置系统参数?
在Windows 3.1 SDK中介绍过SDK函数SystemParametersInfo,调用该函数可以查询和设置系统参数,诸如按键的重复速率设置、鼠标双击延迟时间、图标字体以及桌面覆盖位图等等。
//Create a font that is used for icon titles.
LOGFONT stFont
∶: SystemParametersInfo (SPIF_GETICONTITLELOGFONT, sizeof (LOGFONT), &stFont, SPIF_SENDWININICHANGE)
m_font.CreateFontIndirect (&stFont)
//Change the wallpaper to leaves.bmp.
∶ : SystemParametersInfo (SPI_SETDESKWALLPAPER, 0, _T (" forest.bmp"), SPIF_UPDATEINIFILE)
62.如何使用一个预定义的Windows光标?
调用CWinApp:: LoadStandardCursor并传送光标标识符。
BOOL CSampleDialog:: OnSetCursor (CWnd* pWnd,
UINT nHitTest, UINT
message)
{
//Display wait cursor if busy.
if (m_bBusy)
{
SetCursor (AfxGetApp () ->LoadStandardCursor (IDC_WAIT))
return TRUE
}
return CDialog:: OnSetCursor (pWnd. nHitTest,message)
}
63.如何确定当前屏幕分辨率?
调用SDK函数GetSystemMetrics,该函数可以检索有关windows显示信息,诸如标题大小、边界大小以及滚动条大小等等。
//Initialize CSize object with screen size.
CSize sizeScreen (GetSystemMetrics (SM_CXSCREEN),
GetSystemMetrics (SM_CYSCREEN))
64.如何检索原先的Task Manager应用程序使用的任务列表?
原先的Task Manager应用程序显示顶层窗口的列表。为了显示该列表,窗口必须可见、包含一个标题以及不能被其他窗口拥有。调用CWnd:: GetWindow可以检索顶层窗口的列表,调用IsWindowVisible、GetWindowTextLength以及GetOwner可以确定窗口是否应该在列表中。下例将把TaskManager窗口的标题填充到列表中。
void GetTadkList (CListBox&list)
{
CString strCaption
//Caption of window.
list.ResetContent ()
//Clear list box.
//Get first Window in window list.
ASSERT_VALID (AfxGetMainWnd ())
CWnd* pWnd=AfxGetMainWnd () ->GetWindow (GW_HWNDFIRST)
//Walk window list.
while (pWnd)
{
// I window visible, has a caption, and does not have an owner?
if (pWnd ->IsWindowVisible()
&& pWnd ->GetWindowTextLength ()
&&! pWnd ->GetOwner ())
{
//Add caption o window to list box.
pWnd ->GetWindowText (strCaption)
list.AddString (strCaption)
}
//Get next window in window list.
pWnd=pWnd ->GetWindow(GW_HWNDNEXT)
}
}
65.如何确定Windows和Windows系统目录?
有两个SDK函数可以完成该功能。GetWindowsDirectory和GetSystemDirectory,下例说明了如何使用这两个函数:
TCHAR szDir [MAX_PATH]
//Get the full path of the windows directory.
∶ : GetWindowsDirectory (szDir, MAX_PATH)
TRACE ("Windows directory %s\n", szDir)
//Get the full path of the windows system directory.
∶ : GetSystemDirectory (szDir, MAX_PATH)
TRACE ("Windows system directory %s\n", szDir)
66.在哪儿创建临文件?
调用SDK函数GetTemPath可以确定临时文件的目录,该函数首先为临时路径检测TMP环境变量:如果没有指定TMP,检测TMP环境变量,然后返回到当前目录。下例说明了如何创建一个临时文件。
…
//get unique temporary file.
CString strFile
GetUniqueTempName (strFile)
TRY
{
//Create file and write data.Note that file is closed
//in the destructor of the CFile object.
CFile file (strFile,CFile ::modeCreate | CFile:: modeWrite)
//write data
}
CATCH (CFileException, e)
{
//error opening file
}
END_CATCH
…
Void GetuniqueTempName (CString& strTempName)
{
//Get the temporary files directory.
TCHAR szTempPath [MAX_PATH]
DWORD dwResult=:: GetTempPath (MAX_PATH, szTempPath)
ASSERT (dwResult)
//Create a unique temporary file.
TCHAR szTempFile [MAX_PATH]
UINT nResult=GetTempFileName (szTempPath, _T ("~ex"),0,szTempfile)
ASSERT (nResult)
strTempName=szTempFile
}
67.如何访问桌面窗口?
静态函数CWnd:: GetDesktopWindow 返回桌面窗口的指针。下例说明了MFC函数CFrameWnd::BeginModalStae是如何使用该函数进入内部窗口列表的。
void CFrameWnd::BeginModalSt -
2004-04-29
[转载] Visual C++ 编程问答(二) - [VC开发专辑]
31.如何创建和使用无模式对话框?
MFC将模式和无模式对话封装在同一个类中,但是使用无模式对话需要几个对话需要几个额处的步骤。首先,使用资源编辑器建立对话资源并使用ClassWizard创建一个CDialog的派生类。模式和无模式对话的中止是不一样的:模式对话通过调用CDialog : : EndDialog 来中止,无模式对话则是调用CWnd: : DestroyWindow来中止的,函数CDialog : : OnOK和CDialog : : OnCancel调用EndDialog ,所以需要调用DestroyWindow并重置无模式对话的函数。
void CSampleDialog : : OnOK ( )
{
// Retrieve and validate dialog data .
if (! UpdateData (TRUE) )
{
// the UpdateData rountine
will set focus to correct item TRACEO (" UpdateData failed during dialog termination .\n")
return
}
//Call DestroyWindow instead of EndDialog .
DestroyWindow ( )
}
void CSampleDialog : : OnCancel ( )
{
//Call DestroyWindow instead of EndDialog .
DestroyWindow ( )
}
其次,需要正确删除表示对话的C++对象。对于模式对来说,这很容易,需要创建函数返回后即可删除C++对象;无模式对话不是同步的,创建函数调用后立即返回,因而用户不知道何时删除C++对象。撤销窗口时工作框调用CWnd : : PostNcDestroy,可以重置该函数并执行清除操作,诸如删除this指针。
void CSampleDialog : : PostNcDestroy ( )
{
// Declete the C++ object that represents this dialog.
delete this
最后,要创建无模式对话。可以调用CDialog : : DoModal创建一个模式对放,要创建一个无模式对话则要调用CDialog: : Create。下面的例子说明 了应用程序是如何创建无模式对话的: 象;无模式对话不是同步的,创建函数调用后立即返回,
void CMainFrame : : OnSampleDialog ( )
{
//Allocate a modeless dialog object .
CSampleDilog * pDialog =new CSampleDialog
ASSERT_VALID (pDialog) Destroy ( )
//Create the modeless dialog . represents this dialog.
BOOL bResult = pDialog -> Creste (IDD_IDALOG)
ASSERT (bResult )
}
32.如何在对话框中显示一个位图?
这要归功于Win 32先进的静态控件和Microsoft的资源编辑器,在对话框中显示位图是很容易的, 只需将图形控件拖到对话中并选择适当属性即可,用户也可以显示图标、位图以及增强型元文件。
33.如何改变对话或窗体视窗的背景颜色?
调用CWinApp : : SetDialogBkColor可以改变所有应用程序的背景颜色。第一个参数指定了背景颜色,第二个参数指定了文本颜色。下例将应用程序对话设置为蓝色背景和黄色文本。
BOOL CSampleApp : : InitInstance ( )
{
…
//use blue dialog with yellow text .
SetDialogBkColor (RGB (0, 0, 255 ), RGB ( 255 ,255 , 0 ) )
…
}
需要重画对话(或对话的子控件)时,Windows向对话发送消息WM_CTLCOLOR,通常用户可以让Windows选择绘画背景的刷子,也可重置该消息指定刷子。下例说明了创建一个红色背景对话的步骤。
首先,给对话基类增加一人成员变量
CBursh :class CMyFormView : public CFormView
{
…
private :
CBrush m_ brush // background brush
…
}
其次, 在类的构造函数中将刷子初始化为所需要的背景颜色。
CMyFormView : : CMyFormView ( )
{
// Initialize background brush .
m_brush .CreateSolidBrush (RGB ( 0, 0, 255) )
}
最后,使用ClassWizard处理WM_CTLCOLOR消息并返回一个用来绘画对话背景的刷子句柄。注意:由于当重画对话控件时也要调用该函数,所以要检测nCtlColor参量。
HBRUSH CMyFormView : : OnCtlColor (CDC* pDC , CWnd*pWnd , UINT nCtlColor
)
{
// Determine if drawing a dialog box . If we are, return +handle to
//our own background brush . Otherwise let windows handle it .
if (nCtlColor = = CTLCOLOR _ DLG )
return (HBRUSH) m_brush.GetSafeHandle ( )
return CFormView : : OnCtlColor (pDC, pWnd , nCtlColor
)
}
34.如何获取一个对话控件的指针?
有两种方法。其一,调用CWnd: : GetDlgItem,获取一个CWnd*指针调用成员函数。下例调用GetDlgItem,将返回值传给一个CSpinButtonCtrl*以便调用CSpinButtonCtrl : : SetPos 函数:
BOOL CSampleDialog : : OnInitDialog ( )
{
CDialog : : OnInitDialog ( )
//Get pointer to spin button .
CSpinButtonCtrl * pSpin - ( CSpinButtonCtrl *) GetDlgItem(IDC_SPIN)
ASSERT _ VALID (pSpin)
//Set spin button’s default position .
pSpin -> SetPos (10)
return TRUE
}
其二, 可以使用ClassWizard将控件和成员变量联系起来。在ClassWizard中简单地选择Member Variables标签,然后选择Add Variable …按钮。如果在对话资源编辑器中,按下Ctrl键并双击控件即可转到Add Member Variable对话。 -
2004-04-29
[转载] Visual C++ 编程问答(一) - [VC开发专辑]
1.如何获取应用程序的实例句柄?
应用程序的实例句柄保存在CWinApp m_hInstance 中,可以这么调用AfxGetInstancdHandle获得句柄。
Example: HANDLE hInstance=AfxGetInstanceHandle()
2.如何通过代码获得应用程序主窗口的指针?
主窗口的指针保存在CWinThread::m_pMainWnd中,调用AfxGetMainWnd实现。
AfxGetMainWnd() ->ShowWindow(SW_SHOWMAXMIZED)
//使程序最大化
3.如何在程序中获得其他程序的图标?
两种方法:
(1) SDK函数 SHGetFileInfo 或使用 ExtractIcon获得图标资源的 handle;
(2) SDK函数 SHGetFileInfo 获得有关文件的很多信息,如大小图标,属性, 类型等。
Example(1):
在程序窗口左上角显示 NotePad图标.
void CSampleView:
OnDraw(CDC * pDC)
{
if( :: SHGetFileInfo(_T("c:\\pwin95\\notepad.exe"),0,
&stFileInfo,sizeof(stFileInfo),SHGFI_ICON))
{
pDC ->DrawIcon(10,10,stFileInfo.hIcon)
}
}
Example(2):同样功能,Use ExtractIcon Function
void CSampleView:: OnDraw(CDC *pDC)
{
HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T
("NotePad.exe"),0)
if (hIcon &&hIcon!=(HICON)-1)
pDC->DrawIcon(10,10,hIcon)
}
说明: 获得notepad.exe的路径正规上来说用GetWindowsDirectory函数得到, 如果是调用 win95下的画笔,应该用访问注册表的方法获得其路径,要作成一个比较考究的程序,考虑应该全面点.
4.如何编程结束应用程序?
这是个很简单又是编程中经常要遇到的问题。
向窗口发送 WM_CLOSE消息,调用 CWnd::OnClose成员函数.允许对用户提示是否保存修改过的数据.
Example: AfxGetMainWindow()->SendMessage(WM_CLOSE)
还可以创建一个自定义的函数 Terminate Window
void Terminate Window(LPCSTR pCaption)
{
CWnd *pWnd=Cwnd::FindWindow(NULL,pCaption)
if (pWnd)
pWnd ->SendMessage(WM_CLOSE)
}
说明: FindWindow函数不是提倡的做法,因为它无法处理标题栏自动改变,比如我们要检测 Notepad是不是已运行而事先不知道Notepad的标题栏,这时FindWindow就无能为力了,可以通过枚举 windows任务列表的办法来实现。在机械出版社"Windows 95 API开发人员指南"一书有比较详细的介绍,这里就不再多说乐。
5.怎样加载其他的应用程序?
三个SDK函数 winexec, shellexecute,createprocess可以使用。
WinExec最简单,两个参数,前一个指定路径,后一个指定显示方式.后一个参数值得说一下,比如泥用 SW_SHOWMAXMIZED方式去加载一个无最大化按钮的程序,就是Neterm,calc等等,就不会出现正常的窗体,但是已经被加到任务列表里了。
ShellExecute较 WinExex灵活一点,可以指定工作目录,下面的Example就是直接打开 c:\temp\1.txt,而不用加载与 txt文件关联的应用程序,很多安装程序完成后都会打开一个窗口,来显示Readme or Faq,我猜就是这么作的啦.
ShellExecute(NULL,NULL,_T("1.txt"),NULL,_T("c:\\temp"),SW_SHOWMAXMIZED)
CreateProcess最复杂,一共有十个参数,不过大部分都可以用NULL代替,它可以指定进程的安全属性,继承信息,类的优先级等等.来看个很简单的Example:
STARTUPINFO stinfo
//启动窗口的信息
PROCESSINFO procinfo //进程的信息
CreateProcess(NULL,_T("notepad.exe"),NULL,NULL.FALSE,
NORMAL_PRIORITY_
CLASS,NULL,NULL, &stinfo,&procinfo)
6.确定应用程序的路径
Use GetModuleFileName 获得应用程序的路径,然后去掉可执行文件名。
Example:
TCHAR
exeFullPath[MAX_PATH] // MAX_PATH在API中定义了吧,好象是
128
GetModuleFileName(NULL,exeFullPath,MAX_PATH)
7.获得各种目录信息
Windows目录: Use "GetWindowsDirectory"
Windows下的system目录: Use "GetSystemDirectory"
temp目录: Use "GetTempPath"
当前目录: Use "GetCurrentDirectory"
请注意前两个函数的第一个参数为目录变量名,后一个为缓冲区后两个相反。
8.如何自定义消息
(1) 手工定义消息,可以这么写
#define WM_MY_MESSAGE(WM_USER+100),
MS 推荐的至少是 WM_USER+100
(2)写消息处理函数,用
WPARAM,LPARAM返回LRESULT.
LRESULT CMainFrame::OnMyMessage(WPARAM wparam,LPARAM lParam)
{
temp目录: Use "GetTempPath"
//加入你的处理函数 irectory"
}
9.如何改变窗口的图标?
向窗口发送 WM_SECTION消息。
Example:
HICON hIcon=AfxGetApp() ->LoadIcon(IDI_ICON)
ASSERT(hIcon)
AfxGetMainWnd() ->SendMessage(WM_SECTION,TRUE,(LPARAM)hIcon)
10.如何改变窗口的缺省风格?
重载 CWnd:: PreCreateWindow 并修改CREATESTRUCT结构来指定窗口风格和其他创建信息.
Example: Delete "Max" Button and Set Original
Window’s Position and Size
BOOL CMainFrame:: PreCreateWindow
(CREATESTRUCT &cs)
{
cs.style &=~WS_MAXINIZEMOX
cs.x=cs.y=0
cs.cx=GetSystemMet -
2004-04-29
[原创] Get Local Port - [C/C++开发专辑]
// GetLocalPort.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
system("netstat -a >localPort.txt");
printf("Hello World!\n");
return 0;
} -
2004-04-29
[原创] Block_Manager V1.00 - [ObjectArx开发专辑]
http://upload.yourblog.org/20044/junglesong.20040429163431.rar
图块管理器1.00版本 -
2004-04-29
[原创] DynamicCreateButton - [VC开发专辑]
// DynamicCreateButtonDlg.h : header file
//
#if !defined(AFX_DYNAMICCREATEBUTTONDLG_H__BF0B7061_9F30_484D_92DA_97435414E97A__INCLUDED_)
#define AFX_DYNAMICCREATEBUTTONDLG_H__BF0B7061_9F30_484D_92DA_97435414E97A__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "TextBtn.h"
/////////////////////////////////////////////////////////////////////////////
// CDynamicCreateButtonDlg dialog
class CDynamicCreateButtonDlg : public CDialog
{
// Construction
public:
CDynamicCreateButtonDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CDynamicCreateButtonDlg)
enum { IDD = IDD_DYNAMICCREATEBUTTON_DIALOG };
int m_nCntofDBtn;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDynamicCreateButtonDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CDynamicCreateButtonDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void On_Btn_Create();
afx_msg void On_Btn_Erase();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
CTextBtn* m_btn;
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_DYNAMICCREATEBUTTONDLG_H__BF0B7061_9F30_484D_92DA_97435414E97A__INCLUDED_)
// DynamicCreateButtonDlg.cpp : implementation file
//
#include "stdafx.h"
#include "DynamicCreateButton.h"
#include "DynamicCreateButtonDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define ID_MYBTN 1978
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDynamicCreateButtonDlg dialog
CDynamicCreateButtonDlg::CDynamicCreateButtonDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDynamicCreateButtonDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CDynamicCreateButtonDlg)
m_nCntofDBtn = 0;
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CDynamicCreateButtonDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDynamicCreateButtonDlg)
DDX_Text(pDX, IDC_EDIT1, m_nCntofDBtn);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CDynamicCreateButtonDlg, CDialog)
//{{AFX_MSG_MAP(CDynamicCreateButtonDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, On_Btn_Create)
ON_BN_CLICKED(IDC_BUTTON2, On_Btn_Erase)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDynamicCreateButtonDlg message handlers
BOOL CDynamicCreateButtonDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ -
2004-04-29
[原创] SHFolder - [VC开发专辑]
#ifndef GLOBAL_H
#define GLOBAL_H
#ifndef BIF_NEWDIALOGSTYLE
#define BIF_NEWDIALOGSTYLE 0x0040
#endif
#ifndef BIF_USENEWUI
#define BIF_USENEWUI 0x0050
#endif
// 初始化文件夹设定用的回调函数
int CALLBACK _SHBrowseForFolderCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
if(uMsg == BFFM_INITIALIZED)
::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
return 0;
}
bool g_fSelectFolderDlg(CString* lpstrFolder,CString strIniFolder,bool bAvailNewFolder)
{
bool ret;
char lpszPath[MAX_PATH];
LPMALLOC lpMalloc;
BROWSEINFO sInfo;
LPITEMIDLIST lpidlRoot;
LPITEMIDLIST lpidlBrowse;
if(lpstrFolder == NULL)
return false;
if(::SHGetMalloc(&lpMalloc) != NOERROR)
return false;
ret = false;
if(strIniFolder != "")
{
if(strIniFolder.Right(1) == "\\")
strIniFolder = strIniFolder.Left(strIniFolder.GetLength() - 1); //删除末尾的"\\"
}
::SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &lpidlRoot); //取得选定的文件夹名
::ZeroMemory(&sInfo, sizeof(BROWSEINFO));
sInfo.pidlRoot = lpidlRoot;
sInfo.pszDisplayName = lpszPath;
sInfo.lpszTitle = _T("僼僅儖僟偺慖戰");
sInfo.ulFlags = BIF_RETURNONLYFSDIRS;
if(bAvailNewFolder == true)
sInfo.ulFlags |= BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_USENEWUI;
sInfo.lpfn = _SHBrowseForFolderCallbackProc;
sInfo.lParam = (LPARAM)strIniFolder.GetBuffer(0);
lpidlBrowse = ::SHBrowseForFolder(&sInfo); //显示文件夹选择对话框
if(lpidlBrowse != NULL)
{
if(::SHGetPathFromIDList(lpidlBrowse,lpszPath)) //取得文件夹名
{
*lpstrFolder = "";
*lpstrFolder = lpszPath;
if(*lpstrFolder != "")
{
if(lpstrFolder->Right(1) != "\\")
*lpstrFolder += "\\"; //在末尾时附加"\\"
}
}
ret = true;
}
if(lpidlBrowse != NULL)
::CoTaskMemFree(lpidlBrowse);
if(lpidlRoot != NULL)
::CoTaskMemFree(lpidlRoot);
lpMalloc->Release();
return ret;
}
#endif
void CSHFolderDlg::OnClick_SHBrowseForFolder()
{
if(g_fSelectFolderDlg(&m_strFolder,m_strFolder,false))
{
UpdateData(false);
}
} -
2004-04-29
[原创] CAutoCompleteCombo - [VC开发专辑]
#if !defined(AFX_AUTOCOMPLETECOMBO_H__5FC0B924_A76A_4C95_AC30_EFC12F83BCBE__INCLUDED_)
#define AFX_AUTOCOMPLETECOMBO_H__5FC0B924_A76A_4C95_AC30_EFC12F83BCBE__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// AutoCompleteCombo.h : header file
//
/////////////////////////////////////////////////////////////////////////////
// CAutoCompleteCombo window
class CAutoCompleteCombo : public CComboBox
{
// Construction
public:
CAutoCompleteCombo();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAutoCompleteCombo)
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CAutoCompleteCombo();
// Generated message map functions
protected:
//{{AFX_MSG(CAutoCompleteCombo)
afx_msg void OnEditupdate();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
BOOL m_bAutoComplete;
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_AUTOCOMPLETECOMBO_H__5FC0B924_A76A_4C95_AC30_EFC12F83BCBE__INCLUDED_)
// AutoCompleteCombo.cpp : implementation file
//
#include "stdafx.h"
#include "AutoCompleteCombo.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAutoCompleteCombo
CAutoCompleteCombo::CAutoCompleteCombo()
{
m_bAutoComplete=TRUE;
}
CAutoCompleteCombo::~CAutoCompleteCombo()
{
}
BEGIN_MESSAGE_MAP(CAutoCompleteCombo, CComboBox)
//{{AFX_MSG_MAP(CAutoCompleteCombo)
ON_CONTROL_REFLECT(CBN_EDITUPDATE, OnEditupdate)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CAutoCompleteCombo message handlers
BOOL CAutoCompleteCombo::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if(pMsg->message==WM_KEYDOWN)
{
m_bAutoComplete=TRUE;
int nVirtKey=(int)(pMsg->wParam);
if(nVirtKey==VK_DELETE || nVirtKey==VK_BACK)
{
m_bAutoComplete=FALSE;
}
}
return CComboBox::PreTranslateMessage(pMsg);
}
void CAutoCompleteCombo::OnEditupdate()
{
// TODO: Add your control notification handler code here
if(!m_bAutoComplete) return;
// Get current text concept in the combobox
CString strText;
GetWindowText(strText);
int nLength=strText.GetLength();
// Get selected text range
DWORD dwCurSel=GetEditSel();
WORD dStart=LOWORD(dwCurSel);
WORD dEnd=HIWORD(dwCurSel);
// Serach String
if(SelectString(-1,strText)==CB_ERR)
{
SetWindowText(strText);
if(dwCurSel!=CB_ERR)
{
SetEditSel(dStart,dEnd);
}
}
// Select Text
if(dEnd<nLength && dwCurSel!=CB_ERR)
{
SetEditSel(dStart,dEnd);
}
else
{
SetEditSel(nLength,-1);
}
}
/////////////////////////////////////////////////////////////////////////////
// CTestAutoCompleteComboDlg message handlers
BOOL CTestAutoCompleteComboDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application’s main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_combo.AddString("America");
m_combo.AddString -
2004-04-22
[转载] 殷人东渡中美洲?--许辉教授访谈录 - [上古历史研究文选]
殷人东渡中美洲?
--许辉教授访谈录
戴开元
1996年以来,中国大陆旅美学者、德州基督教大学助理教授许辉博士提出了
一个新颖观点,震动了中美学术界。许教授认为:大约三千多年前,中国的殷商
王朝被周武王打败以後,殷商的一些遗民曾东渡太平洋,抵达中美洲,对当地的
奥尔梅克(Olmec)文明的发展产生了重大影响。
许辉的论点引起中外媒体的轰动。美国的《美国新闻与世界报导》(US News
& World Report)、《大西洋月刊》(Atlantic Monthly)、《发现》杂志(
Discovery)、《全国地理》杂志网路(National Geographic.com)、美国广播公司
新闻网路(ABCNews.com)、《芝加哥太阳时报》(Chicago Sun-Times),日本的
《读卖新闻》,台湾的《光华》杂志,大陆的新华社,先後报导了许辉的发现。
大陆学者对许辉的观点大多持欣赏、鼓励和支持的态度。但研究美洲文化的
美国考古界人士,却多数表示怀疑、否定的看法,只有很少学者同情和支持许辉
的观点。
西方的主流学术界认为,一万多年前,生活在亚洲东北的人类,越过当时已
被冰原联成一片的白令海峡,逐渐移居到南北美洲大陆各地,形成了後来美洲的
原住民----印第安人。自此以後,美洲大陆就与亚洲或其他大陆几乎完全隔绝,
印第安人独自发展出一系列文化和社会,包括三千多年前中美洲兴起的奥尔梅克
文明,後来的玛雅文明和南美的安第斯文明。在十五世纪哥伦布发现美洲新大陆
以前,美洲原住民和地球上其他地区几乎没有发生任何接触,更谈不上相互交流
或影响。
奥尔梅克文明是目前已知最早的美洲文明,是所有後来的美洲文明之母。大
约在公元前1200至1000多年前,在现今中美洲的墨西哥南部和危地马拉一带,突
然出现一种新的文明。这种文明具有阶级分化、城乡区别、农业、商业、宗教、
巫术、艺术等人类文明社会的特征,考古学家把它命名为「奥尔梅克文明」。
然而,经过数十年的考古发掘和研究,对於奥尔梅克文明的起源问题,西方
学术界一直争论不休,莫衷一是,没有得出一个公认的结论。大体说来,西方主
流学术界人士,大多认为奥尔梅克文明是印第安人独自发展出来的,他们被称为
「独创派」(independent inventionists)。只有极少数学者主张奥尔梅克文明
来源於欧洲、非洲或亚洲,他们被称为「传播派」(diffusionists)。但传播派
的观点,被统治主流学术界的独创派,统统视为毫无根据的「异端邪说」或「伪
科学」。
有些传播派学者认为,欧洲地中海一带的居民,在数千年前曾到达美洲。有
些学者提出,古代非洲人曾越过大西洋达到美洲。还有些学者,包括英国的李约
瑟(Joseph Needham)、美国的艾克荷姆(G.K. Ekholm)和麦葛斯(B.J.Meggers)、
法国的圭尼斯(Joseph de Guignes),和中国的郭沫若、董作宾、凌纯声、贾兰
坡等,则提出古代美洲文明与古代中国文明之间可能存在渊源关系。1980年代初
,中国交通科学研究院的房仲甫根据在南美洲海底发现的古代石锚和一些中国古
籍的记载,提出古代中国人曾多次航海远渡太平洋,到达过美洲大陆。
许辉教授的观点之所以引起广泛注意,在於他不仅提出了论点,而且,还提
出具有一定说服力的证据。其中最重要的证据,就是他在奥尔梅克文明及其邻区
文化中搜集到三百多个类似於中国古文字的刻画符号。这些符号经大陆一些知名
考古学家和古文字学家鉴定,认为和中国三千年前的甲骨文有惊人的相似之处。
他们还表示,如果在大陆考古遗址发现这些符号,一定会认为是中国商周时期的
甲骨文。
大陆学者认为,这些与甲骨文类似的奥尔梅克符号,不仅有农业方面的,如
禾、田、木、天、日、雨、水、树苗等文字,还有祭祀、巫术方面的文字,与中
国古代的天干地支、数字和卦画等符号非常类似。
北京中国博物馆馆长、考古学家俞伟超在给许辉的信中写道:「这个文化(指
奥尔梅克文化----笔者)和中国的商文化,在人种上是一致的;许多艺术品的图案
,又有那麽多的相似之处;你找出的那麽多的符号,同甲骨文竟然如此接近;而
且在年代上,两者还可以衔接起来。」
南京大学历史系教授范毓周高度评价许辉的发现。他在给许辉的信中写道:
「我认为,你关於奥尔梅克文明可能来自商的看法,可能成立。我同意你的看法
,奥尔梅克文化的石、玉和陶器上的刻画符号,可以被看成是甲骨文。考虑到中
国古籍记载,商被打败以後,商纣王的儿子武庚禄父率领残余族人向东北方向逃
走,我们可以确定,这些奥尔梅克刻符可能来自商的假说。」
范毓周还认为,文字是文明的重要载体,两种文化之间不可能独自创造出相
同的文字。奥尔梅克符号与甲骨文的相似之处不是个别、孤立的,这充分说明商
周文化与中美洲文化之间存在渊源关系。
另外,许辉还发现,奥尔梅克文明的陶器、玉器、石雕和宗教仪式,都与中
国的商周文明极为相似。中美洲出土的陶器造型、纹饰符号、拉坯技术、镂空方
法,均与中国古代陶器和制陶工艺相同。奥尔梅克文明的玉器崇拜、石器上的多
种纹饰,玉器上的神徽、图腾和符号,与中国先秦时期的内容很相似 -
2004-04-22
[转载] 再造一个中国 - [梦回汉唐]
朔天运河简介及其意义浅析
一、朔天运河简介
众所周知,黄河季节性的断流已经多年了,而且一年比一年严重,整个华北
已经没有一条常流河。不仅大半个北部中国缺水,水资源一向丰富的南方一些城
市和地区,水的短缺也已成为束缚社会和经济发展的瓶颈。中国西北地区穷,就
穷在水上。没有水就没有生命。没有水,怎么搞现代化?水的短缺问题,不仅在
中国,而且在世界的今天和21世纪严重困扰经济发展、甚至是已经造成或将要造
成社会动荡和政治冲突的一大根源。由于缺水导致土地和山川的荒漠化,进而使
一种文明衰亡,在世界史上屡见不鲜。
1994年我国有荒漠16亿亩,现在扩展到21亿亩,加上半沙漠化退化草场,达
68亿亩,几乎占全国面积的47%,而且每年还以200万亩的速度扩展。这样下去,
我们怎么向子孙后代交代?现在是沙漠南侵,黄河断流,生态破坏之严重,已经
构成了对中华文明的威胁,我们还要等到什么时候才治理呢?解决水问题的思想
和方案,不能局限于大中城市或局部地区。一切有利于节水的科学技术都应大力
推广。但是节水是以有水为前提的,沙漠或没有水的地区何节之有?难道任凭荒
漠存在和扩展下去吗?我国的缺水问题不是修修补补、小打小闹能够解决的。治
水的思路和方案不仅仅在通盘考虑到全国各省用水的现状和将来,而且要同能够
改造大西北荒漠问题结合起来。
南水北调是老问题了,50年代毛主席就提出要尽快妥善解决。自那以后提出
过东、中、西三个南水北调方案。
东线方案是利用京杭大运河河道,逐级提高调长江下游白水,解决京津地区
用水。可调水195亿立米,需投资1300亿元,工期10年。但是近几年运河污染严重
,所调之水必须经过严格处理,这样势必要增加投资和水的运营管理成本。
中线方案是加高汉江丹江口水库大坝,调水200亿立米,挖一条1200多公里的
输水干渠到北京,经过360多条大小河流,总投资要1700亿元以上,工期15年,可
是汉江的水正在逐年减少,几乎无水可调。要保证输水量,必须向将来的长江三
峡水库调水。由于输水干渠路经湖北、河南、河北人口稿密地区,无论是移民工
作,还是水的管理和运营的成本,都是很大的;水到京津地区实际只剩八亿立米
。而且,中线沿太行山麓修渠,太行山东坡是暴雨区,山洪频繁,极不安全,遗
患很大。
西线方案是在长江上游通天河、雅砻江、大渡河筑河筑高坝,调水入黄河,
能调水195亿立米;开凿隧洞175公里,其中开凿的一条隧洞长90公里,主体工程
即需1500亿元,工期15年。
这一个方案都是在长江上作文章,问题是到2020年,长江也是缺水户。
我们的思路是立足于寻找我国最丰富的水源,设计出一个既切实可行,又多
、快、好、省的办法,总之是投入少,产出多,见效快。我们反复比较过三个方
案,经查对五万分之一的地图,又到实地考察,推出从雅鲁藏布江调水入黄河的
“大西线”的构想。“大西线”的概念1994年初首先出现在蒋本兴(水利部副部
长)、郭开、于招英合写的《朔天运河“大西线”南水北调》一文中,它区别于
原来基于长江水的西线方案。
按国际标准,我国水资源不算缺乏。据联合国规定,年人均水资源不足1700
立米者为水资源匮乏。我国年人均水资源2816立米。之所以许多地方水紧张,一
是水资源分布不均;二是大量的水流出国境。我们推出“大西线”调水方案,充
分考虑和兼顾这样四个方面:是否有丰沛的水源水量,是否有理想的走水路线,
是否有丰厚的经济、社会、生态效益,是否符合国际惯例。
我国西藏地区处于印度洋气流和太平洋气流的交汇处,降水非常丰富;印度
洋西南季风带来的大量水汽撞击高山,降水多而形成巨量洋川固体水。整个西藏
高原就是一座天然的大水库。雅鲁藏布江、怒江、澜沧江、察隅河、独龙江等年
出国境水量5800多亿立米,不仅白白流走,每到汛期山洪爆发,经常给下游东南
亚一些国家造成水灾,所以拟从中取水2100亿立米。长江上游三条支流金沙江、
雅砻江、大渡河年总水量2074亿立米,可调水250亿立米,保证年引水入黄河200
6亿立米(考虑到水的流失、渗透、蒸发等因素,按保证率85%计算,实际调水约
为2006亿立米)。
“大西线”调水路线的整个地形特点是多水的西南地势高,缺水的西北、华
北的地势逐级降低,形成从西南向东北倾斜的有利于区域间调水的大环境。“大
西线”调水的基本走线是:在雅鲁藏布江朔玛滩筑坝,水位抬高至海拔3588米,
引水到波密,过分水岭(3566米)进八美河入怒江。在夏里筑堵江,提高水位达
3456米,回水过嘉玉桥,在马利打隧洞过分水岭入雅砻江。在甘孜南筑拦雅砻江
回水向东,过分水岭入鲜水河。在炉霍筑坝,使水位达3396米,朔源溢流,过分
水岭入大流落河上游的多柯河和麻尔柯河。引水到阿坝查里寺,过分水岭进贾曲
,入黄河(3366米)。
“大西线”调水工程也叫雅黄工程,从雅鲁藏布江到黄河,直线距离760公里
,实际流程1800公里。这条线路,低顺直,全部自流。实行爆破,搞人 -
2004-04-22
[原创] 本人设计的第一人称三维角色类,请高手指点。 - [OpenGL开发专辑]
一般情况下实现第一人称视角游戏有两种方法,一是移动场景,二是移动眼坐标.移动场景方法比较简单,使用glTranslatef与glRotatef配合即可,但一般只在简单场景和单角色的情况下使用,而且角色的各种计算(如实时坐标、碰撞)不好实现,所以不推荐使用;移动眼坐标的方法就非常灵活,它对场景和角色的状态未做任何操作,一般只要设置成跟随主角色移动旋转即可实现第一人称视角视觉效果,在OpenGL中主要用gluLookAt函数来实现,其函数原形如下。
用途:定义视图变换
包含文件:<glu.h>
描述:根据眼睛的位置,场景中心的位置和从观察者的角度往上指的矢量,定义一个视图变换.
句法:void gluLookAt(Gldouble eyex, Gldouble eyey, Gldouble eyez, Gldouble aimx, Gldouble aimy, Gldouble aimz, Gldouble upx, Gldouble upy, Gldouble upz);
参数: eyex, eyey, eyez:眼睛的三维坐标
aimx, aimy, aimz:被观察的场景中心的三维坐标
upx, upy, upz:指定向上矢量的三维坐标.
除了空战游戏,要实现角色在三维场景中漫游,只需要改变眼睛的三维坐标(eyex, eyey, eyez)和被观察的场景中心的三维坐标(aimx, aimy, aimz)就可以了,指定向上矢量的三维坐标(upx, upy, upz)保持不变,其中又以眼睛的三维坐标为主, 被观察的场景中心的三维坐标为次,本人制作了一个类,粗略实现了漫游功能.首先将三维坐标数据统一成一个结构,如下:
//三维点结构。
struct Point{
float x;
float y;
float z;
Point(){
x=y=z=0.0f;
}
Point(float InputX,float InputY,float InputZ){
x=InputX;y=InputY;z=InputZ;
}
~Point(){
}
};
//角色类如下
class Dat_Roll {
public:
Dat_Roll();
~Dat_Roll();
void TurnLeft(const float InputAngle){//角色左旋
m_fViewAngle-=InputAngle*PI/180;
RefreshAimPos();
};
void TurnRight(const float InputAngle){//角色右旋
m_fViewAngle+=InputAngle*PI/180;
RefreshAimPos();
};
void TurnUp(const float InputAngle){//向上看
if(m_fViewAngleVertical>PI/2) //防止翻转 m_fViewAngleVertical=PI/2;
if(m_fViewAngleVertical<-PI/2) m_fViewAngleVertical=-PI/2;
m_fViewAngleVertical-=InputAngle*PI/180;
RefreshAimPos();
};
void TurnDown(const float InputAngle){//向下看
if(m_fViewAngleVertical>PI/2) //防止翻转 m_fViewAngleVertical=PI/2;
if(m_fViewAngleVertical<-PI/2) m_fViewAngleVertical=-PI/2;
m_fViewAngleVertical+=InputAngle*PI/180;
RefreshAimPos();
};
void MoveForward(){//向前移动
m_NextEyePos.x+=cos(m_fViewAngle)*m_fMoveSpeed;
m_NextEyePos.z+=sin(m_fViewAngle)*m_fMoveSpeed;
RefreshAimPos();
};
void MoveBackward(){//向后移动
m_NextEyePos.x-=cos(m_fViewAngle)*m_fMoveSpeed;
m_NextEyePos.z-=sin(m_fViewAngle)*m_fMoveSpeed;
RefreshAimPos();
};
void MoveLeft(){ //向左移动
m_NextEyePos.x+=sin(m_fViewAngle)*m_fMoveSpeed;
m_NextEyePos.z+=cos(m_fViewAngle)*m_fMoveSpeed;
RefreshAimPos();
};
void MoveRight(){ //向右移动
m_NextEyePos.x-=sin(m_fViewAngle)*m_fMoveSpeed;
m_NextEyePos.z-=cos(m_fViewAngle)*m_fMoveSpeed;
RefreshAimPos();
};
void RefreshAimPos(){//刷新目标点,重新计算目标点的三维坐标
m_AimPos.x=m_EyePos.x+m_fEyeAimDis*cos(m_fViewAngle)*cos(m_fViewAngleVertical);
m_AimPos.y=m_EyePos.y+m_fEyeAimDis*sin(m_fViewAngleVertical);
m_AimPos.z=m_EyePos.z+m_fEyeAimDis*sin(m_fViewAngle)*cos(m_fViewAngleVertical);
};
Point GetEyePos(){
return m_EyePos;
};
Point GetAimPos(){
return m_AimPos;
};
Point GetHoverPos(){
return m_HoverPos;
};
private:
Point m_EyePos;//眼睛的三维坐标
Point m_NextEyePos;//眼睛下一位置的三维坐标,用于碰撞检测
Point m_AimPos;//被观察的场景中心的三维坐标
Point m_HoverPos;//指定向上矢量的三维坐标.
float m_fViewAngle;//水平角度
float m_fViewAngleVertical;//垂直角度
float m_fMoveSpeed;//移动速度
float m_fEyeAimDis;//眼睛到中心的距离
};
Dat_Roll::Dat_Roll(){
m_EyePos=Point(5.0f,4.0f,5.0f);
m_AimPos=Point(0.0f,0.0f,0.0f);
m_HoverPos=Point(0.0f,1.0f,0.0f);
m_fViewAngle=-90.0f*PI/180.0f;
m_fViewAngleVertical=0.0f;
m_fMoveSpeed=10.0f;
m_fEyeAimDis=10;
m_NextEyePos=Point(0,0,0);
}
Dat_Roll::~Dat_Roll(){
}
此文属本人原创,转载请著名作者和出处. -
2004-04-22
[原创] 用点积和叉乘解决空间中固定朝向物体的摆放问题 - [OpenGL开发专辑]
大家都知道可以用下面的方法将一个物体摆放在opengl三维场景中的某个位置:
glPushMatrix();
glScalef(fZoomValue); //缩放
glTranslatef(xTrans,yTrans,zTrans); //平移
glRotatef(xRot,1,0,0 ); //绕x轴旋转
glRotatef(yRot,0,1,0 ); //绕y轴旋转
glRotatef(zRot,0,0,1 ); //绕z轴旋转
DrawSomeObj(**); //画物体
glPopMatrix();
如果是简单场景,即我们知道其绕三轴的夹角,那此方法是可行的,但是,如果要求在点(1,2,3)和点(7,8,9)之间绘制一个柱面,其轴线就是两点的连线,请问上面的办法如何处理。
有人说进行三角函数,计算出夹角,在结果上可以,但函数复杂,速度慢,还有一个变换一次后角度改变的问题,很容易就出错。建议大家不要用此方法。我推荐用点积和叉乘来一次性解决问题。
首先看glRotatef的定义:
glRotatef(Angle,vx,vy,vz);
Angle是一个标量,是变换前后的角度差;
vx,vy,vz组成一个矢量,表示旋转的轴;
只要可以计算出他们的值,就可以一次性将物体朝向旋转到位,而不需要进行开始时的三次旋转。
其次我们来看看点积和叉乘的数学定义:
点积: (x1 , y1 , z1 ) .( x2 , y2 , z2 ) = x1x2 + y1y2 + z1z2
叉乘: ( x1 , y1 , z1 ) X ( x2 , y2 , z2 ) =( y1z2 - z1y2 , z1x2 - x1z2 , x1y2 - y1x2 )
点积可以来计算两矢量的夹角,公式如下:
cos (V ^ W) =V.W / | V | | W |
叉乘可以计算两矢量的垂直矢量,叉乘后的新矢量就是垂直于前两矢量的矢量.
这样,Angle,vx,vy,vz就都可以计算出来了,下面请看一个例子:
我要在x1,y1,z1--开始点,x2,y2,z2--结束点之间画一个柱面,画住的原始函数如下:
void DrawPrism(float SideLen,int SideNum,float Height){
if(SideNum<3) return;
if(SideLen<0.000001 || Height<0.000001) return;
int i=0;
glPushMatrix();
for(i=0;i<SideNum;i++)
{
glBegin(GL_TRIANGLES);
glVertex3f(0,0,Height);
glVertex3f(SideLen*cos(i*2*PI/SideNum),SideLen*sin(i*2*PI/SideNum),Height);
glVertex3f(SideLen*cos((i+1)*2*PI/SideNum),SideLen*sin((i+1)*2*PI/SideNum),Height);
glEnd();
}
for(i=0;i<SideNum;i++)
{
glBegin(GL_TRIANGLES);
glVertex3f(0,0,0);
glVertex3f(SideLen*cos((i+1)*2*PI/SideNum),SideLen*sin((i+1)*2*PI/SideNum),0);
glVertex3f(SideLen*cos(i*2*PI/SideNum),SideLen*sin(i*2*PI/SideNum),0);
glEnd();
}
for(i=0;i<SideNum;i++)
{
glBegin(GL_QUADS);
glVertex3f(SideLen*cos((i+1)*2*PI/SideNum),SideLen*sin((i+1)*2*PI/SideNum),0);
glVertex3f(SideLen*cos((i+1)*2*PI/SideNum),SideLen*sin((i+1)*2*PI/SideNum),Height);
glVertex3f(SideLen*cos(i*2*PI/SideNum),SideLen*sin(i*2*PI/SideNum),Height);
glVertex3f(SideLen*cos(i*2*PI/SideNum),SideLen*sin(i*2*PI/SideNum),0);
glEnd();
}
glPopMatrix();
}
其默认是以(0,0,0)为起点,轴向为z正向,(0,0,Height)为终点,要把它"搬"到Point1(x1,y1,z1),Point2(x2,y2,z2)之间,用点积和叉乘就容易办到了,代码如下:
double a,b,c,d,a1,b1,c1,a2,b2,c2,d1,d2;//定义矢量
double alfa;//定义旋转角度
//这三步求得Point1和Point2的矢量1
a1 = x2 - x1;//
b1 = y2 - y1;//
c1 = z2 - z1;
//这是旋转前轴向代表的矢量2
a2 = 0;
b2 = 0;
c2 = 1;
d1 = sqrt(a1*a1 + b1*b1 + c1*c1);//求矢量1的长度
d2 = sqrt(a2*a2 + b2*b2 + c2*c2);//求矢量2的长度,其实就是1,这里为了说明没有简化
d = d1;//获得矢量1的长度
alfa = (GLdouble)( acos( abs(a1*a2 + b1*b2 + c1*c2)/d1/d2 ) * 180/PI );//由点积计算旋转角度
//由叉乘计算旋转轴
a = b1*c2 - b2*c1;
b = a2*c1 - a1*c2;
c = a1*b2 - a2*b1;
alfa = -alfa;//修正角度(也可以修改下面的glrotatef函数)
if(c1<0)
{
alfa = 180 - alfa;
}
glPushMatrix();
//glScalef(fZoomValue); //这里不用考虑缩放,所以不用
glTranslatef(x1,y1,z1); //平移
glRotatef(alfa,a,b,c ); //旋转
DrawPrism(SideLen,4,d); //画柱,函数在上面
glPopMatrix();
自此函数完成,一般把上述过程优化后写入一个函数里,以方便使用.
如果是已知3轴旋转角,如何简化成一个glRotatef函数呢,用同样的办法就可以办到,复杂的一步是要通过这3轴旋转角计算出新矢量,这个计算过程在许多图形学的书中都有,公式是:
绕X轴旋转角q的矩阵
| 1 0 0 0 |
| 0 cos(q) sin(q) 0 |
| 0 -sin(q) cos(q) 0 |
| 0 0 0 1 |
绕Y轴旋转角q的矩阵:
| cos(q) 0 -sin(q) 0 |
| 0 1 0 0 |
| sin(q) 0 cos(q) 0 |
| 0 0 0 1 |
绕Z轴旋转角q的矩阵:
| cos(q) sin(q) 0 0 |
|-sin(q) cos(q) 0 0 |
| 0 0 1 0 |
| 0 0 0 1 |
计算即可.
此文属本人原创,转载请著名作者和出处. -
2004-04-09
[转贴] 改变人生的21种好习惯 - [工作-生活]
1.当一个人生活枯燥的时候,他忘了用心体会是一种习惯。
2.当一个人觉得人生乏味的时候,他忘了培养幽默是一种习惯。
3.当一个人体力日差的时候,他忘了运动建身是一种习惯。
4.当一个人工作疲惫的时候,他忘了认真休息是一种习惯。
5.当一个人孤傲狂放的时候,他忘了感恩惜福是一种习惯。
6.当一个人志得意满的时候,他忘了谦冲为怀是一种习惯。
7.当一个人钱不够用的时候,他忘了投资理财是一种习惯。
8.当一个人觉得工作低迷的时候,他忘了激励自己是一种习惯。
9.当一个人怀疑自己的时候,他忘了建立自信是一种习惯。
10.当一个人忽略家人的时候,他忘了爱与关怀是一种习惯。
11.当一个人浑噩度日的时候,他忘了阅读好书是一种习惯。
12.当一个人忙于工作的时候,他忘了安排休闲是一种习惯。
13.当一个人目中无人的时候,他忘了不断学习是一种习惯。
14.当一个人服务不佳的时候,他忘了让顾客满意是一种习惯。
15.当一个人慌张失措的时候,他忘了万全准备是一种习惯。
16.当一个人推诿责任的时候,他忘了勇于承担是一种习惯。
17.当一个人肠枯思竭的时候,他忘了转型思考是一种习惯。
18.当一个人沮丧失意的时候,他忘了检讨改进是一种习惯。
19.当一个人畏惧调职的时候,他忘了提升自己是一种习惯。
20.当一个人沟通障碍的时候,他忘了真诚倾听是一种习惯。
21.当一个人业绩消退的时候,他忘了积极行动是一种习惯。
本文出处:http://www.sonicbbs.com/topicdisplay.asp?BoardID=11&Page=1&TopicID=529621
-
2004-04-09
[转贴] 你会用sizeof吗?(vc篇) kernelhao(原作) - [C/C++开发专辑]
本文主要包括二个部分,第一部分重点介绍在VC中,怎么样采用sizeof来求结构的大小,以及容易出现的问题,并给出解决问题的方法,第二部分总结出VC中sizeof的主要用法。
1、 sizeof应用在结构上的情况
请看下面的结构:
struct MyStruct
{
double dda1;
char dda;
int type
};
对结构MyStruct采用sizeof会出现什么结果呢?sizeof(MyStruct)为多少呢?也许你会这样求:
sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13
但是当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。你知道为什么在VC中会得出这样一个结果吗?
其实,这是VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)。
类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char
偏移量必须为sizeof(char)即1的倍数
int
偏移量必须为sizeof(int)即4的倍数
float
偏移量必须为sizeof(float)即4的倍数
double
偏移量必须为sizeof(double)即8的倍数
Short
偏移量必须为sizeof(short)即2的倍数
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
下面用前面的例子来说明VC到底怎么样来存放结构的。
struct MyStruct
{
double dda1;
char dda;
int type
};
为上面的结构分配空间的时候,VC根据成员变量出现的顺序和对齐方式,先为第一个成员dda1分配空间,其起始地址跟结构的起始地址相同(刚好偏移量0刚好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;接下来为第二个成员dda分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把dda存放在偏移量为8的地方满足对齐方式,该成员变量占用sizeof(char)=1个字节;接下来为第三个成员type分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为9,不是sizeof(int)=4的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(MyStruct)=8+1+3+4=16,其中有3个字节是VC自动填充的,没有放任何有意义的东西。
下面再举个例子,交换一下上面的MyStruct的成员变量的位置,使它变成下面的情况:
struct MyStruct
{
char dda;
double dda1;
int type
};
这个结构占用的空间为多大呢?在VC6.0环境下,可以得到sizeof(MyStruc)为24。结合上面提到的分配空间的一些原则,分析下VC怎么样为上面的结构分配空间的。(简单说明)
struct MyStruct
{
char dda;//偏移量为0,满足对齐方式,dda占用1个字节;
double dda1;//下一个可用的地址的偏移量为1,不是sizeof(double)=8
//的倍数,需要补足7个字节才能使偏移量变为8(满足对齐
//方式),因此VC自动填充7个字节,dda1存放在偏移量为8
//的地址上,它占用8个字节。
int type;//下一个可用的地址的偏移量为16,是sizeof(int)=4的倍
//数,满足int的对齐方式,所以不需要VC自动填充,type存
//放在偏移量为16的地址上,它占用4个字节。
};//所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构
//的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof
//(double)=8)的倍数,所以需要填充4个字节,以满足结构的大小为
//sizeof(double)=8的倍数。
所以该结构总的大小为:sizeof(MyStruc)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC自动填充的,没有放任何有意义的东西。
VC对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。
VC中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n -
2004-04-09
[转载] c++辨析谈 - [C/C++开发专辑]
static 是c++中很常用的修饰符,它被用来控制变量的存储方式和可见性,下面我将从 static 修饰符的产生原因、作用谈起,全面分析static 修饰符的实质。
static 的两大作用:
一、控制存储方式:
static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间。
1、引出原因:函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,大家知道,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现?
最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此函数控制)。
2、 解决方案:因此c++ 中引入了static,用它来修饰变量,它能够指示编译器将此变量在程序的静态存储区分配空间保存,这样即实现了目的,又使得此变量的存取范围不变。
二、控制可见性与连接类型 :
static还有一个作用,它会把变量的可见范围限制在编译单元中,使它成为一个内部连接,这时,它的反义词为”extern”.
static作用分析总结:static总是使得变量或对象的存储形式变成静态存储,连接方式变成内部连接,对于局部变量(已经是内部连接了),它仅改变其存储方式;对于全局变量(已经是静态存储了),它仅改变其连接类型。
类中的static成员:
一、出现原因及作用:
1、需要在一个类的各个对象间交互,即需要一个数据对象为整个类而非某个对象服务。
2、同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。
类的static成员满足了上述的要求,因为它具有如下特征:有独立的存储区,属于整个类。
二、注意:
1、对于静态的数据成员,连接器会保证它拥有一个单一的外部定义。静态数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。
2、类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。
const 是c++中常用的类型修饰符,但我在工作中发现,许多人使用它仅仅是想当然尔,这样,有时也会用对,但在某些微妙的场合,可就没那么幸运了,究其实质原由,大多因为没有搞清本源。故在本篇中我将对const进行辨析。溯其本源,究其实质,希望能对大家理解const有所帮助,根据思维的承接关系,分为如下几个部分进行阐述。
c++中为什么会引入const
c++的提出者当初是基于什么样的目的引入(或者说保留)const关键字呢?,这是一个有趣又有益的话题,对理解const很有帮助。
1. 大家知道,c++有一个类型严格的编译系统,这使得c++程序的错误在编译阶段即可发现许多,从而使得出错率大为减少,因此,也成为了c++与c相比,有着突出优点的一个方面。
2. c中很常见的预处理指令 #define variablename variablevalue 可以很方便地进行值替代,这种值替代至少在三个方面优点突出:
一是避免了意义模糊的数字出现,使得程序语义流畅清晰,如下例:
#define user_num_max 107 这样就避免了直接使用107带来的困惑。
二是可以很方便地进行参数的调整与修改,如上例,当人数由107变为201时,进改动此处即可,
三是提高了程序的执行效率,由于使用了预编译器进行值替代,并不需要为这些常量分配存储空间,所以执行的效率较高。
鉴于以上的优点,这种预定义指令的使用在程序中随处可见。
3. 说到这里,大家可能会迷惑上述的1点、2点与const有什么关系呢?,好,请接着向下
看来:
预处理语句虽然有以上的许多优点,但它有个比较致命的缺点,即,预处理语句仅仅只是简单值替代,缺乏类型的检测机制。这样预处理语句就不能享受c++严格类型检查的好处,从而可能成为引发一系列错误的隐患。
4.好了,第一阶段结论出来了:
结论: const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。
现在它的形式变成了:
const datatype variablename = variablevalue ;
为什么const能很好地取代预定义语句?
const 到底有什么大神通,使它可以振臂一挥取代预定义语句呢?
1. 首先,以const 修饰的常量值,具有不可变性,这是它能取代预定义语句的基础。
2. 第二,很明显,它也同样可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。
3. 第三,c++的编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高,同时,这也是它取代预定义语句的重要基础。这里,我要提一下,为什么说这一点是也是它能取代预定义语句的基础,这是因为,编译器不会去读存储的内容,如果编译器为const分配了存储空间,它就不能够成为一个编译期间的常量了。
4. 最后,const定义也像一个 -
2004-04-09
[转贴] 学好VC++的十大良好习惯 - [VC开发专辑]
学好VC++的十大良好习惯
欢迎光临阿蒙的VC++专业技术网站: http://www.vchome.net/
每到年底各大媒体就争先恐后热火朝天地搞总结,什么十大人物,十大品牌,十大美女,十大帅哥等等五花八门乱七八糟的让人充满好奇充满怀疑,这事确实让人有点郁闷,就如同男足国家队的国产教练如沈墙扶们每一次踢球失败后都要说这么一句:我们回去后要好好总结,下次会打得更好! 这话听了几十年了,耳朵都生虫了,但还是无法看到中国猪球队有象人样的表现.因此,总结在某一程度上来说只不过是一种形式罢了,总结不代表就能改过原有的不足,也不代表就能进步了,甚至有点俗不可耐,尽管如此,阿蒙亦明知故俗,前人说过了入乡了就要随俗,因此你生活在这种环境里,你无法对这些无聊无趣的东东置之不理,除非你是天才,天才往往在非天才的人看来是很怪异的,处处与现实格格不入,可阿蒙不是天才,所以还得赶快总结,要不就离题,又被大家骂了,:)
(一)充分利用MSDN,因为我个人觉得它胜过任何一本编程参考书;
MSDN是 Microsoft 当前提供的有关编程信息的最全面的资源,它包含微软最新的技术数据库,加上易学易用的全文检索功能,让您迅速找到任何您需要的技术参考数据,让您随时拥有与全世界菁英同步的技术,掌握最丰富的程序开发资源。我经常收到很多朋友的EMAILS,他们所提的问题往往都非常的简单,MSDN完全可以解答这些问题,但他们好象不太喜欢用,这是让我郁闷的地方,是因为英文不好呢,还是没有学会充分利用各种资源来解决问题的方法呢?
(二)提高英文水平,养成多上英文网站多看英文资料多买老外原版英文书;
有关程序员与英文水平的讨论已太多太多, 我个人认为要成为程序员,高中的英语水平够了,甚至不懂英语的一些人,也同样可以成为较好的程序员,因为开发工具的发展将是越来越傻瓜,但如果你是仅仅满足于能运用某种工具开发某个软件模块,那是没话说了.真正热衷技术肯干钻研乐于接受挑战的程序员是不满足于现状的,他们总感觉有太多的未知,于是总在不停地学习,如今信息技术发展得太快,而大部分的技术最先出现的时候都是英文版本的,要几个月或者几年以后才有中文版本的书出来,因此要想跟上步伐,一定要努力提高自己的英文水平,这样才能同步跟上信息技术。你可能担心自己的英语水平不行,没关系,刚开始多查字典,"万事开头难",必须有持之以恒的精神,不久你就会发现计算机英语其实很容易的。何况很多
英文技术站点确实比国内做得好啊!比如http://www.codeguru.com, http://www.codeproject.com, http://www.programmersheaven.com 等等.
(三)加强自我管理,善于作自我总结,分析自已的优点及缺点。
中国境内百分之八十以上的领导人在百分之八十以上的场合的讲话中都有类似的观点,所以在这里我是不多说了,反正这一条用在什么行业什么地方都不会有错的,人生最大的敌人不是就是自已吗?管好自已认清自已,那还有什么搞不定的?
(四)养成良好的文档习惯
程序员大多都不喜欢写文档,我以前也是特讨厌,在我的思想里,所谓的文档就是一些废话,一句话硬是用十句话来代替的无聊透顶,就如同部分中文系男生的爱情表白,明明就是"我爱你"三个字,他硬是把月亮啊太阳啊大海啊高山啊石头啊天使啊乱七八糟的都拉上关系了,尽管听起来浪漫,但在我认为不实用,:), 甚至太肉麻了,一个男子汉干嘛这么罗里罗嗦的......良好的文档是正规研发流程中非常重要的环节,一个好的程序是先写好设计文档再进行编程的,在设计文档的指导下,才能写出安全的代码。如果你不写文档,一开始就写程序,这样你就不会按已设计好的路线走,而是想到哪写到哪。小功能还好说,要是大功能,就容易混乱甚至失控.那么如何写文档呢?其实我认为没有统一的标准,虽然国家及一些NB的人总结了很多的模板,但每个人的习惯不同,如果你不加以修改或创新,就套用某个标准,我相信写起来会很吃力及说不清的难受,因此我觉得只要能将你的设计思想及实现算法或步骤描述清楚就是好的文档,我强烈建议广大程序员朋友们在写文档时要善于用图表来说明你的思想,我们不是作家,也可能作文都经常性地不及格,写出五官端正的文章对我们来说可能不容易啊!好好地利用VISIO,ROSE或别的工具来表达你的思想吧!
(五)代码风格要规范,严谨,效率要高。
这个不用说了,所以一定要记住了!不过,这一点有时可能与人的性格有关,如果你是经常丢三落四经常胡子长长经常钮扣扣错经常吃个快餐要一个小时的人,那你在CODING的时候可千万要注意了,CODING是CODING,生活是生活,不要写出的程序也是那样就不好了!
(六)掌握好跟踪调试技巧.
跟踪调试程序是一件繁琐而又复杂的事情,所以掌握必要的调试策略及技巧却可以使这些工作变得轻松起来.强烈建议你去看一下老美Everett N.McKay及Mike Wooding写的书<<Debugging Windows Programs>>,你一不定受益匪浅.
(七)养成自我测试的习惯
测试工作应由测试工程师来做,但在你写完一个模块或一个软件时,还是要自已先测试一下,保证不要出现一些低级的错误,何况这些错误让测试工程师看到了,狂扁你一顿,你很没FACES的.
(八 -
2004-04-05
[转载] Solmyr和Zero的故事 —— 内存,最后一块 - [C/C++开发专辑]
Solmyr和Zero的故事 —— 内存,最后一块
Zero
--------------------------------------------------------------------------------
“Shit!又死机了。我已经在这平台上工作了一个月了。可死机的次数比我在这个月里被女孩甩的次数还多。为什么?还不是这该死的平台,为什么掌上系统的内存就和愿意和我说话的女孩一样少?”Solmyr抱怨道。
“兄弟,怎么了。”Zero问。
“Zero是我们这组的主程序员,他懂得很多,人长的也帅,很讨女孩子的欢心。甚至连美工组的Lili(长得比孙燕姿还好看)也暗恋他。”。Solmyr一边想,一边说,“老大,你看,又死机了。为什么我每次用动态内存超过10次,就crash了呢?”
“我知道你原来是Java程序员,也许到现在,你还念念不忘那GC (垃圾收集机制,我说还不如叫上帝也哭泣-God Cry),可你要知道,你现在是在用C++编程,重要的是效率。”
“效率,我……”忽然,Solmyr觉得嘴角边似乎有液体流过。那是Solmyr的口水。每次提到效率,Solmyr总要流口水,就像看到漂亮MM,Solmyr要喷鼻血一样。
“C++中,关于动态内存的是new and delete。”
“我知道,”Solmyr急于表现自己,想证明自己对C++并不是一无所知,“我正在读Scott Meyer的More Effective C++。在C++中,new operator是C++内建的行为。任何人(也许除了Bjarne Stroustrup)都无法改变。new operator先调用一个名为operator new的函数动态申请内存。标准模式就像这样:
void* operator new(size_t size);
然后在传回的void*指针上进行构造的行为。而delete operator则先析构对象,然后调用名为operator delete的函数,标准形式就像这样:
void operator delete(void* pToDeAlloc)
// GotW中说即使在指针参数后加上size_t size,仍然是标准形式。
// size_t size的作用是检查所要卸除的内存是否是期望的大小,
//如果在类层次中定义的话,只要基类是virtual destructor,那么size可以
// 确保是正确大小。
而placement new……。”
“唔,说得不错,有进步。关于placement new/delete,先知Meyer[1]有详细的论述。原来的placement new仅仅是这样:
void* operator new(size_t, void* pMem) { return pMem; }
‘ 随着时间过去,任何‘要求额外引数’的 operator new 版本,也都渐渐采用 placement new 这个术语。事实上这个术语已经被铭记於 C++ 标准规格中。因此,当 C++ 程序员谈到所谓的 placement new 函数,他们所谈的可能是上述那个‘需要额外一个 void* 参数,用以指出物件置于何处’的版本,但也可能是指那些‘所需参数比单一而必要之 size_t 引数更多’的任何 operator new 版本,包括上述函数,也包括其他‘参数更多’的 operator new 函数。’——引自[1]。
现在我们来考虑一个问题,如果在operator new结束申请内存后,构造函数抛出了异常,那么已经申请的内存谁来回收,答案当然是编译器。因为整个new operator还未结束。所以你无法获得控制权。如果你对operator new/delete进行了重载,那么编译器会调用那个operator delete呢?由于不同的operator new可能通过不同的方法获得内存,而让不知道怎样分配的operator delete去释放内存显然是不负责任的。所以编译器假定哪个operator delete与operator new有相同的参数(除了size_t size和void* pDeAlloc),那么那个operator delete便知道operator new怎样获得内存。构造函数抛出异常后,也会调用与operator new相同参数的operator delete来释放内存。”
“那么,我该怎么解决现在的问题呢?”
“别那么急,已经下午五点了。该回家了,别让人以为程序员是夜游神。回家看看Effective C++第二部分和GotW9, GotW10。明天再说吧。”
第二天一早,Solmyr啃着大饼走进了办公室,看到Guru Zero早已姿势优雅地坐在电脑前收发Email,不禁自惭形秽,连忙放下手中的大饼,跑去和Zero说:“老大,昨天晚上,我挑灯夜读,总结出两点:
1. 如果你写了一个operator new,请对应写一个operator delete。
2. 不要delete不是自己new来的内存。
“嗯,不错,怎么我看上去,就象是Effective C++中的条款呢?你有没有自己想过关于placement delete的问题?”
“placement delete有什么问题吗?”Solmyr一脸茫然的问道。
“你有没有试过把一块用placement new申请得到的内存用placement delete卸除掉呢?不妨你现在试试看。”
只见Solmyr跑到一台电脑前,两手如飞在键盘上敲击,可是我们能听到的只有他的唉声叹气和编译器的哇哇大叫。Solmyr实在是没办法,只能向Zero求教。Zero喝了一口咖啡,说道:“我们平时写的那些要求额外参数的operator delete只有在构造函数抛出异常时,才会被编译器自动调用,而我们是不可能手工调用到任何带有额外参数的delete的,为什么没有一个内建的‘placement delete’来与‘placement new’相匹配的原因在于没有办法保证它被正确使用,在C++类型系统中,无法推断一个指针从哪里获得它指向的内存,可能是指向heap,也可 -
2004-04-05
[转载] Solmyr 的小品文系列之八:拷贝 - [C/C++开发专辑]
Solmyr 的小品文系列之八:拷贝
Elminster
--------------------------------------------------------------------------------
“zero 帮帮忙吧 ~~ ”
“灿烂”的笑脸,充满诚意的眼神,再加上点头哈腰的姿势,这三者构成了一尊名为“有求于人”的塑像。
在 QQ 上聊的正欢的 zero 抬起头,看着塑像的作者和材料 ——— pisces ,方圆五十米内唯一的女性程序员 ——— 问道:“什么事?”
“我这里有一段 C++ 程序调不通。”
“这类问题你应该去问 Solmyr。”
“哎呀,别开玩笑了,我哪敢去问他呀!总说我笨!上次问他一个小问题,结果又被训的狗血喷头,哼!”,pisces 显得忿忿不平,“还是你来帮帮我吧,我知道你是部门里有数的高手,肯定搞的定的。帮帮忙吧 ~~”
zero 明显的被打动了,于是,在 pisces 的努力下,zero 坐到了 pisces 的计算机前。
“好吧,什么问题?”
“是这样的啦,这里有一组 C 风格的 API ,负责管理设备上的字符通信链接。它们是好些年前设计的”,说着,pisces 调出了一些代码:
// old C style API
typedef int conn_handle;
typedef struct
{
/* ... 打开链接所需的参数和属性 ... */
}conn_attr;
conn_handle open_conn(conn_attr* p_attr, char* buf, unsigned int buf_size);
void close_conn(conn_handle h);
char read_conn(conn_handle h);
void write_conn(conn_handle h, char c);
...
“枝节的东西不算,主干大概就是这样,一对函数负责打开和关闭,一对函数负责读写。创建链接时候的那个 buf 参数指向一个缓冲区,这个要你自己分配并把长度传进去,和链接一一对应,read_conn/write_conn 会用它做缓冲。我的任务就是写个类把这些 API 包装起来。”,说着 pisces 又调出了另外一段代码:
// pisces’ connection class
class connection
{
private:
conn_attr m_attr;
bool m_opened;
int m_bufsize;
char* m_buf;
conn_handle m_h;
...
public:
connection(const conn_attr& attr, int bufsize)
{
m_attr = attr;
m_opened = false;
m_bufsize = bufsize;
m_buf = new char[m_bufsize];
}
~connection() { delete m_buf; }
void open()
{
m_h = open_conn(&m_attr, m_buf, m_bufsize);
m_opened = true;
}
void close()
{
close_conn(m_h);
m_opened = false;
}
char read()
{
assert(m_opened);
return read_conn(m_h);
}
void write(char c)
{
assert(m_opened);
write_conn(m_h, c);
}
...
};
“应该是很简单的,可是不知道怎么回事,用了 connection 类的程序总是时不时的崩溃,说是非法的内存操作。”,pisces 显得很苦恼。
zero 一眼就看出了毛病 ——— 这使他小小的自鸣得意了一下 ——— 但是表面上不动声色,等到他看过 pisces 提供的“总是引发崩溃”的代码段之后,他才开口说到:
“这是一个常见的错误 pisces”,zero 尽量使自己的口吻和语气听起来象一个权威,“关于 C++,有一条重要的指导原则:析构函数、拷贝构造函数和赋值运算符三者几乎总是一起出现。也就是说,如果你为一个类写了析构函数,那么往往你不得不再提供一个拷贝构造函数和一个赋值运算符,违反它往往意味着错误。你看这里:”
说着,zero 在屏幕上标出了两行代码:
void some_func()
{
conn_attr attr;
...
connection c1(512, attr);
connection tmp = c1;
...
}
“这里对象 tmp 是从 c1 拷贝构造而来的,而你没有定义拷贝构造函数,这使得编译器在这里自动进行按位拷贝,而这使得 tmp 和 c1 的所有成员都相等,包括 m_buf 成员。这样在函数返回时,c1 析构的时候 delete 了一遍 m_buf,在 tmp 析构的时候又 delete 了一遍 ……”
“哦!我明白了!” pisces 打断了 zero ,“所以就出现一个非法内存操作,对吧?哎呀,这一条以前在学校里写 string 类的时候遇到过,我怎么会忘了呢?”
“对,你只要写一个拷贝构造函数和一个赋值运算符处理一下 m_buf 指针就可以解决这个问题了。这你自己搞的定吧?”
“我可以的,多谢了 zero !”
zero 心满意足的回到了自己的座位上,开始继续和“你不懂我纤细的心”在 QQ 上探讨“爱情的意义”。可是好景不长,没过多久,本文开头所描述的景象再一次的出现了。
“zero 帮帮忙吧 ~~ ”
zero 在心中叹了口气,抬头问道:“又是什么问题,pisces?”
“呃,还是那个类。我照你说的给 conn 添加了拷贝构造函数,非法内存操作确实少多了,可还是有,还有好像链接传输数据也有点问题 ———— 你还是过来帮我看看吧 ~~”
zero 心不甘情不愿的再次来到了 pisces 的计算机前,翻出 pisces 写的拷贝构造函数检查起来:
-
2004-04-05
[转载] Solmyr 的小品文系列之七:异常 - [C/C++开发专辑]
Solmyr 的小品文系列之七:异常
Elminster
--------------------------------------------------------------------------------
大雨。
乌云象铅块一样低低的压了下来,豆大的雨滴打的玻璃窗啪啪作响,难得一见的异常天气正在竭力表现它令人讨厌的一面。不过这一切似乎并没有影响到 Solmyr,他仍然以他习惯的舒适姿势半躺在宽大的椅子里,手里还托着一杯热腾腾的果汁,在他背后,zero 在键盘上敲打着什么。
“唉,Solmyr ,标准库中的 stack 怎么会是这个样子?设计糟透了。”zero 停止了工作,转过身来面对 Solmyr ,看起来有些困惑。
“胡乱批评被纳入神圣标准的成员是会遭天遣的。”Solmyr 低着头,以一种算命先生似的语调答道。
不知道上天是否打算加强 Solmyr 的说服力,恰在此时天空划过一道闪电,蓝白色的电光挣扎着努力向地面扑来,紧接着就是“喀喇”一声巨响 ——— 这个雷很近。
一秒钟前还在想“这未免也太扯了”的 zero 表情一下子变得很古怪,良久才恢复正常。他标出了两行代码接着说到:“好、好吧,Solmyr,那请你解释一下为什么 stack 的界面是这个样子。”
std::stack<int> si;
……
int i = si.top();
si.pop();
“只要让 pop() 返回栈顶元素就可以把上面两行合成一行,而且更加直观,为什么要搞成现在这样?”
目睹了 zero 表情变化的 Solmyr 强忍住放声