[轉(zhuǎn)]靜態(tài)成員數(shù)據(jù)和靜態(tài)成員函數(shù)
在沒有講述本章內(nèi)容之前如果我們想要在一個范圍內(nèi)共享某一個數(shù)據(jù),那么我們會設(shè)立全局對象,但面向?qū)ο蟮某绦蚴怯蓪ο髽?gòu)成的,我們?nèi)绾尾拍茉陬惙秶鷥?nèi)共享數(shù)據(jù)呢?
這個問題便是本章的重點(diǎn):
聲明為static的類成員或者成員函數(shù)便能在類的范圍內(nèi)共同享,我們把這樣的成員稱做靜態(tài)成員和靜態(tài)成員函數(shù)。
下面我們用幾個實例來說明這個問題,類的成員需要保護(hù),通常情況下為了不違背類的封裝特性,我們是把類成員設(shè)置為protected(保護(hù)狀態(tài))的,但是我們?yōu)榱撕喕a,使要說明的問題更為直觀,更容易理解,我們在此處都設(shè)置為public。
以下程序我們來做一個模擬訪問的例子,在程序中,每建立一個對象我們設(shè)置的類靜態(tài)成員變自動加一,代碼如下:
#include <iostream>
using namespace std;
class Internet
{
public:
??? Internet(char *name,char *address)
??? {
??????? strcpy(Internet::name,name);
??????? strcpy(Internet::address,address);
??????? count++;
??? }
??? static void Internet::Sc()//靜態(tài)成員函數(shù)
??? {
??????? cout<<count<<endl;
??? }
??? Internet &Rq();
public:
??? char name[20];
??? char address[20];
??? static int count;//這里如果寫成static int count=0;就是錯誤的
};
Internet& Internet::Rq()//返回引用的成員函數(shù)
{
??? return *this;
}
int Internet::count = 0;//靜態(tài)成員的初始化
void vist()
{
??? Internet a1("中國軟件開發(fā)實驗室","
www.cndev-lab.com
");
??? Internet a2("中國軟件開發(fā)實驗室","
www.cndev-lab.com
");
}
void fn(Internet &s)
{
??? cout<<s.Rq().count;
}
void main()
{
??? cout<<Internet::count<<endl;//靜態(tài)成員值的輸出
??? vist();
??? Internet::Sc();//靜態(tài)成員函數(shù)的調(diào)用
??? Internet b("中國軟件","
www.cnsoft.com
");
??? Internet::Sc();
??? fn(b);
??? cin.get();
}
上面代碼我們用了幾種常用的方式建立對象,當(dāng)建立新對象并調(diào)用其構(gòu)造函數(shù)的時候,靜態(tài)成員cout便運(yùn)行加1操作,靜態(tài)成員的初始化應(yīng)該在主函數(shù)調(diào)用之前,并且不能在類的聲明中出現(xiàn),通過運(yùn)行過程的觀察我們發(fā)現(xiàn),靜態(tài)成員count的狀態(tài)并不會隨著一個新的對象的新建而重新定義,盡而我們了解到類的靜態(tài)成員是屬于類的而不是屬于哪一個對象的,所以靜態(tài)成員的使用應(yīng)該是類名稱加域區(qū)分符加成員名稱的,在上面的代碼中就是Internet::count,雖然我們?nèi)匀豢梢允褂脤ο竺狱c(diǎn)操作符號加成員名稱的方式使用,但是不推薦的,靜態(tài)態(tài)類成員的特性就是屬于類而不專屬于某一個對象。
靜態(tài)成員函數(shù)的特性類似于靜態(tài)成員的使用,同樣與對象無關(guān),調(diào)用方法為類名稱加域區(qū)分符加成員函數(shù)名稱,在上面的代碼中就是Internet::Sc();,靜態(tài)成員函數(shù)由于與對象無關(guān)系,所以在其中是不能對類的普通成員進(jìn)行直接操作的。
如果上面的 static void Internet::Sc()修改成為:
static void Internet::Sc()//靜態(tài)成員函數(shù)
{
??? cout<<name<<endl;//錯誤
??? cout<<count<<endl;
}
靜態(tài)成員函數(shù)與普通成員函數(shù)的差別就在于缺少this指針,沒有這個this指針自然也就無從知道name是哪一個對象的成員了。
根據(jù)類靜態(tài)成員的特性我們可以簡單歸納出幾點(diǎn),靜態(tài)成員的使用范圍:
1.用來保存對象的個數(shù)。
2.作為一個標(biāo)記,標(biāo)記一些動作是否發(fā)生,比如:文件的打開狀態(tài),打印機(jī)的使用狀態(tài),等等。
3.存儲鏈表的第一個或者最后一個成員的內(nèi)存地址。
為了做一些必要的練習(xí),深入的掌握靜態(tài)對象的存在的意義,我們以前面的結(jié)構(gòu)體的教程為基礎(chǔ),用類的方式描述一個線性鏈表,用于存儲若干學(xué)生的姓名,代碼如下:
#include <iostream>
using namespace std;
class Student
{
public:
??? Student (char *name);
??? ~Student();
public:
??? char name[30];
??? Student *next;
??? static Student *point;
};
Student::Student (char *name)
{
??? strcpy(Student::name,name);
??? this->next=point;
??? point=this;
}
Student::~Student ()//析構(gòu)過程就是節(jié)點(diǎn)的脫離過程
{
??? cout<<"析構(gòu):"<<name<<endl;
??? if(point==this)
??? {
??????? point=this->next;
??????? cin.get();
??????? return;
??? }
??? for(Student *ps=point;ps;ps=ps->next)
??? {
??????? if(ps->next==this)
??????? {
??????? cout<<ps->next<<"|"<<this->next<<endl;
??????? ps->next=next;//=next也可以寫成this->next;
??????? cin.get();
??????? return;
??????? }
??? }
??? cin.get();
}
Student* Student::point=NULL;
void main()
{
??? Student *c = new Student("marry");
??? Student a("colin");
??? Student b("jamesji");
??? delete c;
??? Student *fp=Student::point;
??? while(fp!=NULL)
??? {
??????? cout<<fp->name<<endl;
??????? fp=fp->next;
??? }
??? cin.get();
}
從上面的代碼來看,原來單純結(jié)構(gòu)化編程需要的一個鏈表進(jìn)入全局指針在這里被類的靜態(tài)成員指針?biāo)娲?類的靜態(tài)成員完全可以替代全局變量),這個例子的理解重點(diǎn)主要是要注意觀察類成員的析構(gòu)順序,通過對析構(gòu)順序的理解,使用析構(gòu)函數(shù)來進(jìn)行節(jié)點(diǎn)的脫鏈操作。
靜態(tài)成員的提出是為了解決數(shù)據(jù)共享的問題。實現(xiàn)共享有許多方法,如:設(shè)置全局性的變量或?qū)ο笫且环N方法。但是,全局變量或?qū)ο笫怯芯窒扌缘摹_@一章里,我們主要講述類的靜態(tài)成員來實現(xiàn)數(shù)據(jù)的共享。
靜態(tài)數(shù)據(jù)成員
在類中,靜態(tài)成員可以實現(xiàn)多個對象之間的數(shù)據(jù)共享,并且使用靜態(tài)數(shù)據(jù)成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態(tài)成員是類的所有對象中共享的成員,而不是某個對象的成員。
使用靜態(tài)數(shù)據(jù)成員可以節(jié)省內(nèi)存,因為它是所有對象所公有的,因此,對多個對象來說,靜態(tài)數(shù)據(jù)成員只存儲一處,供所有對象共用。靜態(tài)數(shù)據(jù)成員的值對每個對象都是一樣,但它的值是可以更新的。只要對靜態(tài)數(shù)據(jù)成員的值更新一次,保證所有對象存取更新后的相同的值,這樣可以提高時間效率。
靜態(tài)數(shù)據(jù)成員的使用方法和注意事項如下:
1、靜態(tài)數(shù)據(jù)成員在定義或說明時前面加關(guān)鍵字static。
2、靜態(tài)成員初始化與一般數(shù)據(jù)成員初始化不同。靜態(tài)數(shù)據(jù)成員初始化的格式如下:
<數(shù)據(jù)類型><類名>::<靜態(tài)數(shù)據(jù)成員名>=<值>
這表明:
????? (1) 初始化在類體外進(jìn)行,而前面不加static,以免與一般靜態(tài)變量或?qū)ο笙嗷煜?/font>
(2) 初始化時不加該成員的訪問權(quán)限控制符private,public等。
(3) 初始化時使用作用域運(yùn)算符來標(biāo)明它所屬類,因此,靜態(tài)數(shù)據(jù)成員是類的成員,而不是對象的成員。
3、靜態(tài)數(shù)據(jù)成員是靜態(tài)存儲的,它是靜態(tài)生存期,必須對它進(jìn)行初始化。
4、引用靜態(tài)數(shù)據(jù)成員時,采用如下格式:
<類名>::<靜態(tài)成員名>
如果靜態(tài)數(shù)據(jù)成員的訪問權(quán)限允許的話(即public的成員),可在程序中,按上述格式來引用靜態(tài)數(shù)據(jù)成員。
下面舉一例子,說明靜態(tài)數(shù)據(jù)成員的應(yīng)用:
#include
class Myclass
{
public:
Myclass(int a, int b, int c);
void GetNumber();
void GetSum();
private:
int A, B, C;
static int Sum;
};
int Myclass::Sum = 0;
Myclass::Myclass(int a, int b, int c)
{
A = a;
B = b;
C = c;
Sum += A+B+C;
}
void Myclass::GetNumber()
{
cout<<"Number="< }
void Myclass::GetSum()
{
cout<<"Sum="< }
void main()
{
Myclass M(3, 7, 10),N(14, 9, 11);
M.GetNumber();
N.GetNumber();
M.GetSum();
N.GetSum();
}
從輸出結(jié)果可以看到Sum的值對M對象和對N對象都是相等的。這是因為在初始化M對象時,將M對象的三個int型數(shù)據(jù)成員的值求和后賦給了Sum,于是Sum保存了該值。在初始化N對象時,對將N對象的三個int型數(shù)據(jù)成員的值求和后又加到Sum已有的值上,于是Sum將保存另后的值。所以,不論是通過對象M還是通過對象N來引用的值都是一樣的,即為54。
靜態(tài)成員函數(shù)
靜態(tài)成員函數(shù)和靜態(tài)數(shù)據(jù)成員一樣,它們都屬于類的靜態(tài)成員,它們都不是對象成員。因此,對靜態(tài)成員的引用不需要用對象名。
在靜態(tài)成員函數(shù)的實現(xiàn)中不能直接引用類中說明的非靜態(tài)成員,可以引用類中說明的靜態(tài)成員。如果靜態(tài)成員函數(shù)中要引用非靜態(tài)成員時,可通過對象來引用。下面通過例子來說明這一點(diǎn)。
#include
class M
{
public:
M(int a) { A=a; B+=a;}
static void f1(M m);
private:
int A;
static int B;
};
void M::f1(M m)
{
cout<<"A="< cout<<"B="< }
int M::B=0;
void main()
{
M P(5),Q(10);
M::f1(P); file://調(diào)用時不用對象名
M::f1(Q);
}
讀者可以自行分析其結(jié)果。從中可看出,調(diào)用靜態(tài)成員函數(shù)使用如下格式:
<類名>::<靜態(tài)成員函數(shù)名>(<參數(shù)表>);
posted @
2006-07-31 02:54 Jerry Cat 閱讀(294) |
評論 (0) |
編輯 收藏
為MFC應(yīng)用程序添全屏幕顯示功能
在CMainFrame類中添加下列成員變量和成員函數(shù)(使用ClassWizard),下面是這些變量和函數(shù)的功能
說明:
成員變量:
BOOL m_bFullScreen; //全屏幕顯示標(biāo)志
CRect m_FullScreenWindowRect; //全屏幕顯示窗口Rect
WINDOWPLACEMENT m_wpPrev; //用于保存正常視圖時的窗口位置信息
CToolBar * m_wndFullScreenBar; //全屏幕顯示時的浮動工具條
成員函數(shù):
void OnMenuFullscreen(); //全屏幕顯示的處理函數(shù)
void OnGetMinMaxInfo(); //捕獲WM_GETMINMAXINFO消息以便允許你增加窗口大小
void OnUpdateViewFullScreen(); //更新“全屏幕顯示”菜單的狀態(tài)
源碼
? void CMainFrame::OnMenuFullscreen()
{//全屏幕顯示的處理函數(shù)
RECT rectDesktop;
WINDOWPLACEMENT wpNew;
if (m_bFullScreen)
{//全屏幕顯示模式
//隱藏工具條和狀態(tài)條
m_wndStatusBar.ShowWindow(SW_HIDE);
m_wndToolBar.ShowWindow(SW_HIDE);
//保存正常視圖時的窗口位置信息以便恢復(fù)原來狀態(tài)
GetWindowPlacement (&m_wpPrev);
m_wpPrev.length = sizeof m_wpPrev;
//調(diào)整RECT為新的窗口尺寸
::GetWindowRect ( ::GetDesktopWindow(), &rectDesktop );
::AdjustWindowRectEx(&rectDesktop, GetStyle(), TRUE, GetExStyle());
//保存RECT以便OnGetMinMaxInfo()使用
m_FullScreenWindowRect = rectDesktop;
wpNew = m_wpPrev;
wpNew.showCmd = SW_SHOWNORMAL;
wpNew.rcNormalPosition = rectDesktop;
//生成新的工具條
m_wndFullScreenBar=new CToolBar;
if(!m_wndFullScreenBar->Create(this, CBRS_SIZE_DYNAMIC|CBRS_FLOATING)
|| !m_wndFullScreenBar->LoadToolBar(IDR_FULLSCREEN))
{
TRACE0("Failed to create toolbar\n");
return; // fail to create
}
}
posted @
2006-07-31 02:51 Jerry Cat 閱讀(387) |
評論 (0) |
編輯 收藏
MFC socket程序開發(fā)
Socket編程在大多數(shù)的編程語言中都是一件比較有趣的事情。它是比較常用的編寫通過網(wǎng)絡(luò)通信的服務(wù)器和客戶端方法。在windows平臺Socket通信大多是基于MS Winsock設(shè)計的。Windows支持基于TCP和UDP的socket通信。Windows APIs在socket編程中是非常有用的,但是有些人發(fā)現(xiàn)在用它們工作的時候有困難。
所以在這里我介紹一種最簡單用MFC socket類進(jìn)行socket編程的方法。這不僅可以使你的工作變得簡單而且能減少你在網(wǎng)絡(luò)程序上的開發(fā)時間。你可以定制一個socket類,然后你可以在你的其他的網(wǎng)絡(luò)應(yīng)用程序中重用。
在socket編程中,MFC提供了兩個基本的類,分別是CAsyncSocket和Csocket。Csocket是從CAsyncSocket繼承來的。我們可以建立定制的socket類,也是從CasyncSocket繼承而來的,當(dāng)然也是為了我們程序特殊的需要。
初始化socket
首先需要調(diào)用AfxSocketInit()函數(shù)來初始化我們的socket環(huán)境。
為了初始化sockets,我們需要調(diào)用AfxSocketInit()函數(shù)。它通常是在MFC中的InitInstance()函數(shù)中被調(diào)用的。如果我們用程序向?qū)韯?chuàng)建socket程序的話,查看“use Windows Sockets”這個選項,然后選中它。它將會自動的為我們創(chuàng)建這個步驟了。(如果我們沒有選中這個選項的話,我們也可以手動添加這些代碼的。)這個函數(shù)的返回值顯示這個函數(shù)的調(diào)用成功或失敗。
BOOL CServerApp::InitInstance()
{....
if( AfxSocketInit() == FALSE)
? {
? AfxMessageBox("Sockets Could Not Be Initialized");
? return FALSE;
? }
? ...
}
創(chuàng)建Server Sockets
為了創(chuàng)建一個Server Socket,我們需要聲明一個CAyncSocket的變量或者我們自己定制的一個從AyncSocket或是Cscket繼承來的類的類型的變量。然后調(diào)用Create()函數(shù),同時指定監(jiān)聽的端口。這個函數(shù)的返回值顯示這個函數(shù)的調(diào)用成功或失敗。
UpdateData(TRUE);
m_sListener.Create(m_port);
if(m_sListener.Listen()==FALSE)
{
AfxMessageBox("Unable to Listen on that port,please try another port");
m_sListener.Close();
return;
}
創(chuàng)建Client Sockets
為了創(chuàng)建Client socket類,我們需要聲明一個CAyncSocket的變量或者我們自己定制的一個從AyncSocket或是Cscket繼承來的類的類型的變量。然后調(diào)用Create()函數(shù),同時指定監(jiān)聽的端口。這個函數(shù)的返回值顯示這個函數(shù)的調(diào)用成功或失敗。
m_sConnected.Create();
m_sConnected.Connect("server ip",port);
監(jiān)聽客戶端的連接
創(chuàng)建了server socket以后,我們要進(jìn)行監(jiān)聽。調(diào)用Listen()函數(shù)。這個函數(shù)的返回值顯示這個函數(shù)的調(diào)用成功或失敗。
if( m_sListener.Listen()== FALSE)
{
AfxMessageBox("Unable to Listen on that port,please try another port");
m_sListener.Close();
return;
}
接受連接
連接請求要被接受accept,是用另外的socket,不是正在監(jiān)聽的socket。請參看代碼。
void CXXXDlg::OnAccept()
{
?CString strIP;
?UINT port;
?if(m_sListener.Accept(m_sConnected))
?{
? m_sConnected.GetSockName(strIP,port); //應(yīng)該是GetPeerName,獲取對方的IP和port
? m_status="Client Connected,IP :"+ strIP;
? m_sConnected.Send("Connected To Server",strlen("Connected To? Server"));?????
UpdateData(FALSE);
?}
?else
?{
?AfxMessageBox("Cannoot Accept Connection");
?}
}
發(fā)送數(shù)據(jù)
數(shù)據(jù)放在一個buffer中或是結(jié)構(gòu)體中,調(diào)用send()函數(shù)發(fā)送。
m_sConnected.Send(pBuf,iLen);
接受數(shù)據(jù)
調(diào)用receive()接受數(shù)據(jù)。
void CXXXrDlg::OnReceive()
{
char *pBuf =new char [1025];
CString strData;
int iLen;
iLen=m_sConnected.Receive(pBuf,1024);???
if(iLen == SOCKET_ERROR)?????
? {
? AfxMessageBox("Could not Recieve");?????
? }?????
else????
? {
? pBuf[iLen]=NULL;
? strData=pBuf;
? m_recieveddata.Insert(m_recieveddata.GetLength(),strData);
?//display in server?????????????
?UpdateData(FALSE);
?m_sConnected.Send(pBuf,iLen);? //send the data back to the Client????
?delete pBuf;?????????
? }
}
關(guān)閉連接
m_sConnected.ShutDown(0);??? 停止發(fā)送數(shù)據(jù)
m_sConnected.ShutDown(1);??? 停止接受數(shù)據(jù)
m_sConnected.ShutDown(2);??? 停止發(fā)送接受數(shù)據(jù)
m_sConnected.Close();
編寫自己的socket類
在class view中選擇添加一個新類,設(shè)置它的基類為CAsyncSocket,在類向?qū)У膸椭绿砑尤缦碌囊恍┖瘮?shù)。
class MySocket : public CAsyncSocket
{ // Attributes
public:
?// Operations
public:
MySocket();
virtual ~MySocket();
// Overrides
public:
void SetParentDlg(CDialog *pDlg);// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(MySocket)
public:
virtual void OnAccept(int nErrorCode);
virtual void OnClose(int nErrorCode);
virtual void OnConnect(int nErrorCode);
virtual void OnOutOfBandData(int nErrorCode);
virtual void OnReceive(int nErrorCode);
virtual void OnSend(int nErrorCode);
//}}AFX_VIRTUAL // Generated message map functions
//{{AFX_MSG(MySocket)
// NOTE - the ClassWizard will add and remove member functions here. //}}AFX_MSG
protected:
private:
CDialog * m_pDlg;
};
設(shè)置“Parent Dialog”
調(diào)用這個socket類的SetParentDlg函數(shù),保證當(dāng)socket事件發(fā)生的時候這個窗體能接收到。
m_sListener.SetParentDlg(this);
m_sConnected.SetParentDlg(this);
建立Socket 事件和窗體成員函數(shù)之間的聯(lián)系
在這個窗體類中添加一些函數(shù),比如void OnReceive(); void OnClose(); void OnAccept(); void OnConnect()等,它們會在我們編寫的的socket類中調(diào)用到。
void MySocket::OnAccept(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
if(nErrorCode==0)
{
((CServerDlg*)m_pDlg)->OnAccept();
}
CAsyncSocket::OnAccept(nErrorCode);
}
這里只寫了一個OnAccept()函數(shù),其他的幾個中也有類似的調(diào)用。詳細(xì)的請參考代碼。
posted @
2006-07-28 01:07 Jerry Cat 閱讀(814) |
評論 (0) |
編輯 收藏
構(gòu)造函數(shù)、析構(gòu)函數(shù)與賦值函數(shù)是每個類最基本的函數(shù)。每個類只有一個析構(gòu)函數(shù),但可以有多個構(gòu)造函數(shù)(包含一個拷貝構(gòu)造函數(shù),其它的稱為普通構(gòu)造函數(shù))和多個賦值函數(shù)(除了同類的賦值以外,還有其他的賦值方法)。對于任意一個類A,如果不想編寫上述函數(shù),C++編譯器將自動為A產(chǎn)生四個缺省的函數(shù),如
A(void);??????????????????? // 缺省的無參數(shù)構(gòu)造函數(shù)
A(const A &a);????????????? // 缺省的拷貝構(gòu)造函數(shù)
~A(void);?????????????????? // 缺省的析構(gòu)函數(shù)
A & operate =(const A &a);? // 缺省的賦值函數(shù)
有幾個需要注意的內(nèi)容:
@ 構(gòu)造函數(shù)與析構(gòu)函數(shù)的另一個特別之處是沒有返回值類型
@ 構(gòu)造從類層次的最頂層的基類開始,在每一層中,首先調(diào)用基類的構(gòu)造函數(shù),然后調(diào)用成員對象的構(gòu)造函數(shù)。析構(gòu)則嚴(yán)格按照與構(gòu)造相反的次序執(zhí)行,在析構(gòu)的時候,最低層的派生類的析構(gòu)函數(shù)最開始被調(diào)用,然后調(diào)用每個基類的析構(gòu)函數(shù)。
@ “缺省的拷貝構(gòu)造函數(shù)”和“缺省的賦值函數(shù)”均采用“位拷貝”而非“值拷貝”的方式來實現(xiàn),倘若類中含有指針變量,這兩個函數(shù)注定將出錯
下面通過例子進(jìn)一步說明,
1.構(gòu)造函數(shù)的初始化表
設(shè)存在兩個類:
class
?A
{
????…
????A(
void
);????????????????
//
?無參數(shù)構(gòu)造函數(shù)
????A(
const
?A?
&
other);??????
//
?拷貝構(gòu)造函數(shù)
????A?
&
?operate?
=
(?
const
?A?
&
other);??
//
?賦值函數(shù)
????
virtual
?
~
A(
void
);????????
//
析構(gòu)函數(shù)
};
class
?B
{
public
:
????B(
const
?A?
&
a);????
//
?B的構(gòu)造函數(shù)
private
:???
????A??m_a;????????????
//
?成員對象
};
下面面是B的構(gòu)造函數(shù)的2個實現(xiàn),其中第一個的類B的構(gòu)造函數(shù)在其初始化表里調(diào)用了類A的拷貝構(gòu)造函數(shù),從而將成員對象m_a初始化;而第二個的B的構(gòu)造函數(shù)在函數(shù)體內(nèi)用賦值的方式將成員對象m_a初始化。我們看到的只是一條賦值語句,但實際上B的構(gòu)造函數(shù)干了兩件事:先暗地里創(chuàng)建m_a對象(調(diào)用了A的無參數(shù)構(gòu)造函數(shù)),再調(diào)用類A的賦值函數(shù),將參數(shù)a賦給m_a。
B::B(
const
?A?
&
a)
?:?m_a(a)
{
???…
}
B::B(
const
?A?
&
a)
{
????m_a?
=
?a;
????…
}
2.拷貝函數(shù)和構(gòu)造函數(shù)的區(qū)別
拷貝構(gòu)造函數(shù)是在對象被創(chuàng)建時調(diào)用的,而賦值函數(shù)只能被已經(jīng)存在了的對象調(diào)用。
String? a(“hello”);
String? b(“world”);
String? c = a;? // 調(diào)用了拷貝構(gòu)造函數(shù),最好寫成 c(a);
c = b; ?? ??? ??? ?// 調(diào)用了賦值函數(shù)
本例中第三個語句的風(fēng)格較差,宜改寫成String c(a) 以區(qū)別于第四個語句。
如果我們實在不想編寫拷貝構(gòu)造函數(shù)和賦值函數(shù),又不允許別人使用編譯器生成的缺省函數(shù),可以將拷貝構(gòu)造函數(shù)和賦值函數(shù)聲明為私有函數(shù),不用編寫代碼。
3.析構(gòu)函數(shù)與虛析構(gòu)函數(shù)
基類的構(gòu)造函數(shù)、析構(gòu)函數(shù)、賦值函數(shù)都不能被派生類繼承。如果類之間存在繼承關(guān)系,在編寫上述基本函數(shù)時應(yīng)注意以下事項:
@ 派生類的構(gòu)造函數(shù)應(yīng)在其初始化表里調(diào)用基類的構(gòu)造函數(shù)
@ 基類與派生類的析構(gòu)函數(shù)應(yīng)該為虛(即加virtual關(guān)鍵字)
#include?
<
iostream
>
class
?Base
{
public
:
????
virtual
?
~
Base()?{?cout
<<
?
"
~Base
"
?
<<
?endl?;?}
};
class
?Derived?:?
public
?Base
{
public
:
????
virtual
?
~
Derived()?{?cout
<<
?
"
~Derived
"
?
<<
?endl?;?}
};
void
?main(
void
)
{
????Base?
*
?pB?
=
?
new
?Derived;??
//
?upcast
???delete?pB;
}
輸出結(jié)果為:
?????? ~Derived
?????? ~Base
如果析構(gòu)函數(shù)不為虛,那么輸出結(jié)果為
?????? ~Base
posted @
2006-07-28 00:50 Jerry Cat 閱讀(216) |
評論 (0) |
編輯 收藏
什么是對象的句柄?它是指針嗎?它是引用嗎?它是指向指針的指針?它是什么?
句柄術(shù)語一般用來指獲取另一個對象的方法——一個廣義的假指針。這個術(shù)語是(故意的)含糊不清的。
含糊不清在實際中的某些情況下是有用的。例如,在早期設(shè)計時,你可能不準(zhǔn)備用句柄來表示。你可能不確定是否將一個簡單的指針或者引用或者指向指針的指針或者指向引用的指針或者整型標(biāo)識符放在一個數(shù)組或者字符串(或其它鍵)以便能夠以哈希表(hash-table)(或其他數(shù)據(jù)結(jié)構(gòu))或數(shù)據(jù)庫鍵或者一些其它的技巧來查詢。如果你只知道你會需要一些唯一標(biāo)識的東西來獲取對象,那么這些東西就被稱為句柄。
因此,如果你的最終目標(biāo)是要讓代碼唯一的標(biāo)識/查詢一個Fred類的指定的對象的話,你需要傳遞一個Fred句柄這些代碼。句柄可以是一個能被作為眾所周知的查詢表中的鍵(key)來使用的字符串(比如,在std::map<std::string,Fred> 或 std::map<std::string,Fred*>中的鍵),或者它可以是一個作為數(shù)組中的索引的整數(shù)(比如,F(xiàn)red* array = new Fred[maxNumFreds]),或者它可以是一個簡單的 Fred*,或者它可以是其它的一些東西。
初學(xué)者常常考慮指針,但實際上使用未初始化的指針有底層的風(fēng)險。例如,如果Fred對象需要移動怎么辦?當(dāng)Fred對象可以被安全刪除時我們?nèi)绾潍@知?如果Fred對象需要(臨時的)連續(xù)的從磁盤獲得怎么辦?等等。這些時候的大多數(shù),我們增加一個間接層來管理位置。例如,句柄可以是Fred**,指向Fred*的指針可以保證不會被移動。當(dāng)Fred對象需要移動時,你只要更新指向Fred*的指針就可以了。或者讓用一個整數(shù)作為句柄,然后在表或數(shù)組或其他地方查詢Fred的對象(或者指向Fred對象的指針)。
重點(diǎn)是當(dāng)我們不知道要做的事情的細(xì)節(jié)時,使用句柄。
使用句柄的另一個時機(jī)是想要將已經(jīng)完成的東西含糊化的時候(有時用術(shù)語magic cookie也一樣,就像這樣,“軟件傳遞一個magic cookie來唯一標(biāo)識并定位適當(dāng)?shù)腇red對象”)。將已經(jīng)完成的東西含糊化的原因是使得句柄的特殊細(xì)節(jié)或表示物改變時所產(chǎn)生的連鎖反應(yīng)最小化。舉例來說,當(dāng)將一個句柄從用來在表中查詢的字符串變?yōu)樵跀?shù)組中查詢的整數(shù)時,我們可不想更新大量的代碼。
當(dāng)句柄的細(xì)節(jié)或表示物改變時,維護(hù)工作更為簡單(或者說閱讀和書寫代碼更容易),因此常常將句柄封裝到類中。這樣的類常重載operator-> 和 operator*算符(既然句柄的效果象指針,那么它可能看起來也象指針)。
posted @
2006-07-28 00:48 Jerry Cat 閱讀(546) |
評論 (0) |
編輯 收藏
[轉(zhuǎn)]重載函數(shù)和重載運(yùn)算符
重載函數(shù)有如下約束
@ 該組重載函數(shù)中任何兩個都必須有不同的參量表。
@ 具有相同類型參量表、僅在返回值類型上不同的重載函數(shù)會引起錯誤。
@ 成員函數(shù)的重載不能僅基于一個說明為靜態(tài)的,另一個說明為非靜態(tài)的。
@ typedef說明并未定義新的類型,它們僅為已存在的類型引入了一個同義詞。它們不能影響重載機(jī)制。
@ 枚舉類型是一些可區(qū)分的類型,故可以區(qū)分重載函數(shù)。
@ 從區(qū)分重載函數(shù)的意義上說,類型“數(shù)組”和“指針”是相同的。對于一維數(shù)組來說是正確的。
運(yùn)算符重載有如下的約束
@ 運(yùn)算符要遵守它們同內(nèi)部類型一起使用所指定的優(yōu)先原則、分組及操作數(shù)的個數(shù)。
@ 單目運(yùn)算符說明為成員函數(shù)不帶參量;如果說明為全局函數(shù),要帶一個參量。雙目運(yùn)算符說明為成員函數(shù)只帶一個參量;如果說明為全局函數(shù),要帶兩個參量。
@ 所有的重載運(yùn)算符除了賦值(operator=)外均可被派生類繼承。
@ 重載運(yùn)算符的成員函數(shù)的第一個參量總是激活該運(yùn)算符的對象的類類型參量(運(yùn)算符被定義的類,或者定義了運(yùn)算符的類的派生類)。對于第一個參量也不支持轉(zhuǎn)換。
具體內(nèi)容:
單目運(yùn)算符函數(shù)
ret-type operator op()?? ??? ??? ?//成員,使用類型的內(nèi)部成員
ret-type operator op(arg)?? ??? ?//全局,參數(shù)為對其操作的類型的變量
雙目運(yùn)算符函數(shù)
ret-type oprator op(arg)?? ??? ?//arg可以為任意類型的變量
ret-type operator op(arg1, arg2)
?? ??? ?//全局,arg1和arg2是參量。至少其中之一必須是操作類類型。
注意:對于雙目運(yùn)算符的返回類型沒有限制;然而大多數(shù)用戶自定義型雙目運(yùn)算符返回類類型或類類型的引用。
參考:
C++運(yùn)算符重載轉(zhuǎn)換運(yùn)算符
C++運(yùn)算符重載賦值運(yùn)算符
posted @
2006-07-28 00:43 Jerry Cat 閱讀(455) |
評論 (0) |
編輯 收藏
[轉(zhuǎn)]智能指針與微妙的隱式轉(zhuǎn)換
??? C++
雖然是強(qiáng)類型語言,但是卻還不如
Java
、
C#
那么足夠的強(qiáng)類型,原因是允許的隱式轉(zhuǎn)換太多
-
從
C
語言繼承下來的基本類型之間的隱式轉(zhuǎn)換
-
T*
指針到
void*
的隱式轉(zhuǎn)換
-
non-explicit constructor
接受一個參數(shù)的隱式轉(zhuǎn)換
-
從子類到基類的隱式轉(zhuǎn)換
(
安全)
-
從
const
到
non-const
的同類型的隱式轉(zhuǎn)換
(
安全
)
除開上面的五種隱式轉(zhuǎn)換外,
C++
的編譯器還非常聰明,當(dāng)沒法直接隱式轉(zhuǎn)換的時候,它會嘗試間接的方式隱式轉(zhuǎn)換,這使得有時候的隱式轉(zhuǎn)換非常的微妙,一個誤用會被編譯器接受而會出現(xiàn)意想不到的結(jié)果。例如假設(shè)類
A
有一個
non-explicit constructor
,唯一的參數(shù)是類
B
,而類
B
也有一個
non-explicit constructor
接受類型
C
,那么當(dāng)試圖用類型
C
的實例初始化類
A
的時候,編譯器發(fā)現(xiàn)沒有直接從類型
C
構(gòu)造的過程,但是呢,由于類
B
可以被接受,而類型
C
又可以向類型
B
隱式轉(zhuǎn)換,因此從
C->B->A
的路就通暢了。這樣的隱式轉(zhuǎn)換多數(shù)時候沒什么大礙,但是不是我們想要的,因為它可能造成一些微妙的
bug
而難以捕捉。
?
為了在培訓(xùn)的時候展示棧上析構(gòu)函數(shù)的特點(diǎn)和自動資源管理,準(zhǔn)備下面的一個例子,結(jié)果測試的時候由于誤用而發(fā)現(xiàn)一些問題。
(
測試的
IDE
是
Visual Studio 2005)
class A
{
public:
A(){ a = 100; }
int a;
void f();
};
?
A * pa = new A();
std::auto_ptr<A>? p = pa;? //
無意這樣使用的,本意是
std::auto_ptr<A> p(pa)
p->f();
?
這個寫法是拷貝構(gòu)造函數(shù)的形式,顯然從
T*
是不能直接拷貝構(gòu)造的
auto_ptr
的,但是編譯器會嘗試其他的路徑來轉(zhuǎn)換成
auto_ptr
來拷貝構(gòu)造,因此如果存在一個中間的
,這個類能接受從
T*
的構(gòu)造,而
同時auto_ptr也能接受從類X
的構(gòu)造,那編譯器就會很高興的生成這樣的代碼。
這段代碼在
VC6
上是通不過的,因為
VC6
的
auto_ptr
實現(xiàn)就只有一個接受
T*
指針的
explicit constructor
.
但是
C++ Standard
的修正規(guī)范中,要求
auto_ptr
還應(yīng)該有個接受
auto_ptr_ref
的
constructor
。那么這個
auto_ptr_ref
是什么呢?按照
C++ Standard
的解釋
:
Template auto_ptr_ref holds a reference to an auto_ptr. It is used by the auto_ptr
conversions to allow auto_ptr
objects to be passed to and returned from functions.
有興趣可以參考
Scott Meyers
的
" auto_ptr update page "? (
http://www.awprofessional.com/content/images/020163371X/autoptrupdate%5Cauto_ptr_update.html
?)講訴auto_ptr的歷史.
?
再回到前面的代碼,本來應(yīng)該是通不過的編譯,但是
VC2005
的編譯器卻沒有任何怨言的通過
(
即使把警告等級設(shè)置到
4)
。結(jié)果運(yùn)行的時候卻崩潰了,出錯在
auto_ptr
的析構(gòu)函數(shù)
,delete
的指針?biāo)赶虻刂肥?/span>
100
,而如果在
p->f()
后面加上一句
cout << pa->a << endl;
發(fā)現(xiàn)輸出結(jié)果為
0
。
為什么會這樣,原因就是前面所訴的間接的隱式轉(zhuǎn)換,這與
VC 2006
的
auto_ptr
和
auto_ptr_ref
實現(xiàn)有關(guān),看看
P.J.Plauger
是怎么實現(xiàn)的
:
// auto_ptr_ref
template<class _Ty>
struct auto_ptr_ref
{
// proxy reference for auto_ptr copying
auto_ptr_ref(void *_Right)
: _Ref(_Right)
{?? // construct from generic pointer to auto_ptr ptr
}
void *_Ref;// generic pointer to auto_ptr ptr
};
?
// construct auto_ptr from an auto_ptr_ref object
auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()
{
// construct by assuming pointer from _Right auto_ptr_ref
_Ty **_Pptr = (_Ty **)_Right._Ref;
_Ty *_Ptr = *_Pptr;
*_Pptr = 0;
// release old
_Myptr = _Ptr;
// reset this
}
?
這樣代碼通過編譯的原因也就清楚了,
A* -> void * -> auto_ptr_ref -> auto_ptr -> copy constructor -> accept.
好長的隱式轉(zhuǎn)換鏈
, -_-, C++
編譯器太聰明了。
那么為什么最后會出現(xiàn)指針被破壞的結(jié)果呢,原因在
auto_ptr
的實現(xiàn),因為按照
C++ Standard
要求,
auto_ptr_ref
應(yīng)該是包含一個
auto_ptr
的引用,因此
auto_ptr
的構(gòu)造函數(shù)也就假設(shè)了
auto_ptr_ref
的成員
_Ref
是一個指向
auto_ptr
的指針。
而
auto_ptr
中只有一個成員就是
A*
的指針,因此指向
auto_ptr
對象的指針相當(dāng)于就是個
A**
指針,因此上面
auto_ptr
從
auto_ptr_ref
構(gòu)造的代碼是合理的。
但是由于罪惡的
void*
造成了一條非常寬敞的隱式轉(zhuǎn)換的道路,
A*
指針也能夠被接受,因此把
A*
當(dāng)作
A**
來使用,結(jié)果可想而知,
A*
指向地址的前
4
個字節(jié)
(
因為
32
位
OS)
被拷貝出來,而這四個字節(jié)被賦值為
0( *_Pptr=0 )
。
所以出現(xiàn)了最后的結(jié)果是
_Myptr
值為
100
,而
pa->a
為
0
。
如果要正確執(zhí)行結(jié)果,只要保證是個
A**
指針就行了,有兩個方法
第一,
auto_ptr_ref
所包含的引用是指向的
auto_ptr
對象
A * p = new A();
std::auto_ptr<A> pt( new A() );
std::auto_ptr_ref<A> ra( pt );
std::auto_ptr<A> pb
=
ra
;
pb->f();
?
第二,直接用二級指針
A * p = new A();
std::auto_ptr<A> pb = &p;? //
這句話后
, p
將等于
0
pb->f();
?
當(dāng)然第二種是利用了
VC2005
的實現(xiàn)而造出來的,看著很別扭
,:)
。
我不明白
P.J.Plauger
為什么用
void *
,而不是用
auto_ptr<T>&
,因為任何指針都能隱式轉(zhuǎn)換為
void *
,這樣的危險性大多了。并且如果用了
auto_ptr<T>&
,從
auto_ptr_ref
構(gòu)造也容易寫得更簡單清楚,看看以前的實現(xiàn)方式吧,仍然是
P.J.Plauger
的,但是版本低了點(diǎn):
template<class _Ty>
struct auto_ptr_ref
{
// proxy reference for auto_ptr copying
?
auto_ptr_ref(auto_ptr<_Ty>& _Right)
: _Ref(_Right)
{
// construct from compatible auto_ptr
}
auto_ptr<_Ty>& _Ref;
// reference to constructor argument
};
auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()
: _Myptr(_Right._Ref.release())
{
// construct by assuming pointer from _Right auto_ptr_ref
}
?
這樣的實現(xiàn)方法,顯然不能接受任何指針的隱式轉(zhuǎn)換,也就防止一開始的那種錯誤寫法,并且也是符合
C++ Standard
的要求的。
而
SGI STL
的
auto_ptr_ref
的實現(xiàn)則是包含了一個
T*
的指針,構(gòu)造
auto_ptr
時候直接從
auto_ptr_ref
中拷貝這個指針,因此這樣的實現(xiàn)可以上代碼編譯通過,運(yùn)行也正確,不過不符合
C++ Standard
。
?
總結(jié)一下,危險的潛伏bug的隱式轉(zhuǎn)換應(yīng)該被杜絕的,特別是
void *
的隱式轉(zhuǎn)換和構(gòu)造函數(shù)的隱式轉(zhuǎn)換,因此建議是
:
-
慎用
void *
,因為
void *
必須要求你知道轉(zhuǎn)換前的實現(xiàn),因此更適合用在底層的、性能相關(guān)的內(nèi)部實現(xiàn)。
-
單一參數(shù)的構(gòu)造函數(shù)應(yīng)該注意是否允許隱式轉(zhuǎn)換,如果不需要,加上
explicit
。例如
STL
容器中
vector
接受從
int
的構(gòu)造函數(shù),用于預(yù)先申請空間,這樣的構(gòu)造函數(shù)顯然不需要隱式轉(zhuǎn)換,因此加上了
explicit
。
-
重載函數(shù)中,如果可能,就用更有明確意義的名字替代重載,因為隱式轉(zhuǎn)換也許會帶來一些意想不到的麻煩。
-
避免隱式轉(zhuǎn)換不等于是多用顯示轉(zhuǎn)換。
Meyers
在
Effective C++
中提到,即使
C++
風(fēng)格的顯示轉(zhuǎn)換也應(yīng)該盡量少用,最好是改進(jìn)設(shè)計。
posted @
2006-07-25 00:37 Jerry Cat 閱讀(781) |
評論 (0) |
編輯 收藏
[轉(zhuǎn)]Linux下使用和生成庫
基本概念
庫有動態(tài)與靜態(tài)兩種,動態(tài)通常用.so為后綴,靜態(tài)用.a為后綴。例如:libhello.so libhello.a
為了在同一系統(tǒng)中使用不同版本的庫,可以在庫文件名后加上版本號為后綴,例如: libhello.so.1.0,由于程序連接默認(rèn)以.so為文件后綴名。所以為了使用這些庫,通常使用建立符號連接的方式。
ln -s libhello.so.1.0 libhello.so.1
ln -s libhello.so.1 libhello.so
使用庫
當(dāng)要使用靜態(tài)的程序庫時,連接器會找出程序所需的函數(shù),然后將它們拷貝到執(zhí)行文件,由于這種拷貝是完整的,所以一旦連接成功,靜態(tài)程序庫也就不再需要了。然而,對動態(tài)庫而言,就不是這樣。動態(tài)庫會在執(zhí)行程序內(nèi)留下一個標(biāo)記‘指明當(dāng)程序執(zhí)行時,首先必須載入這個庫。由于動態(tài)庫節(jié)省空間,linux下進(jìn)行連接的缺省操作是首先連接動態(tài)庫,也就是說,如果同時存在靜態(tài)和動態(tài)庫,不特別指定的話,將與動態(tài)庫相連接。
現(xiàn)在假設(shè)有一個叫hello的程序開發(fā)包,它提供一個靜態(tài)庫libhello.a 一個動態(tài)庫libhello.so,一個頭文件hello.h,頭文件中提供sayhello()這個函數(shù)
/* hello.h */
void sayhello();
另外還有一些說明文檔。這一個典型的程序開發(fā)包結(jié)構(gòu)
1.與動態(tài)庫連接
linux默認(rèn)的就是與動態(tài)庫連接,下面這段程序testlib.c使用hello庫中的sayhello()函數(shù)
/*testlib.c*/
#include <hello.h>
#include <stdio.h>
int main()
{
sayhello();
return 0;
}
使用如下命令進(jìn)行編譯
$gcc -c testlib.c -o testlib.o
用如下命令連接:
$gcc testlib.o -lhello -o testlib
在連接時要注意,假設(shè)libhello.o 和libhello.a都在缺省的庫搜索路徑下/usr/lib下,如果在其它位置要加上-L參數(shù)
與與靜態(tài)庫連接麻煩一些,主要是參數(shù)問題。還是上面的例子:
$gcc testlib.o -o testlib -WI,-Bstatic -lhello
注:這個特別的"-WI,-Bstatic"參數(shù),實際上是傳給了連接器ld.
指示它與靜態(tài)庫連接,如果系統(tǒng)中只有靜態(tài)庫當(dāng)然就不需要這個參數(shù)了。
如果要和多個庫相連接,而每個庫的連接方式不一樣,比如上面的程序既要和libhello進(jìn)行靜態(tài)連接,又要和libbye進(jìn)行動態(tài)連接,其命令應(yīng)為:
$gcc testlib.o -o testlib -WI,-Bstatic -lhello -WI,-Bdynamic -lbye
3.動態(tài)庫的路徑問題
為了讓執(zhí)行程序順利找到動態(tài)庫,有三種方法:
(1)把庫拷貝到/usr/lib和/lib目錄下。
(2)在LD_LIBRARY_PATH環(huán)境變量中加上庫所在路徑。例如動態(tài)庫libhello.so在/home/ting/lib目錄下,以bash為例,使用命令:
$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib
(3) 修改/etc/ld.so.conf文件,把庫所在的路徑加到文件末尾,并執(zhí)行l(wèi)dconfig刷新。這樣,加入的目錄下的所有庫文件都可見、
4.查看庫中的符號
有時候可能需要查看一個庫中到底有哪些函數(shù),nm命令可以打印出庫中的涉及到的所有符號。庫既可以是靜態(tài)的也可以是動態(tài)的。nm列出的符號有很多,常見的有三種,一種是在庫中被調(diào)用,但并沒有在庫中定義(表明需要其他庫支持),用U表示;一種是庫中定義的函數(shù),用T表示,這是最常見的;另外一種是所謂的“弱態(tài)”符號,它們雖然在庫中被定義,但是可能被其他庫中的同名符號覆蓋,用W表示。例如,假設(shè)開發(fā)者希望知道上央提到的hello庫中是否定義了printf():
$nm libhello.so |grep printf
U printf
U表示符號printf被引用,但是并沒有在函數(shù)內(nèi)定義,由此可以推斷,要正常使用hello庫,必須有其它庫支持,再使用ldd命令查看hello依賴于哪些庫:
$ldd hello
libc.so.6=>/lib/libc.so.6(0x400la000)
/lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)
從上面的結(jié)果可以繼續(xù)查看printf最終在哪里被定義,有興趣可以go on
生成庫
第一步要把源代碼編繹成目標(biāo)代碼。以下面的代碼為例,生成上面用到的hello庫:
/* hello.c */
#include <stdio.h>
void sayhello()
{
printf("hello,world\n");
}
用gcc編繹該文件,在編繹時可以使用任何全法的編繹參數(shù),例如-g加入調(diào)試代碼等:
gcc -c hello.c -o hello.o
1.連接成靜態(tài)庫
連接成靜態(tài)庫使用ar命令,其實ar是archive的意思
$ar cqs libhello.a hello.o
2.連接成動態(tài)庫
生成動態(tài)庫用gcc來完成,由于可能存在多個版本,因此通常指定版本號:
$gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o
另外再建立兩個符號連接:
$ln -s libhello.so.1.0 libhello.so.1
$ln -s libhello.so.1 libhello.so
這樣一個libhello的動態(tài)連接庫就生成了。最重要的是傳gcc -shared 參數(shù)使其生成是動態(tài)庫而不是普通執(zhí)行程序。
-Wl 表示后面的參數(shù)也就是-soname,libhello.so.1直接傳給連接器ld進(jìn)行處理。實際上,每一個庫都有一個soname,當(dāng)連接器發(fā)現(xiàn)它正在查找的程序庫中有這樣一個名稱,連接器便會將soname嵌入連結(jié)中的二進(jìn)制文件內(nèi),而不是它正在運(yùn)行的實際文件名,在程序執(zhí)行期間,程序會查找擁有soname名字的文件,而不是庫的文件名,換句話說,soname是庫的區(qū)分標(biāo)志。
這樣做的目的主要是允許系統(tǒng)中多個版本的庫文件共存,習(xí)慣上在命名庫文件的時候通常與soname相同
libxxxx.so.major.minor
其中,xxxx是庫的名字,major是主版本號,minor 是次版本號
posted @
2006-07-25 00:33 Jerry Cat 閱讀(578) |
評論 (0) |
編輯 收藏
[轉(zhuǎn)]如何讓你的程序安全通過windows防火墻
http://m.shnenglu.com/davyy/archive/2006/07/24/10410.html
大家開發(fā)網(wǎng)絡(luò)程序,經(jīng)常要連接其他主機(jī),如果在xp上運(yùn)行,一定會提示你,只有選擇解除阻止才能
實現(xiàn)正常的網(wǎng)絡(luò)連接.那么有沒有辦法在防火墻的例外列表里面通過編程的方式加入自己的程序呢?
當(dāng)然有了,不然就不要介紹了:)
xp的系統(tǒng)目錄下面有個hnetcfg.dll就是這個編程接口,頭文件是netfw.h,初始化代碼如下:
INetFwProfile* m_pFireWallProfile=NULL;
??? HRESULT hr? =? S_FALSE;
??? INetFwMgr *? fwMgr? =? NULL;
??? INetFwPolicy *? fwPolicy? =? NULL;
??? FW_ERROR_CODE ret? =? FW_NOERROR;
???? try
?????? {
???????? if ( m_pFireWallProfile )
???????????? throw? FW_ERR_INITIALIZED;
???????? //? Create an instance of the firewall settings manager.
???????? hr? =? CoCreateInstance( __uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof( INetFwMgr), ( void ** ) & fwMgr );
???????? if ( FAILED( hr ))
???????????? throw? FW_ERR_CREATE_SETTING_MANAGER;
???????? //? Retrieve the local firewall policy.
???????? hr? =? fwMgr -> get_LocalPolicy(? & fwPolicy );
???????? if ( FAILED( hr ))
???????????? throw? FW_ERR_LOCAL_POLICY;
???????? //? Retrieve the firewall profile currently in effect
???????? hr? =? fwPolicy -> get_CurrentProfile(? & m_pFireWallProfile );
???????? if ( FAILED( hr ))
???????????? throw? FW_ERR_PROFILE;
??? }
???? catch ( FW_ERROR_CODE nError)
????? {
??????? ret? =? nError;
??? }
???? if ( fwPolicy )
??????? fwPolicy -> Release();
???? if ( fwMgr )
??????? fwMgr -> Release();
???? return? ret; 將程序名稱加入例外列表:
WinXPSP2FireWall::AddApplication( const wchar_t* lpszProcessImageFileName, const wchar_t* lpszRegisterName )
{
??? FW_ERROR_CODE ret = FW_NOERROR;
??? HRESULT hr;
??? BOOL bAppEnable;
??? BSTR bstrProcessImageFileName = NULL;
??? BSTR bstrRegisterName = NULL;
??? INetFwAuthorizedApplication* pFWApp = NULL;
??? INetFwAuthorizedApplications* pFWApps = NULL;
??? try
??? {
??????? if( m_pFireWallProfile == NULL )
??????????? throw FW_ERR_INITIALIZED;
??????? if( lpszProcessImageFileName == NULL || lpszRegisterName? == NULL )
??????????? throw FW_ERR_INVALID_ARG;
??????? // First of all, check the application is already authorized;
??????? FW_ERROR_CODE? nError = this->IsAppEnabled( lpszProcessImageFileName, bAppEnable );
??????? if( nError != FW_NOERROR )
??????????? throw nError;
??????? // Only add the application if it isn't authorized
??????? if( bAppEnable == FALSE )
??????? {
??????????? // Retrieve the authorized application collection
??????????? hr = m_pFireWallProfile->get_AuthorizedApplications( &pFWApps );
??????????? if( FAILED( hr ))
??????????????? throw FW_ERR_AUTH_APPLICATIONS;
??????????? // Create an instance of an authorized application
??????????? hr = CoCreateInstance( __uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&pFWApp);
??????????? if( FAILED( hr ))
??????????????? throw FW_ERR_CREATE_APP_INSTANCE;
??????????? // Allocate a BSTR for the Process Image FileName
??????????? bstrProcessImageFileName = SysAllocString( lpszProcessImageFileName );
??????????? if( SysStringLen( bstrProcessImageFileName ) == 0)
??????????????? throw FW_ERR_SYS_ALLOC_STRING;
??????????? // Set the process image file name
??????????? hr = pFWApp->put_ProcessImageFileName( bstrProcessImageFileName );
??????????? if( FAILED( hr ) )
??????????????? throw FW_ERR_PUT_PROCESS_IMAGE_NAME;
??????????? // Allocate a BSTR for register name
??????????? bstrRegisterName = SysAllocString( lpszRegisterName );
??????????? if( SysStringLen( bstrRegisterName ) == 0)
??????????????? throw FW_ERR_SYS_ALLOC_STRING;
??????????? // Set a registered name of the process
??????????? hr = pFWApp->put_Name( bstrRegisterName );
??????????? if( FAILED( hr ))
??????????????? throw FW_ERR_PUT_REGISTER_NAME;
??????????? // Add the application to the collection
??????????? hr = pFWApps->Add( pFWApp );
??????????? if( FAILED( hr ))
??????????????? throw FW_ERR_ADD_TO_COLLECTION;
??????? }
??? }
??? catch( FW_ERROR_CODE nError )
??? {
??????? ret = nError;
??? }
??? SysFreeString( bstrProcessImageFileName );
??? SysFreeString( bstrRegisterName );
??? if( pFWApp )
??????? pFWApp->Release();
??? if( pFWApps )
??????? pFWApps->Release();
??? return ret;
}
posted @
2006-07-25 00:22 Jerry Cat 閱讀(545) |
評論 (0) |
編輯 收藏
/********************************************\
|????歡迎轉(zhuǎn)載, 但請保留作者姓名和原文鏈接, 祝您進(jìn)步并共勉!???? |
\********************************************/
讀VC++內(nèi)幕之體悟 - 09
作者: Jerry Cat
時間: 2006/07/24
鏈接:
http://m.shnenglu.com/jerysun0818/archive/2006/07/24/10428.html
9.?? 所有的ActiveX控件屬性,包括設(shè)計時的屬性,在運(yùn)行時都是可以訪問的。
posted @
2006-07-24 23:58 Jerry Cat 閱讀(634) |
評論 (0) |
編輯 收藏