青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

可視化graph的工具: GraphViz

這套工具可以把有向圖(digraph)和無向圖(graph)在平面內展現出來,方便觀察。GraphViz使用DOT(一種圖形描述語言)描述圖,然后有解釋工具dot生成圖像文件。dot支持多種圖像文件,包括非矢量的gif、矢量的ps、svg等約20多種格式。DOT語言也非常簡單易學。舉個例子:

digraph G {
?size = "4,4"
?main [shape=box];?/* this is a comment */
?main -> parse [weight=8];
?parse -> execute;
?main -> init [style=dotted];
?main -> cleanup;
?execute -> { make_string; printf}
?init -> make_string;
?edge [color=red]; // so is this
?main -> printf [style=bold, label="100 times"];
?node [shape=box, style=filled, color=".7.3 1.0"];
?execute -> compare;
}

存為test.dot,然后執行
? > dot test.dot -Tpng -o test.png
就生成了graph的圖像文件。很方便哦。
?
實際上GraphViz還可以畫出很多漂亮的“圖”。比如ER圖,hash table示意圖。更多請參考:http://www.graphviz.org/Gallery.php。以后可以考慮用GraphViz畫一些示意圖,既方便有專業,還很容易轉成ps(eps)。繼續研究研究。
?
posted @ 2007-03-06 21:35 dyh 閱讀(2006) | 評論 (0)編輯 收藏
 
[轉載]Visual C++ MFC 中常用宏的含義

文章來源: http://www.vccode.com/
文章作者:不詳

AND_CATCHAND_CATCH
AND_CATCH(exception_class,exception _object_point_name)
說明:

定義一個代碼塊,它用于獲取廢除當前TRY塊中的附加異常類型。使用CATCH宏以獲得一個異常類型,然后使用AND_CATCH宏獲得隨后的異常處理代碼可以訪問異常對象(若合適的話)已得到關于異常的特別原因的更多消息。在AND_CATCH塊中調用THROW_LAST宏以便把處理過程移到下個外部異常框架。AND_CATCH可標記CATCH或AND_CATCH塊的末尾。

注釋:
AND_CATCH塊被定義成為一個C++作用域(由花括號來描述)。若用戶在此作用域定義變量,那么記住他們只在此作用域中可以訪問。他也用于exception_object_pointer_name變量。
?
ASSERT
ASSERT(booleanExpression)
說明:
計算變量的值。如果結構的值為0,那么此宏便打印一個診斷消息并且成訊運行失敗。如果條件為非0,那么什么也不做。 診斷消息的形式為: assertion failed in file in line 其中name是元文件名,num是源文件中運行失敗的中斷號。 在Release版中,ASSERT不計算表達式的值也就不中斷程序。如果必須計算此表達式的值且不管環境如何那么用VERIFY代替ASSERT。
注釋:
ASSERT只能在Debug版中用

ASSERT_VAILD
ASSERT_VAILD(pObject)
說明:
用于檢測關于對象的內部狀態的有效性。ASSERT_VALID調用此對象的AssertValid成員函數(把它們作為自己的變量來傳遞)。在Release版中ASSERT_VALID什么也不做。在DEBUG版中,他檢查指針,以不同于NULL的方式進行檢查,并調用對象自己的AssertValid成員函數。如果這些檢測中有任何一個失敗的話,那么他會以與ASSERT相同的方法顯示一個警告的消息。
注釋:
此函數只在DEBUG版中有效。

BEGIN_MESSAGE_MAP
BEGIN_MESSAGE_MAP(the class,baseclass)
說明:
使用BEGIN_MESSAGE_MAP開始用戶消息映射的定義。在定義用戶類函數的工具(.cpp)文件中,以BEGIN_MESSAGE_MAP宏開始消息映射,然后為每個消息處理函數增加宏項,接著以END_MESSAGE_MAP宏完成消息映射。

CATCH
CATCH(exception_class,exception_object_pointer_name)
說明:
使用此用定義一個代碼塊,此代碼用來獲取當前TRY塊中都一個異常類型。異常處理代碼可以訪問異常對象,如何合適的話,就會得到關于異常的特殊原因的更多消息。調用THROW_LAST宏以把處理過程一下一個外部異常框架,如果exception-class是類CExceptioon,那么會獲取所有異常類型。用戶可以使用CObject::IsKindOf成員函數以確定那個特別異常被排除。一種獲取異常的最好方式是使用順序的AND_CATCH語句,每個帶一個不同的異常類型。此異常類型的指針由宏定義,用戶不必定義。
注釋:
此CATCH塊被定義作一個C++范圍(由花括號描述)。如用戶在此范圍定義變量,那么它們只在吃范圍內可以訪問。他還可以用于異常對象的指針名。

DEBUG_NEW
#define new DEBUG_NEW
說明:
幫助查找內存錯誤。用戶在程序中使用DEBUG_NEW,用戶通常使用new運算符來從堆上分配。在Debug模式下(但定義了一個DEBUG符號),DEBUG_NEW為它分配的每個對象記錄文件名和行號。然后,在用戶使用CMemoryState::DumpAllObjectSince成員函數時,每個以DEBUG_NEW分配的對象分配的地方顯示出文件名和行號。 為了使用DEBUG_NEW,應在用戶的資源文件中插入以下指令: #define new DEBUG_NEW 一旦用戶插入本指令,預處理程序將在使用new的地方插入DEBUG_NEW,而MFC作其余的工作。但用戶編譯自己的程序的一個發行版時,DEBUG_NEW便進行簡單的new操作,而且不產生文件名和行號消息。

DECLARE_DYNAMIC
DECLARE_DYNAMIC(class_name)
說明:
但從CObject派生一個類時,此宏增加關于一個對象類的訪問運行時間功能。把DECLARE_DYNAMIC宏加入類的頭文件中,然后在全部需要訪問詞類對象的.CPP文件中都包含此模塊。如果像所描述那樣使用DELCARE_DYNAMIC和IMPLEMENT_DYNAMIC宏,那么用戶便可使用RUNTIME_CLASS宏和CObject::IsKindOf函數以在運行時間決定對象類。如果DECLARE_DYNAMIC包含在類定義中,那么IMPLEMETN_DYNAMIC必須包含在類工具中。

DECLARE_DYNCREATE
DECLARE_DYNCREATE(class_name)
說明:
使用DECLARE_DYNCRETE宏以便允許CObject派生類的對象在運行時刻自動建立。主機使用此功能自動建立新對象,例如,但它在串行化過程中從磁盤讀一個對象時,文件及視圖和框架窗應該支持動態建立,因為框架需要自動建立它。把DECLARE_DYNCREATE宏加入類的.H文件中,然后在全部需要訪問此類對象的.CPP文件中包含這一模式。如果DECLARE_DYNCREATE包含在類定義中,那么IMPLEMENT_DYNCREATE必須包含在類工具中。

DECLARE_MESSAGE_MAP
DECLARE_MESSAGE_MAP()
說明:
用戶程序中的每個CCmdTarget派生類必須提供消息映射以處理消息。在類定義的末尾使用DECLARE_MESSAGE_MAP宏。接著,在定義類成員函數的.CPP文件中,使用BEGIN_MESSAGE_MAP宏,每個用戶消息處理函數的宏項下面的列表以及END_MESSAGE_MAP宏。
注釋:
如果在DECLARE_MESSAGE_MAP之后定義任何一個成員,那么必須為他們指定一個新存取類型(公共的,私有的,保護的)。

DECLARE_SERIAL
DECLARE_SERIAL(class_name)
說明:
DECLARE_SERIAL為一個可以串行化的CObject派生類產生必要的C++標題代碼。串行化是把某個對象的內容從一個文件讀出和寫入一文件。在.H文件中使用DECLARE_SERIAL宏,接著在需要訪問此類對象的全部.CPP文件中包含此文件。如果DECLARE_SERIAL包含在類定義中,那么IMPLEMENT_SERIAL必須包含在類工具中。DECLARE_SERIAL宏包含全部DECLARE_DYNAMIC,IMPLEMENT_DYCREATE的功能。

END_CATCH
END_CATCH
說明:
標識最后的CATCH或AND_CATCH塊的末尾。

END_MESSAGE_MAP
END_MESSAGE_MAP
說明:
使用END_MESSAGE_MAP宏結束用戶的消息映射定義
IMPLEMENT_DYNAMIC
IMPLEMENT_DYNAMIC(class_name,base_class_name)
說明:
通過運行時在串行結構中為動態CObject派生類訪問類名和位置來產生必要的C++代碼。在.CPP文件中使用IMPLEMENT_DYNAMIC宏,接著一次鏈接結果對象代碼

IMPLEMENT_DYNCREATE
IMPLEMENT_DYNCREATE(class_name,base_class_name)
說明:
通過DECLARE_DYNCREATE宏來使用IMPLEMENT_DYNCREATE宏,以允許CObject派生類對象在運行時自動建立。主機使用此功能自動建立對象,例如,但它在串行化過程中從磁盤讀去一個對象時,他在類工具里加入IMPLEMENT_DYNCREATE宏。若用戶使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏,那么接著使用RUNTIME_CLASS宏和CObject::IsKindOf成員函數以在運行時確定對象類。若declare_dyncreate包含在定義中,那么IMPLEMENT_DYNCREATE必須包含在類工具中。

IMPLEMENT_SERIAL
IMPLEMENT_SERIAL(class_name,base_class_name,wSchema)
說明:
通過運行時在串行結構中動態CObject派生類訪問類名和位置來建立必要的C++代碼。在.CPP文件中使用IMPLEMENT_SERIAL宏,然后一次鏈接結果對象代碼。

ON_COMMAND
ON_COMMAND(id,memberFxn)
說明:
此宏通過ClassWizard或手工插入一個消息映射。它表明那個函數將從一個命令用戶接口(例如一個菜單項或toolbar按鈕)處理一個命令消息。當一個命令對象通過指定的ID接受到一個Windows WM_COMMAND消息時,ON_COMMAND將調用成員函數memberFxn處理此消息。在用戶的消息映射中,對于每個菜單或加速器命令(必須被映射到一個消息處理函數)應該確實有一個ON_COMMAND宏語句。

ON_CONTROL
ON_CONTROL(wNotifyCode,id,memberFxn)
說明:
表明哪個函數將處理一個常規控制表示消息。控制標識消息是那些從一個控制夫發送到母窗口的消息。

ON_MESSAGE
ON_MESSAGE(message,memberFxn)
說明:
指明哪個函數將處理一用戶定義消息。用戶定義消息通常定義在WM_USER到0x7FF范圍內。用戶定義消息是那些不是標準Windows WM_MESSAGE消息的任何消息。在用戶的消息映射中,每個必須被映射到一個消息處理函數。用戶定義消息應該有一個ON_MESSAGE宏語句。

ON_REGISTERED_MESSAGE
ON_REGISTERED_MESSAGE(nmessageVarible,memberFxn)
說明:
Windows的RegisterWindowsMesage函數用于定義一個新窗口消息,此消息保證在整個系統中是唯一的。此宏表明哪個函數處理已注冊消息。變量nMessageViable應以NEAR修飾符來定義。

ON_UPDATE_COMMAND_UI
ON_UPDATE_COMMAND_UI(id,memberFxn)
說明:
此宏通常通過ClassWizard被插入一個消息映射,以指明哪個函數將處理一個用戶接口個更改命令消息。在用戶的消息映射中,每個用戶接口更改命令(比訊被映射到一個消息處理函數)應該有一個ON_UPDATE_COMMAND_UI宏語句。

ON_VBXEVENT
ON_VBXEVENT(wNotifyCode,memberFxn)
說明:
此宏通常通過ClassWizard被插入一個消息映射,以指明哪個函數將處理一個來自VBX控制的消息。在用戶的消息映射中每個被映射到一消息處理函數的VBX控制消息應該有一個宏語句。

RUNTIME_CLASS
RUNTIME_CLASS(class_name)
說明:
使用此宏從c++類民眾獲取運行時類結構。RUNTIME_CLASS為由class_name指定的類返回一個指針到CRuntimeClass結構。只有以DECLARE_DYNAMIC,DECLARE_DYNCREATE或DECLARE_SERIAL定義的CObject派生類才返回到一個CRuntimeClass結構的指針。

THROW
THROW(exception_object_pointer)
說明:
派出指定的異常。THROW中斷程序的運行,把控制傳遞給用戶程序中的相關的CATCH塊。如果用戶沒有提供CATCH塊,那么控制被傳遞到一個MFC模塊,他打印出一個錯誤并終止運行。

THROW_LAST
THROW_LAST()
說明:
此宏允許用戶派出一個局部建立的異常。如果用戶試圖排除一個剛發現的異常,那么一般此異常將溢出并被刪除。使用THROW_LAST,此異常被直接傳送到下一個CATCH處理程序。

TRACE
TRACE(exp)
說明:
把一個格式化字符串送到轉儲設備,例如,文件或調試監視器,而提供與printf相似的功能。同MS_DOS下C程序的printf一樣,TRACE宏是一個在程序運行時跟蹤變量值的方便形式。在DEBUG環境中,TRACE宏輸出到afxDump。在Release版中他不做任何工作。
注釋:
此宏只在MFC的DEBUG版中有效。

TRACE0
TRACE0(exp)
說明:
與TRACE相似,但他把跟蹤字符串放在代碼段中,而不是DGROUP,因此使用少的DGROUP空間。TRACE0是一組跟蹤宏的一個變體,這些宏可用于調試輸出。這一組包括TRACE0,TRACE1,TRACE2和TRACE3,這些宏不同在于所取參數的數目不同。TRACE0只取一個格式化字符串并可用于簡單文本消息。TRACE1取一格式化字符串加上一個變量——一個將轉儲的變量。同樣,TRACE2,TRACE3分別取2個或3個參數(在格式化字符串之后)。如果用戶以便以了應用程序的發行版,那么它只把數據轉儲到afxDump。
注釋:
此宏只在MFC的DEBUG中有效。


TRACE1
TRACE1(exp,param1)
說明:
參見TRACE0

TRACE2
TRACE2(exp,param1,param2)
說明:
參見TRACE0

TRACE3
TRACE3(exp,param1,param2,param3)
說明:

TRY
TRY
說明:
使用此宏建立一TRY塊。一個TRY識別一個可排除異常的代碼塊。這些異常在隨后的CATCH和AND_CATCH塊處理。傳遞是允許的:異常可以傳遞一個外部TRY塊,或者忽略它們或者使用THROW_LAST宏。

VERIFY
VERIFY(booleanExpression)
說明:
在MFC的DEBUG版中,VERIFY宏計算它的變量值。 如果結果為0,那么宏打印一個診斷消息并中止程序。如果條件不為0,那么什么工作也不作。 診斷有如下形式: assertion failed in file in line 其中name是源文件的名字,num是在源文件中失敗的中止行號。在MFC的Release版中,VERIFY計算表達式值但不打印或中止程序。例如:如果表達式是個函數調用,那么調用成功。
posted @ 2007-01-15 19:20 dyh 閱讀(383) | 評論 (0)編輯 收藏
 
     摘要: [轉載]VC中的一些常用方法(20條) 文章來源: www.csdn.net 作者blog: http://blog.csdn.net/oury/ VC中的一些常用方法 //一、打開CD-ROMmciSendStrin...  閱讀全文
posted @ 2007-01-15 19:14 dyh 閱讀(424) | 評論 (0)編輯 收藏
 
寫這個文章完全是因為想要搞清楚 vc 怎么布局每個 c++ 對象,以及怎樣完成指針的轉換的過程.
  先問一個問題,兩個不同類型的指針相互轉換以后,他們在數值上是一樣的嗎?比如:

    int nValue = 10;
    int *pInt = &nValue;
    void *pVoid = pInt;
    char *pChar = (char*)pInt;


  這些指針的值(不是說指針指向的內存的內容)是一樣的嗎? 如果你的回答是 yes,那如果是一個類的繼承體系呢?在繼承類向基類轉換的過程中,指針的數值還是不變化的么?如果你的回答是"不一定會變化,要看類的體系是怎么設計的"的話,那恭喜你,不用看下去了.如果你還不確定究竟變還是不變,究竟哪些變,哪些不變,究竟為什么要變為什么不變的話,接著看下來.

  c++ 標準不規定 c++ 實現的時候的對象的具體的內存布局,除了在某些方面有小的限制以外,c++ 對象在內存里面的布局完全是由編譯器自行決定,這里我也只是討論 vc++ .net 2003 build 7.1.3091 的實現方式,我并沒有在 vc5 vc6 vc.net 2002 以及其他的 2003 build 上面做過測試,結論也許不適合那些編譯平臺.這些屬于編譯器具體實現,ms 保留有在不通知你我的情況下作出更改的權利.廢話這么多,馬上開始.

  對于 c 的內建指針的轉換,結果是不用多討論的,我們只是討論 c++ 的對象.從最簡單的開始.

    class CBase
    {
    public:
      int m_nBaseValue;
    };


  這樣的一個類在內存里放置是非常簡單的,他占有4個 bytes 的空間,不用多說,我們從他派生一個類出來.

    class CDerive1 : public CBase
    {
    public:
      int m_nDerive1Value;
    };


  CDerive1 的對象在內存里面是怎么放的呢? 也很簡單,占有8個 bytes 的空間,前4個 bytes 屬于 CBase 類,后四個 bytes 屬于自己.一個CDerive1 的指針轉換成一個 CBase 的指針,結果是一樣的.下面我們加上多重繼承看看.

    class CFinal : public CDerive,public CBase // 這里的 CDerive 是一個和 CBase 差不多的基類
    {
    public:
      int m_nFinalValue;
    };


  CFinal 的對象在內存里面的布局稍微復雜一點,但是也很容易想象,他占有 12 個 bytes 的空間,前4個屬于 CDerive,中間4個屬于 CBase,后面4個才是自己的.那一個 CFinal 的指針轉換成一個 CDerive 指針,數值會變么? 轉換成一個 CBase 指針呢?又會變化么?答案是,前一個不變,后一個要變化,道理非常的明顯,CFinal 對象的開頭剛好是一個 CDerive 對象,而 CBase 對象卻在 CFinal 對象的中間,自然是要變化的了,具體怎么變化呢? 加 4 就 ok(自然要檢查是否是空指針).

    CBase *pBase = pFinal ? (CBase*)((char*)pFinal + sizeof(CDerive)) : 0;// 當你寫下 pBase = pFinal 的時候,其實是這樣的

  這種不帶 virtual 的繼承就這么簡單,只是加上一個 offset 而已.下面我們看看如果加上 virtual function 的時候是什么樣子的呢?
還是從簡單類開始.

    class CBase
    {
    public:
      virtual void VirtualBaseFunction(){}
      int m_nBaseValue;
    };


  這里刻意沒有使用 virtual destructor,因為這個函數稍微有些不同.還是同樣的問題,CBase 類在內存上占多大的空間?還是 4 bytes 么? 答案是 no, 在我的編譯器上面是 8 bytes,多出來的 4 bytes 是 __vfptr(watch 窗口看見的名字),他是一個指針,指向了類的 vtable,那什么是 vtable 呢,他是用來干什么的呢? vtable 是用來支援 virtual function 機制的,他其實是一個函數指針數組(并不等同于c/c++語言里面的指針數組,因為他們的類型并不一定是一樣的.)他的每一個元素都指向了一個你定義的 virtual function,這樣通過一個中間層來到達動態連編的效果,這些指針是在程序運行的時候準備妥當的,而不是在編譯的時候準備妥當的,這個就是動態聯編的目的,具體是由誰來設置這些指針的呢?constructor/destructor/copy constructor/assignment operator他們完成的,不用奇怪,編譯器會在你寫的這些函數里面安插些必要的代碼用來設置 vtable 的值,如果你沒有寫這些函數,編譯器會在適當的時候幫你生成這些函數.明白一點, vtable 是用來支持 virtual function 機制的,而需要 virtual 機制的類基本上都會由一個 __vfptr 指向他自己的 vtable.在調用 virtual function的時候,編譯器這樣完成:

   pBase->VirtualBaseFunction(); => pBase->__vfptr[0]();// 0 是你的virtual function 在 vtable 中的 slot number,編譯器決定

  現在應該很想象 CBase 的大小了吧,那這個 __vfptr 是放到什么位置的呢? 在 m_nBaseValue 之前還是之后呢? 在我的編譯器上看來,是在之前,為什么要放到之前,是因為在通過 指向類成員函數的指針調用 virtual function 的時候能少些代碼(指匯編代碼),這個原因這里就不深入討論了,有興趣的同學可以看看 inside the c++ object model 一書.
  接下來,我們加上繼承來看看.

    class CDerive1 : public CBase
    {
    public:
      virtual void VirtualDerive1Function();
    };


  這個時候你也許要說,內存布局跟沒有 virtual 是一樣的,只不過每個類多了一個 __vfptr 而已,呃...這個是不對的,在我的編譯器上面 兩個類共享同一個 __vfptr, vtable 里面放有兩個指針,一個是兩個類共享的,一個只屬于 CDerive1 類,調用的時候如何呢?

   pDerive1->VirtualDerive1Function() => pDerive1->__vfptr[1]();
   pDerive1->VirtualBaseFunction() => pDerive1->__vfptr[0]();


  至于指針的相互轉換,數值還是沒有變化的(也正是追求這種效果,所以把 __vfptr 放到類的開頭,因為調整 this 指針也是要占有運行時的時間的).

  現在加上多重繼承瞧瞧,代碼我不寫上來了,就跟上面的 CFinal, CDerive, CBase 體系一樣,只是每個類多一個VirtualxxxFunction出來,這個時候的指針調整還是沒有什么變化,所以我們只是看看 vtable 的情況,你會說 CDerive 和 CFinal 共享一個 __vfptr,而 CBase 有一個自己的 __vfptr,而 CFinal 的 __vfptr 有 2 個slot,這個結論是正確的. 同時你也會說 通過 CFinal 類調用 CBase 的函數是要進行指針調整的,yes you'r right,不僅僅是 this 指針調整(呃,this 指針會成為 function 的一個參數),還要調整 vtable 的值:

   pFinal->VirtualBaseFunction() => (CBase*)((char*)pFinal + sizeof(CDerive))->__vfptr[0]();

   轉換成 asm 的代碼大約是這樣的:

   mov eax,[pFinal] ; pFinal is a local object,pFinal will be epb - xx
   add eax,8 ; 8 = sizeof(CDerive)
   mov ecx,eax ; ecx is this pointer
   mov edx,[eax] ; edx = vtable address
   call [edx] ; call vtable[0]


  寫到這里也就明白this指針是怎么調整的.帶 virtual function 的繼承也不復雜,this指針調整也是很簡單的,下面看最復雜的部分 virtual inheritance.

  我的編譯器支持虛擬繼承的方式和虛函數的方式差不多,都是通過一個 table 完成,只是這個就看不到 vc 賦予的名字了,我們叫他 vbtable 吧,編譯器同樣在類里面加入一個指向 vbtable 的指針,我們叫他 __vbptr 吧,這個指針指向了 vbtable ,而 vbtable 里面的每一項對應了一個基類,vbtable 記錄了每個基類的某一個偏移量,通過這個偏移量就能計算出具體類的指針的位置.看個簡單的例子:

   class CBase
   {
   public:
     virtual ~CBase(){}
   };

   class CMid1 : public virtual CBase
   {
   public:
     virtual ~CMid1(){}
     int m_nMid1;
   };

   class CMid2 : public virtual CBase
   {
   public:
     virtual ~CMid2(){}
     int m_nMid2;
   };

   class CFinal : public CMid1,public CMid2
   {
   public:
     virtual ~CFinal(){}
     int m_nFinal;
   };

   CFinal final;
   CFinal *pFinal = &final;??? // pFinal = 0x0012feb4;
   CBase *pBase = pFinal; // pBase = 0x0012fec8 = pFinal + 0x14;
   CMid1 *pMid1 = pFinal; // pMid1 = 0x0012feb4 = pFinal;
   CMid2 *pMid2 = pFinal; // pMid2 = 0x004210b4 = pFinal;


  結果讓你吃驚嗎? 最奇怪的地方居然是 CMid2 和 CMid1 的地址居然是一樣的,這個是因為 vc 把 vbtable 放到了 CFinal 類的開頭的原因,而CMid1 和 CMid2 也同樣要使用這個 vbtable, 所以 這個三個的地址也就必須相同了.那 CBase 的地址是怎么出來的呢? 呃...剛剛我們說了 vbtable 放到了CFinal 的開頭(vc 一定會放在開頭嗎?答案是不一定,這個稍后解釋).在我的機器上面 final 對應內存的第一個 dword 是 0x00426030,查看這個地址,第一個dword 是 0 ,第二個就是 0x14,剛好和 pBase 的偏移相同,這個只是巧合,也許你換個類的繼承體系就完全不同了,但是我只是想說明一點,基類的偏移計算是和 vbtable 的值相關聯的.下面我們就來看看 vc 是怎么計算這些偏移的.
  vc 在分析我們的代碼的時候,生成了一份類的繼承體系信息,其中有一個叫 thisDisplacement 的_PMD結構:

    struct _PMD // total undocumented
    {
      int mdisp; // i think the meaning is Multiinheritance DISPlacement
      int pdisp; // Pointer to vbtable DISPlacement
      int vdisp; // Vbtable DISPlacement
    };


  結構的名字和成員變量的名字確確實實是 vc 的名字(在 watch 窗口輸入 (_PMD*)0 就能看到這個結構的詳細信息),每個字段的含義卻是我自己猜測出來的.mdisp 大概用來表示多重繼承(包括單一繼承)的時候的偏移量,pdisp 表示 vbtable 的偏移量,而 vdisp 表示類在 vbtable 里面的下標.那么有了這個結構怎樣才能完成指針的轉換呢?假如我們有一個派生類指針 pFinal,要轉換成一個特定的基礎類,我們首先要知道和這個基類對應的 _PMD 結構的信息(這個信息的獲取,我暫時沒有找到一個非常方便的方法,現在我使用的方法下面會有描述),有了這個信息以后,轉換就方便了.首先找到 vbtabel 的地址 *(pFinal + pdisp),然后找到基類的偏移 *(*(pFinal + pdisp) + vdisp) 這個偏移值是相對vbtable的,所以還要加上 vbtable的偏移,最后加上 mdisp的偏移,如下:

  char *pFinal = xxx; // need a init value
  char *pBase; // we must calc
  pBase = pFinal + mdisp + *(int *)(*(int *)(pFinal + pdisp) + vdisp) + pdisp;


  注意: 當 pdisp < 0 的時候就表示這個類沒有 vbtable 直接使用 pFinal + mdisp 就得到結果了.
  所以這個結構是一個通用的結構,專門用作類型轉換,不管是有無虛繼承都能使用這個結構進行類型轉換.

  通過這個結構,我們也能看到 vc 是怎樣布局這個 object 的.

  看到這里,也許你要大呼一口氣,媽媽呀,一個類型轉換要這么的麻煩嗎?我直接寫 pBase = pFinal 不就可以了嗎? 恭喜你還沒有被我忽悠得暈頭轉向,哈哈.其實你寫下那行語句的時候,編譯器在幫你做這個轉換,大約生成下面的代碼

    mov eax,[pFinal] ;final address
    mov ecx,[eax] ; vbtable address *(int *)(pFinal + pdisp)
    mov edx,eax ; save to edx
    add edx,[ecx + 4] ; ecx + 4 is (*(int *)(pFinal + pdisp) + vdisp)
    mov [pBase],edx ; edx = pFinal + mdisp + *(int *)(*(int *)(pFinal + pdisp) + vdisp) + pdisp;
    ; here mdisp = 0, pdisp = 0, vdisp = 4


  也許你要說了,我要這些東西來干什么?要轉換的時候直接轉換就好了,編譯器會幫做,的確,大多數的時候確實是這樣,但是,在某些時候卻并不如此,現在你要實現一個功能,輸入一個指針,輸入一個 _PMD 結構,你要實現一個AdjustPointer 的函數來生成另一個指針.這個時候你也只能這樣完成了,因為我沒有給你兩個指針的名字,就算給了你字符串形式的名字也沒有用,呃....你也許會說,辦法是有的,的確是有,模板就能實現這種功能,呵..這個我們暫時不討論具體的實現細節.也許你要問了,究竟什么時候會去實現這種聽都沒有聽過的功能,其實這個函數是真正存在的,只不過不是由你來實現的,而是 ms 的人實現的,你只用寫一個 帶有 c++ 異常的程序,使用 ida 反匯編,然后查找函數,就能找到這個函數了,他用來在異常處理時創建 catch 所需要的 object.至于這個詳細的信息,請期待.我會最快速度寫出關于 vc 是怎樣實現 c++ 異常的文章來.

  最后了,說說那個 _PMD 結構的獲取方式.看的時候不要吃驚,方法比較的麻煩,比如我想知道和 CFinal 類相關的 _PMD 信息,先新建工作,寫下 throw pFinal 這樣的語句,編譯,在這個語句的地方設置斷點,運行,轉到反匯編,進入 __CxxThrowException@8 函數,這個時候不出意外你能看到一個叫 pThrowInfo 的東西(如果看不到,請打開"顯示符號名"選項),在 watch 窗口里面輸入pThrowInfo,展開他,看到一個pCatchableTypeArray,記錄下他的 nCacthableTypes的值,然后在 watch 里面輸入
pThrowInfo->pCatchableTypeArray->arrayOfCatchableTypes[0] 到 pThrowInfo->pCatchableTypeArray->arrayOfCatchableTypes[n], n 就是你剛剛記錄的值減1,再展開他們,你就能看到一個 thisDisplacement 的數據,繼續展開就是 mdisp 等等了,很是麻煩吧.哈..你已經猜到了,這個是和異常有關系的.

  后記: 這段時間,我一直在讀些反匯編之后的代碼,也頗有些心得,所以才有想法寫一些文章,探討 vc 編譯器鮮為人知(太過狂妄了)的秘密,這個方面的文章也有人寫過,那些文章也給我不少的啟發,我不認為自己是第一個發現這些秘密的人,但是至少我自己知道的,我是第一個把這些東西寫出來的人.文章里面作墨多的部分都是自己發現的.就這個文章里面的內容來說,inside the c++ object model 是有比較詳細的描寫,但是他并不是轉換針對 vc 這個編譯器的實現,而 _PMD 這個結構我也沒有在什么地方見有人描述過,只是在 windows develop network 的2002年12月的雜志上看有人提到過這個結構,可惜他卻沒有了解(至少他在他發表文章的時候是如是說的)這個結構的用處(正是因為這個原因,我才有寫這個文章以及后續文章的沖動).所以,這個文章也算是我自己的原創吧.這個文件雖然和游戲制造沒有太大的關系,但是小 T 自視清高,不愿意自己的文章被一幫不懂的人評價來評價去的,所以也沒有發到那些著名的 xxx 網站,只發 goldpoint.轉載請注明出處(小 T 對自己的第一個原創文章比較珍惜,比較重視,謝謝).
posted @ 2007-01-08 21:17 dyh 閱讀(1182) | 評論 (1)編輯 收藏
 
安裝Visual Studio.net 2003時,系統提示"安裝程序檢測到另一個程序要求計算機重新啟動。必須重新啟動計算機后才能安裝 Visual Studio .NET 系統必備。系統重新啟動后,您需要重新啟動安裝程序。單擊“確定”重新啟動。單擊“取消”退出安裝程序,以后再安裝。"系統重新啟動后,問題依舊。
解決方法:
打開注冊表,找到下面主鍵并刪除
HKEY_LOCAL_MACHINE\SYSTEM\Control001\Contrl\Session Manager\PendingFileRenameOperations,刪除后的效果是立桿見影的,馬上就可以安裝了。
posted @ 2006-12-13 21:16 dyh 閱讀(2466) | 評論 (5)編輯 收藏
 

一、在程序中包含commctrl.h頭文件,鏈接到ComCtl32.lib,并調用InitCommon Controls.

#if ?(_WIN32_IE?>=?0x0300)
????INITCOMMONCONTROLSEX?iccx;
????iccx.dwSize?
= ? sizeof (iccx);
????iccx.dwICC?
= ?ICC_COOL_CLASSES? | ?ICC_BAR_CLASSES;
????BOOL?bRet?
= ?::InitCommonControlsEx( & iccx);
????bRet;
????ATLASSERT(bRet);
#else
????::InitCommonControls();
#endif


二、向項目中添加一個名為YourApp.exe.manifest的文件,其中的YourApp就寫你的可執行文件的名稱,這個文件具有XML格式:

?? <? xml?version="1.0"?encoding="UTF-8"?standalone="yes"? ?> ?
-?
< assembly? xmlns ="urn:schemas-microsoft-com:asm.v1" ?manifestVersion ="1.0" >
??
< assemblyIdentity? version ="1.0.0.0" ?processorArchitecture ="X86" ?name ="CompanyName.ProductName.YourApp" ?type ="win32" ? /> ?
??
< description > Your?application?description?here. </ description > ?
-?
< dependency >
-?
< dependentAssembly >
??
< assemblyIdentity? type ="win32" ?name ="Microsoft.Windows.Common-Controls" ?version ="6.0.0.0" ?processorArchitecture ="X86" ?publicKeyToken ="6595b64144ccf1df" ?language ="*" ? /> ?
??
</ dependentAssembly >
??
</ dependency >
??
</ assembly >

三、在應用程序的資源文件(YourApp.rc)的頂部中添加這樣的一行語句:

CREATEPROCESS_MANIFEST_RESOURCE_ID?RT_MANIFEST? "YourApp .exe.manifest "

我在程序中沒有做第一步也能成功,可能是因為沒有用API創建的窗口或動態窗口,在程序中如果調用了CreateWindow函數去創建窗口,則需要執行第一步。

posted @ 2006-06-22 13:04 dyh 閱讀(630) | 評論 (0)編輯 收藏
 
大家在使用C++寫操作文件的程序時,一定使用過eof()這個函數,用它來判別文件結束,但有不少也用來判別文件是否為空的.但是,這樣操作的結果不是我們所想的.看下面程序:

#include?<string>

using?namespace?std;

int?main()
{
????
char?c?=?'c';
????ifstream?FILE(
"test.txt");
????
if?(FILE.eof())
????????cout?
<<?"文件是空的."<<endl;exit(1);?

????
while?(!FILE.eof())
????
{
????????FILE.
get(c);
????????cout?
<<?c;
????}
????
????system(
"pause");
????
return?0;
}


當test.txt為空文件時,它輸出的是:c
奇怪!應該輸出是:文件是空的. while里面的操作也應該不用到的.但是結果偏偏和我們所想的相反.
好,那操作二進制文件又是怎樣的呢?修改下為:
???

?ifstream?FILE("test.txt",ios::in|ios::binary);
????
if?(FILE.eof())
????????cout?
<<?"文件是空的."<????while?(!FILE.eof())
????
{
????????FILE.read(
&c,1);
????????cout?
<<?c;
????}
//代碼其他部分相同

結果輸出還是c..噢!!怎么會這樣的.分明是騙人的東西嘛!!到底是什么原因呢?
???? 經過一段研究后,原來eof()返回true的條件是"讀到文件結束符",而不是文件內容的最后一個字符。
要清楚"文件結束符"(0xff).就是說我們文件最后的字符不是文件結束符,而最后的字符的下一位才是.所以操作再讀多一次.就為什么上面if (FILE.eof())總是false的. 在一些編譯器中(dev c++),它讀到最后一個字符后文件位置的指針會定在那兒,所以就會重復最后一個字符.?????
??? 在一個外國的CPP論壇見到一位同志的代碼剛好有這解決方法.現在把上面的代碼改為下面的:

#include?<iostream.h>
#include?
<stdlib.h>
#include?
<fstream.h>

int?main()
{
????
char?c?=?'c';
????ifstream?FILE(
"test.txt");
????
if?(FILE.peek()?==?EOF)//修改?
????{
????????cout?
<<?"文件是空的."<<?endl;?
????????exit(
1);
????}
???????
????
while?(FILE.peek()?!=?EOF)//修改
????{
????????FILE.
get(c);
????????cout?
<<?c;
????}
????
????system(
"pause");
????
return?0;
}

主要的是把eof()改為peek() == EOF來判別,其中peek()是取文件當前指針,EOF是文件尾標符,它的值為-1.所以采用這種方法就解決上面eof()的問題了..這種方法也可以用在讀寫二進制文件中.

posted @ 2006-06-21 21:10 dyh 閱讀(4906) | 評論 (6)編輯 收藏
 

??????? 由于項目的需要,需要用C++去連Oracle數據庫,Oracle版本為10g,在經過n次錯誤后終于成功連接。
?????? ?最開始用ADO連,裝上客戶端以后,在Oracle Net Manager中設置服務命名,測試連接成功,然后
設置ODBC數據源,測試也成功,ADO連接串寫上"DSN=xxx"就能連上了,但是在釋放連接時總是出錯,
經過多次嘗試后放棄,在網上看到Oracle專門為C++提供了連接的接口OCCI,于是嘗試采用這種方法。
??????? 一、安裝Oracle客戶端
??????????????? 安裝方式選擇為管理員。安裝完以后設置服務命名。在%ORACLIENTHOME%\NETWORK\ADMIN目錄下tnsnames.ora文件記錄了服務命名的設置。我的設置如下:
???????????????

ORCL_192.168.0.3?=
??(DESCRIPTION?=
????(ADDRESS_LIST?=
??????(ADDRESS?=?(PROTOCOL?=?TCP)(HOST?=?192.168.0.3)(PORT?=?1521))
????)
????(CONNECT_DATA?=
??????(SID?=?orcl)
??????(SERVER?=?DEDICATED)
????)
??)

ORCL?=
??(DESCRIPTION?=
????(ADDRESS_LIST?=
??????(ADDRESS?=?(PROTOCOL?=?TCP)(HOST?=?192.168.0.3)(PORT?=?1521))
????)
????(CONNECT_DATA?=
??????(SERVICE_NAME?=?ORCL_192.168.0.3)
????)
??)

其中192.168.0.3為遠程Oracle服務器IP地址
設置完以后在Net Manager中測試連接,成功就OK了。
????????二、設置VC++.net環境
??????? 我用的編譯器是VS2003,首先在工具---選項中Project添加包含文件%OracleClientHome%\Oci\include
添加庫文件%OracleClientHome%\Oci\lib\msvc\vc71和%OracleClientHome%\Oci\lib\msvc如果是VC6.0的話就
添加%OracleClientHome%\Oci\lib\msvc\vc6。
然后設置項目屬性,在編譯器---鏈接中添加lib文件,oraocci10d.lib,之后把oraocci10d.dll文件復制到system32目錄下,這個文件在VC71目錄下。
第三步在C++----代碼生成中設置運行時庫為多線程DLL或是多線程調試DLL,注意這一步設置很重要,
沒有設置的話會造成getString函數出錯。
??????? 三、代碼
???????

#include? " stdafx.h "
#include?
< occi.h >
using ? namespace ?std;
using ? namespace
?oracle::occi?;
int
?main()?
{
????Environment
* ?env = Environment::createEnvironment( " ZHS16GBK " ,? " UTF8 "
);
????
// Environment?*env=Environment::createEnvironment(Environment::DEFAULT);


????
string ?mc;
????
{
????????Connection?
* conn? = ?env -> createConnection( " system " ,? " system " ,? " orcl_192.168.0.3 "
);
????????
????????
try

????????
{
????????????Statement?
* stmt? = ?conn -> createStatement( " select?*?from?test1 "
);

????????????ResultSet?
* rs? = ?stmt ->
executeQuery();
????????????
while (rs -> next()? == ? true
)
????????????
{
????????????????mc
= rs -> getString( 1
);
????????????????cout?
<< ?mc? <<
?endl;
????????????}

????????????stmt
-> closeResultSet(rs);
????????????conn
->
terminateStatement(stmt);
????????}

????????
catch ?(SQLException?e)
????????
{
????????????cout
<<
e.what();
????????}

????????env
-> terminateConnection(conn);
????}

????Environment::terminateEnvironment(env);
????system(
" pause " );
????
return ? 0
;
}

orcl_192.168.0.3就是前面設置的連接串。
測試成功。以上就是所有的設置過程,唉,還是jdbc好連阿。。。

注意,項目屬性中運行時庫一定要設置為多線程DLL或是多線程調試DLL,否則getString函數就會出錯,我在這個問題上也卡了很長時間,單步調試發現是在string對象析購時出錯,在網上看到一篇文章得到答案。
??????? 原因是由于程序中使用的內存管理多來源于crt提供的例程,而非直接使用操作系統的接口,這些例程都需要維護一些module全局數據(例如維護池、維護空閑塊、或者標記已申請的塊等等,不同的實現中有不同的作用),當他們被靜態連編時,實際上這些“全局數據”就不“全局”了,不同的module各自為政,每份module都有自己的“全局數據”,自身的內存信息不為他人所知,module A的合法內存快自然不可能通得過module B的合法性驗證

解決問題的方法有:
1、不要跨module傳遞c++對象,或者避免釋放跨module申請的內存

2、將參與合作的module統統以multithreaded dll方式鏈入crt庫,讓他們的“全局”數據真正全局,注意,所有有交互的module都需要動態鏈入crt。

posted @ 2006-06-20 13:07 dyh 閱讀(6054) | 評論 (12)編輯 收藏
 

1)運行時庫就是 C run-time library,是 C 而非 C++ 語言世界的概念:取這個名字就是因為你的 C 程序運行時需要這些庫中的函數.

2)C 語言是所謂的“小內核”語言,就其語言本身來說很小(不多的關鍵字,程序流程控制,數據類型等);所以,C 語言內核開發出來之后,Dennis Ritchie 和 Brian Kernighan 就用 C 本身重寫了 90% 以上的 UNIX 系統函數,并且把其中最常用的部分獨立出來,形成頭文件和對應的 LIBRARY,C run-time library 就是這樣形成的。

3)隨后,隨著 C 語言的流行,各個 C 編譯器的生產商/個體/團體都遵循老的傳統,在不同平臺上都有相對應的 Standard Library,但大部分實現都是與各個平臺有關的。由于各個 C 編譯器對 C 的支持和理解有很多分歧和微妙的差別,所以就有了 ANSI C;ANSI C (主觀意圖上)詳細的規定了 C 語言各個要素的具體含義和編譯器實現要求,引進了新的函數聲明方式,同時訂立了 Standard Library 的標準形式。所以C運行時庫由編譯器生產商提供。至于由其他廠商/個人/團體提供的頭文件和庫函數,應當稱為第三方 C 運行庫(Third party C run-time libraries)。

4)C run-time library里面含有初始化代碼,還有錯誤處理代碼(例如divide by zero處理)。你寫的程序可以沒有math庫,程序照樣運行,只是不能處理復雜的數學運算,不過如果沒有了C run-time庫,main()就不會被調用,exit()也不能被響應。因為C run-time library包含了C程序運行的最基本和最常用的函數。


5)到了 C++ 世界里,有另外一個概念:Standard C++ Library,它包括了上面所說的 C run-time library 和 STL。包含 C run-time library 的原因很明顯,C++ 是 C 的超集,沒有理由再重新來一個 C++ run-time library. VC針對C++ 加入的Standard C++ Library主要包括:LIBCP.LIB, LIBCPMT.LIB和 MSVCPRT.LIB

6)Windows環境下,VC提供的 C run-time library又分為動態運行時庫和靜態運行時庫。
動態運行時庫主要是DLL庫文件msvcrt.dll(or MSVCRTD.DLL for debug build),對應的Import library文件是MSVCRT.LIB(MSVCRTD.LIB for debug build)
靜態運行時庫(release版)對應的主要文件是:
LIBC.LIB (Single thread static library, retail version)
LIBCMT.LIB (Multithread static library, retail version)

msvcrt.dll提供幾千個C函數,即使是像printf這么低級的函數都在msvcrt.dll里。其實你的程序運行時,很大一部分時間時在這些運行庫里運行。在你的程序(release版)被編譯時,VC會根據你的編譯選項(單線程、多線程或DLL)自動將相應的運行時庫文件(libc.lib,libcmt.lib或Import library msvcrt.lib)鏈接進來。

編譯時到底哪個C run-time library聯入你的程序取決于編譯選項:
/MD, /ML, /MT, /LD?? (Use Run-Time Library)
你可以VC中通過以下方法設置選擇哪個C run-time library聯入你的程序:
To find these options in the development environment, click Settings on the Project menu. Then click the C/C++ tab, and click Code Generation in the Category box. See the Use Run-Time Library drop-down box.

從程序可移植性考慮,如果兩函數都可完成一種功能,選運行時庫函數好,因為各個 C 編譯器的生產商對標準C Run-time library提供了統一的支持.

posted @ 2006-06-20 12:11 dyh 閱讀(606) | 評論 (0)編輯 收藏
僅列出標題
共2頁: 1 2 
 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩一区二区三区在线| 久久国产精品一区二区三区| 国产精品扒开腿爽爽爽视频| 欧美日韩国产成人在线| 免费成人高清| 欧美激情一区二区三级高清视频| 欧美成人资源| 国产精品狠色婷| 国产欧美一区二区三区在线老狼 | 亚洲三级网站| 日韩亚洲欧美在线观看| 亚洲在线播放电影| 久久久激情视频| 亚洲国产日韩一区| 亚洲国产精品小视频| 99视频在线精品国自产拍免费观看| 99一区二区| 久久久夜精品| 欧美性大战久久久久久久| 国产女人aaa级久久久级| 激情亚洲网站| 亚洲欧美在线高清| 欧美国产日韩精品免费观看| 国产私拍一区| 亚洲国产欧美精品| 欧美一区二区三区久久精品茉莉花| 久久这里只有精品视频首页| 亚洲国产精品嫩草影院| 香蕉av777xxx色综合一区| 欧美成人一区二区三区在线观看 | av不卡在线观看| 久久福利精品| 亚洲肉体裸体xxxx137| 午夜日韩av| 欧美视频观看一区| 亚洲人成网站在线观看播放| 欧美在线视频二区| 在线视频日韩| 欧美成人久久| 在线免费观看日本一区| 久久er精品视频| 一本色道久久精品| 欧美激情1区2区| 亚洲国产成人av| 久久亚裔精品欧美| 亚洲影视在线播放| 欧美日韩中文在线观看| 91久久精品美女高潮| 久久久欧美一区二区| 亚洲视频一区| 国产精品成人免费精品自在线观看| 亚洲国内高清视频| 欧美大胆a视频| 美女日韩在线中文字幕| 影音先锋中文字幕一区| 久久精品视频免费播放| 亚洲自拍另类| 国产日韩亚洲欧美综合| 欧美一区二区私人影院日本| 在线视频一区二区| 国产精品人人做人人爽| 亚洲欧美成人| 亚洲在线一区二区三区| 国产精品视频xxx| 欧美一区二区三区免费看| 亚洲一区免费在线观看| 国产手机视频精品| 久热精品在线视频| 蜜臀av性久久久久蜜臀aⅴ| 亚洲国产网站| 亚洲人妖在线| 欧美色视频一区| 午夜精品一区二区三区在线播放 | 欧美特黄一级| 西西人体一区二区| 久久国产精品99国产| 黄色成人在线网址| 亚洲最新色图| 在线视频精品一| 国产日韩亚洲欧美精品| 久久久亚洲国产美女国产盗摄| 久久九九99| 99精品热6080yy久久 | 亚洲视频在线观看视频| 国产喷白浆一区二区三区| 久久婷婷国产麻豆91天堂| 另类天堂av| 亚洲永久视频| 久久久999精品| 一区二区三区黄色| 欧美一区二区三区免费观看视频| 在线观看日韩www视频免费 | 久久嫩草精品久久久久| 亚洲国产日韩欧美在线动漫| 亚洲精品九九| 国产亚洲精品综合一区91| 你懂的网址国产 欧美| 欧美日韩国产首页| 久久人人97超碰精品888| 欧美女同视频| 麻豆精品视频在线观看视频| 欧美日韩精品在线观看| 久久综合五月| 国产精品日日摸夜夜摸av| 欧美成人亚洲成人| 国产欧美精品va在线观看| 亚洲国产精品va在线观看黑人| 国产精品久久久久av免费| 欧美韩国日本一区| 国产亚洲精品美女| 亚洲一区三区视频在线观看| 亚洲精品一区二区三区在线观看 | 国产精品高清网站| 欧美a级片网| 国产小视频国产精品| 亚洲剧情一区二区| 在线观看国产日韩| 亚洲免费在线观看| 亚洲一区二区成人在线观看| 毛片一区二区| 美日韩丰满少妇在线观看| 国产欧美精品在线观看| 一二三区精品| 亚洲性感激情| 欧美日韩免费观看一区=区三区| 欧美国产免费| 亚洲国产精品va| 久久久噜噜噜久噜久久| 久久久久久网站| 国产一区二区三区久久久| 亚洲在线国产日韩欧美| 亚洲一区三区视频在线观看| 欧美精品在线观看91| 亚洲国语精品自产拍在线观看| 在线观看视频亚洲| 久久香蕉国产线看观看av| 久久综合精品国产一区二区三区| 国产人成精品一区二区三| 亚洲欧美综合精品久久成人| 国产一区二区在线观看免费播放 | 亚洲欧洲一区二区三区在线观看 | 中文久久乱码一区二区| 国产精品久久久久久久久果冻传媒| 久久一区二区视频| 久久亚洲一区| 老司机午夜精品视频在线观看| 久久久亚洲成人| 久久在线免费观看视频| 午夜精品999| 国产精品成人aaaaa网站| 99精品国产99久久久久久福利| 在线综合亚洲| 国产精品丝袜91| 午夜精品一区二区三区电影天堂 | 性久久久久久久久| 国产日韩欧美日韩| 久久久精品tv| 亚洲欧洲一区| 欧美一级片一区| 在线成人激情视频| 欧美老女人xx| 亚洲欧美日韩天堂一区二区| 久久亚洲精选| 一区二区三区国产盗摄| 国产欧美一区二区三区久久人妖| 另类av一区二区| 一本色道久久综合亚洲精品婷婷| 欧美与黑人午夜性猛交久久久| 在线日韩成人| 欧美日韩国产成人在线免费| 香蕉乱码成人久久天堂爱免费| 欧美成在线观看| 欧美在线观看视频一区二区三区| 伊人久久亚洲影院| 国产精品成人v| 久久精品二区三区| 99国产精品久久久久久久成人热 | 欧美视频在线观看免费| 亚洲视频狠狠| 欧美国产高潮xxxx1819| 亚洲一区二区三区精品在线观看| 国产日韩综合| 国产精品二区影院| 老司机精品视频一区二区三区| 一区二区三区精品国产| 毛片av中文字幕一区二区| 亚洲一区二区三区四区五区黄| 伊人成人网在线看| 国产精品免费视频观看| 欧美不卡激情三级在线观看| 欧美一级大片在线免费观看| 亚洲美女视频| 亚洲国产第一页| 久久综合久久久| 久久精品91| 欧美大片免费久久精品三p | 亚洲女人天堂成人av在线| 韩国av一区二区三区四区| 欧美日韩1区| 欧美高清你懂得|