程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> C++/VC++編程疑難問題及解答(二)

C++/VC++編程疑難問題及解答(二)

編輯:vc教程

標准程序庫問題,再談list的迭代器是否可以隨機移動?

上篇文章中的"list的迭代器是否可以隨機移動?"問題的回顧:

由於list的內部實現是雙向鏈表,鏈表就要求迭代器(指針)只能依次從前向後(或從後向前)移動,依次移動一個位置,因此list只定義了++和--操作符,而沒有定義+、-、+=和-=等操作符。所以要想list的迭代器移動一段距離,就需要自己編程實現,用一個小循環就行了,代碼如下:

#include <list>
using namespace std;
list<int> myList;
…  // myList的初始化及其他操作
list<int>::const_iterator itList = myList.begin();
// itList向前移動len個距離
for ( int i= 0; i < len; i++ )
{
  ++itList;
}
...  // 其他操作
  上面對STL中的list的指針隨機移動問題的解釋不是很好,感謝周星星的提醒,我們可以用STL的advance操作,我給出的代碼的是advance針對list的一個可能的實現方法。這裡我建議使用advance操作代替我的那段代碼。

advance操作是STL針對所有容器類型的一個通用的迭代器移動操作,它能根據容器類型的不同自動選擇適合的移動方法,對於隨機存取容器(如vector和deque),迭代器可以直接移動到所需要的位置,對於非隨機存取的容器(如list,map等),迭代器就需要慢慢往後移動,直到移到需要的位置。但是不同的STL實現版本對advance的實現可能是不同的。我們沒有必要了解它到底是怎麼實現的,會用即可。

標准程序庫問題,vector的resize()和reserve()函數的區別

首先這兩個函數有本質的區別。reserve是容器預留空間,但並不真正創建元素對象,在創建對象之前,不能引用容器內的元素,因此當加入新的元素時,需要用push_back()/insert()函數。
  resize是改變容器的大小,並且創建對象,因此,調用這個函數之後,就可以引用容器內的對象了,因此當加入新的元素時,用operator[]操作符,或者用迭代器來引用元素對象。

再者,兩個函數的形式是有區別的,reserve函數之後一個參數,即需要預留的容器的空間;resize函數可以有兩個參數,第一個參數是容器新的大小,第二個參數是要加入容器中的新元素,如果這個參數被省略,那麼就調用元素對象的默認構造函數。下面是這兩個函數使用例子:
vector<int> myVec;
myVec.reserve( 100 );      //新元素還沒有構造
for (int i = 0; i < 100; i++ )
{
  myVec.push_back( i );     //新元素這時才構造
}
myVec.resize( 102 );      // 用元素的默認構造函數構造了兩個新的元素
myVec[100] = 1;        //直接操作新元素
myVec[101] = 2;
…    
標准程序庫問題,vector的內存重分配問題

在使用vector時,一定要注意vector是動態分配內存的。雖然使用vector很方便,但是如果不注意相關的問題,後果是很糟糕的。例如下面的程序:
struct ForwardProb
{
  string m_SS;
  string m_dictItem;
  int m_index;
  float m_forwardProb;
  ForwardProb *preFP;
};
vector<ForwardProb> myVec;
for ( int i = 0; i < count; i++ )
{
  ForwardProb thisFP;
  …
  thisFP->preFP = some previous pointer in myVec;
  myVec.push_back( thisFP );
}
…      
  在這段代碼中,由於每次thisFP都是新加入myVec中的,這樣可能需要重新分配內存,即myVec在內存中的位置就可能發生了變化,那麼每個元素中的指針preFP就可能變得無效了。
  解決的方法:在使用myVec之前,先用reserve函數為vector預留出足夠的空間,或者將指preFP針改成下標。

動態鏈接庫與靜態鏈接庫

動態鏈接庫和靜態鏈接庫的建立是很相似的,在Visual C++.net用建立項目時,在應用程序設置中選擇DLL或這靜態庫,就可以建立一個空的動態鏈接庫/靜態鏈接庫的空項目。

靜態鏈接庫的建立和使用

假設建立一個staticLinkLib的靜態鏈接庫。在靜態鏈接庫的接口函數的定義形式如下:

extern "C" return_type interfaceFunctionName( parameter… );

注意這裡加上extern "C"是必要的。另外在動態鏈接庫對應的頭文件裡也要像這樣聲明,編譯之後將動態鏈接庫(.lib文件)和對應的頭文件提交給用戶使用即可。假設這兩個文件是staticLinkLib.lib和staticLinkLib.h。

用戶在使用動態鏈接庫時,要包含上面的頭文件staticLinkLib.h,並且在項目屬性中的"鏈接器/輸入"選擇靜態鏈接庫文件staticLinkLib.lib。如下圖:

圖一 VC.net下的項目屬性設置

這樣,用戶在自己的應用程序就可以調用靜態鏈接庫中定義的接口函數了。

動態鏈接庫的建立和使用

動態鏈接庫的建立/使用和靜態鏈接庫基本相同,不同的地方在於接口函數的聲明形式。動態鏈接庫的接口函數聲明形式如下:
extern "C" __declspec(dllexport) return_type interfaceFunctionName( parameter… );

另外,動態鏈接庫編譯之後生成一個動態鏈接庫文件(dll)和一個.lib文件。提交時需要提交這兩個文件和對應的接口的頭文件。

用戶使用動態鏈接庫時,也需要在項目屬性中的"鏈接器/輸入"選擇對應的.lib文件,那麼程序會自動調用.dll文件的。用戶不需要包含上面提交的頭文件,用戶只需要按照規定的形式聲明接口函數即可,形式如下:

extern "C" __declspec(dllimport) return_type interfaceFunctionName( parameter… );
  這樣,用戶就可以在自己的應用程序中調用動態鏈接庫中定義的接口函數了。

動態鏈接庫中不能有屏幕輸出語句,如cout << …等,因此調試時不太方便,這裡沒有研究動態鏈接庫的調試方法。

靜態鏈接庫和動態鏈接庫的一個很大的區別還有,在靜態鏈接庫中不能在包含其他的動態鏈接庫或者靜態庫;而在動態鏈接庫中還可以再包含其他的動態/靜態鏈接庫。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved