• <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>

            積木

            No sub title

              C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              140 Posts :: 1 Stories :: 11 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(1)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            #

            原文轉載自:http://blog.csdn.net/leishiwei/article/details/4009307

            看完下面這篇文章,即可明白為什么在使用lua時,需要對所包含的頭文件添加:extern "C"這樣的符號來括起來。
            extern "C"
            {
                #include "lua.h"
                #include "lualib.h"
                #include "luaxlib.h"
            }

            Lua編譯要考慮extern “C”

            分類: 游戲腳本 219人閱讀 評論(1) 收藏 舉報

             (轉 七星重劍)
            C++項目要到了如Lua這種純C寫成的庫時,如果鏈接不過,就考慮是包含頭文件時沒加extern “C”。
            #ifdef __cplusplus
            extern "C" {
            #endif
                #include "lualib.h"
                #include "lauxlib.h"
            #ifdef __cplusplus
            }
            #endif
              
            時常在cpp的代碼之中看到這樣的代碼:
            #ifdef __cplusplus
            extern "C" {
            #endif
            //一段代碼
            #ifdef __cplusplus
            }
            #endif
              這樣的代碼到底是什么意思呢?首先,__cplusplus是cpp中的自定義宏,那么定義了這個宏的話表示這是一段cpp的代碼,也就是說,上面的代碼的含義是:如果這是一段cpp的代碼,那么加入extern "C"{和}處理其中的代碼。
              要明白為何使用extern "C",還得從cpp中對函數的重載處理開始說起。在c++中,為了支持重載機制,在編譯生成的匯編碼中,要對函數的名字進行一些處理,加入比如函數的返回類型等等.而在C中,只是簡單的函數名字而已,不會加入其他的信息.也就是說:C++和C對產生的函數名字的處理是不一樣的.
              比如下面的一段簡單的函數,我們看看加入和不加入extern "C"產生的匯編代碼都有哪些變化:
            int f(void)
            {
            return 1;
            }
              在加入extern "C"的時候產生的匯編代碼是:
            .file "test.cxx"
            .text
            .align 2
            .globl _f
            .def _f; .scl 2; .type 32; .endef
            _f:
            pushl %ebp
            movl %esp, %ebp
            movl $1, %eax
            popl %ebp
            ret
              但是不加入了extern "C"之后
            .file "test.cxx"
            .text
            .align 2
            .globl __Z1fv
            .def __Z1fv; .scl 2; .type 32; .endef
            __Z1fv:
            pushl %ebp
            movl %esp, %ebp
            movl $1, %eax
            popl %ebp
            ret
              兩段匯編代碼同樣都是使用gcc -S命令產生的,所有的地方都是一樣的,唯獨是產生的函數名,一個是_f,一個是__Z1fv。
              明白了加入與不加入extern "C"之后對函數名稱產生的影響,我們繼續我們的討論:為什么需要使用extern "C"呢?C++之父在設計C++之時,考慮到當時已經存在了大量的C代碼,為了支持原來的C代碼和已經寫好C庫,需要在C++中盡可能的支持C,而extern "C"就是其中的一個策略。
              試想這樣的情況:一個庫文件已經用C寫好了而且運行得很良好,這個時候我們需要使用這個庫文件,但是我們需要使用C++來寫這個新的代碼。如果這個代碼使用的是C++的方式鏈接這個C庫文件的話,那么就會出現鏈接錯誤.我們來看一段代碼:首先,我們使用C的處理方式來寫一個函數,也就是說假設這個函數當時是用C寫成的:
            //f1.c
            extern "C"
            {
            void f1()
            {
            return;
            }
            }
              編譯命令是:gcc -c f1.c -o f1.o 產生了一個叫f1.o的庫文件。再寫一段代碼調用這個f1函數:
            // test.cxx
            //這個extern表示f1函數在別的地方定義,這樣可以通過
            //編譯,但是鏈接的時候還是需要
            //鏈接上原來的庫文件.
            extern void f1();
            int main()
            {
            f1();
            return 0;
            }
              通過gcc -c test.cxx -o test.o 產生一個叫test.o的文件。然后,我們使用gcc test.o f1.o來鏈接兩個文件,可是出錯了,錯誤的提示是:
            test.o(.text + 0x1f):test.cxx: undefine reference to 'f1()'
              也就是說,在編譯test.cxx的時候編譯器是使用C++的方式來處理f1()函數的,但是實際上鏈接的庫文件卻是用C的方式來處理函數的,所以就會出現鏈接過不去的錯誤:因為鏈接器找不到函數。
              因此,為了在C++代碼中調用用C寫成的庫文件,就需要用extern "C"來告訴編譯器:這是一個用C寫成的庫文件,請用C的方式來鏈接它們。
              比如,現在我們有了一個C庫文件,它的頭文件是f.h,產生的lib文件是f.lib,那么我們如果要在C++中使用這個庫文件,我們需要這樣寫:
            extern "C"
            {
            #include "f.h"
            }
              回到上面的問題,如果要改正鏈接錯誤,我們需要這樣子改寫test.cxx:
            extern "C"
            {
            extern void f1();
            }
            int main()
            {
            f1();
            return 0;
            }
              重新編譯并且鏈接就可以過去了.
              總結
              C和C++對函數的處理方式是不同的.extern "C"是使C++能夠調用C寫作的庫文件的一個手段,如果要對編譯器提示使用C的方式來處理函數的話,那么就要使用extern "C"來說明。

            posted @ 2012-12-03 10:28 Jacc.Kim 閱讀(557) | 評論 (0)編輯 收藏

            1) 正如書中所述,stl所述的heap都是max-heap。即:父節點的“值”[注釋1],永遠是 >= 其子節點的值。
            2) 正如書中所述,stl所述的heap不歸屬于容器。因為它是一組算法。這些算法的實現原理,在此[注釋2],是以一棵完全二叉樹來設計的。
            3) 以下對各個max-heap接口算法的小結:

            a) make_heap()
            說明:顧名思義,該接口就是用來“創建”一個heap的。是對原有的一堆任意存放的數據,按照第一點所述的規則,進行“排列”(注意:不是排序)。
            示例(來自書中例子,抄出來,經常看,印象會更深刻。在此,我們重在理解算法與掌握運用):
            int ai[9] = {0, 1, 2, 3, 4, 8, 9, 3, 5};
            vector<int> ivec(ia, ia + 9);
            make_heap(ivec.begin(), ivec.end());//調用后,ivec中的數據的排列將改變掉,并且已經是按max-heap的結構存儲的。
            for (int i = 0; i < ivec.size(); i++)
                cout << ivec[i] << ' ';  // 9 5 8 3 4 0 2 3 1
            cout << endl;

            b) push_heap()
            說明:將新push_back()到ivec的末尾元素按照max-heap的要求規則,進行位置調整。使得新的整個ivec中的元素排列規則符合max-heap的規則要求。
            注意:
                1) push_heap()的操作,一定都是針對最末尾的那個元素,對它的位置按照max-heap的要求,進行重新調整的。
                2) 執行push_heap()操作前,一定一定要保證除了最末尾的那個元素外,前面的元素的排列規則一定都滿足max-heap()的規則存放的。
            示例:
            ivec.push_back(7);
            push_heap(ivec.begin(), ivec.end());
            for (int i = 0; i < ivec.size(); i++)
                cout << ivec[i] << ' '; // 9 7 8 3 5 0 2 3 1 4
            cout << endl;

            c) pop_heap()
            說明:該接口意即:要從整個heap中,取出元素。但這里取出的一定是“值”最大的那個元素。而不是像vector或list等那樣,可以取出任意位置的元素。
            注意:
                1) 調用該接口“取出”元素后,其實該元素(即:“值”最大的那個元素)并未真正被取出來,而是將該元素放到了ivec的最末尾位置。(也正是因此,如果對整個ivec進行多次的pop_heap()操作,即可完成ivec的排序功能)
                2) 正如 注意1) 所述的,則在pop_heap()后,ivec除了最末尾的那個元素外,前面的元素仍然是保持著max-heap的規則存儲的。
            示例:
            pop_heap(ivec.begin(), ivec.end());
            cout << ivec.back() << endl; // 9. return but not remove.
            ivec.pop_back(); // remove last elem and no return;

            d) sort_heap()
            說明:顧名思義,是對一個heap進行排序。
            注意:
                  1) 排序后的“heap"(即:原始的heap)將不復存在(理由很簡單:排序后,原heap中的元素的存儲規則不符合max-heap的規則,因此排序后的,就不能再稱為heap)
            示例:
            sort_heap(ivec.begin(), ivec.end());
            for (int i = 0; i < ivec.size(); i++)
                cout << ivec[i] << ' '; // 0 1 2 3 3 4 5 7 8
            cout << endl;

            補充:max-heap的隱式表達式的push_heap()與pop_heap()操作時間都只有:O(logN)。一種算是比較居中的,還算不錯的時間性能參考值。

            最后再說兩點:
               1) 只要深刻理解了以上算法與接口的使用,對實際項目的動作,個人認為,是很有價值的。另外,理解了heap的原理,則我們也十分容易priority queue的實現細節。
               2) 對知識的掌握,還是重在理解。

            以上表述有誤之處,還望大伙多多指正啊。。:)

            [注釋1]:此處的值:我們可以當它是節點本身的值,也可以當它是某種權值。依自己使用需要而定。
            [注釋2]:指的是隱匿表達式實現的heap.即:以完全二叉樹方式實現的heap。
            posted @ 2012-11-21 12:07 Jacc.Kim 閱讀(326) | 評論 (0)編輯 收藏

            UPDATE creatureitem I
            SET
              I.xxx =
              (select I.yyy from space s
              where s.type = 1
              AND s.id = i.spaceid
              AND (i.roleid = ''
              OR isnull(i.roleid)))
            posted @ 2012-11-19 17:06 Jacc.Kim 閱讀(106) | 評論 (0)編輯 收藏

                 摘要: 原文轉載自:http://m.shnenglu.com/shenhuafeng/archive/2006/12/30/17041.html 模版偏特化--Partial Template Specialization(《Modern C++ Design讀書筆記二》) Partial Template Specialization顧名思義,模版偏特化就是對模版進行特化的意思。舉個例子: na...  閱讀全文
            posted @ 2012-11-15 10:23 Jacc.Kim 閱讀(527) | 評論 (0)編輯 收藏

            原文轉載自:http://www.cnblogs.com/cutepig/archive/2009/01/14/1375917.html

            按照默認規定,只有一個參數的構造函數也定義了一個隱式轉換,將該構造函數對應數據類型的數據轉換為該類對象,如下面所示:
            class String {
            String ( const char* p ); // 用C風格的字符串p作為初始化值
            //…
            }
            String s1 = “hello”; //OK 隱式轉換,等價于String s1 = String(“hello”);
             
            但是有的時候可能會不需要這種隱式轉換,如下:
            class String {
                   String ( int n ); //本意是預先分配n個字節給字符串
            String ( const char* p ); // 用C風格的字符串p作為初始化值
            //…
            }
             
            下面兩種寫法比較正常:
            String s2 ( 10 );   //OK 分配10個字節的空字符串
            String s3 = String ( 10 ); //OK 分配10個字節的空字符串
             
            下面兩種寫法就比較疑惑了:
            String s4 = 10; //編譯通過,也是分配10個字節的空字符串
            String s5 = ‘a’; //編譯通過,分配int(‘a’)個字節的空字符串
             
            s4 和s5 分別把一個int型和char型,隱式轉換成了分配若干字節的空字符串,容易令人誤解。
            為了避免這種錯誤的發生,我們可以聲明顯示的轉換,使用explicit 關鍵字:
            class String {
                   explicit String ( int n ); //本意是預先分配n個字節給字符串
            String ( const char* p ); // 用C風格的字符串p作為初始化值
            //…
            }
            加上explicit就抑制了String ( int n )的隱式轉換,
             
            下面兩種寫法仍然正確:
            String s2 ( 10 );   //OK 分配10個字節的空字符串
            String s3 = String ( 10 ); //OK 分配10個字節的空字符串
             
            下面兩種寫法就不允許了:
            String s4 = 10; //編譯不通過,不允許隱式的轉換
            String s5 = ‘a’; //編譯不通過,不允許隱式的轉換
             
            因此,某些時候,explicit 可以有效得防止構造函數的隱式轉換帶來的錯誤或者誤解

            ----------------------------------------------------------
            explicit   只對構造函數起作用,用來抑制隱式轉換。如:  
              class   A   {  
                      A(int   a);  
              };  
              int   Function(A   a);  
               
              當調用   Function(2)   的時候,2   會隱式轉換為   A   類型。這種情況常常不是程序員想要的結果,所以,要避免之,就可以這樣寫:  
               
              class   A   {  
                      explicit   A(int   a);  
              };  
              int   Function(A   a);  
               
              這樣,當調用   Function(2)   的時候,編譯器會給出錯誤信息(除非   Function   有個以   int   為參數的重載形式),這就避免了在程序員毫不知情的情況下出現錯誤。

            總結:explicit   只對構造函數起作用,用來抑制隱式轉換。

            參考:
            http://blog.csdn.net/smilelance/archive/2007/03/14/1528737.aspx
            http://topic.csdn.net/t/20040509/15/3046021.html
            posted @ 2012-11-13 16:49 Jacc.Kim 閱讀(289) | 評論 (0)編輯 收藏

            原文轉載自:http://www.eefocus.com/wang312/blog/2012-05/229203_12ffa.html

            volatile的作用與用法  2011-12-15 13:34

            本文引自百度百科volatile,就當作了筆記,和大家一起分享吧。

             

            先說說我的理解,volatile我們叫它“易變的”,是一個類型修修飾符,一般情況下,我們定義了一個變量,如果說這個變量在整個代碼中都沒有改變,那么編譯器就會把這個變量放到寄存器中,cpu取值就只在寄存器中取。但是在嵌入式中就會出現特殊情況了,很多時候,我們在代碼中都不會改變一些變量的值(特別是寄存器的狀態,或者某個引腳的狀態),但是硬件很有可能去改變這個值,如按鍵改變某引腳的狀態等一些情況,可是編譯器并不知道這個變量已經發生改變,這個時候很有可能編譯器所使用的值和實際值并不一致,所以C語言中采用volatile來修飾這一類變量,它的作用是讓編譯器每次取值的都會到內存單元中去重新讀取該變量的值,而不是直接使用寄存器中的值。

            在代碼中經常可以看到(*((volatile unsigned short *)(x)))這樣的代碼,作用也就是這個了。

            以下是百科原文:

            就象大家更熟悉的const一樣,volatile是一個類型修飾符(type specifier)。它是被設計用來修飾被不同線程訪問和修改的變量。如果沒有volatile,基本上會導致這樣的結果:要么無法編寫多線程程序,要么編譯器失去大量優化的機會。

             

              推薦一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
              1). 并行設備的硬件寄存器(如:狀態寄存器)
              2). 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
              3). 多線程應用中被幾個任務共享的變量
              回答不出這個問題的人是不會被雇傭的。我認為這是區分C程序員和嵌入式系統程序員的最基本的問題。嵌入式系統程序員經常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內容將會帶來災難。
              假設被面試者正確地回答了這是問題(嗯,懷疑是否會是這樣),我將稍微深究一下,看一下這家伙是不是真正懂得volatile完全的重要性。
              1). 一個參數既可以是const還可以是volatile嗎?解釋為什么。
              2). 一個指針可以是volatile 嗎?解釋為什么。
              3). 下面的函數有什么錯誤:
              int square(volatile int *ptr)
              {
              return *ptr * *ptr;
              }
              下面是答案:
              1).是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
              2). 是的。盡管這并不很常見。一個例子是當一個中斷服務子程序修改一個指向一個buffer的指針時。
              3).這段代碼是個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:
              int square(volatile int *ptr)
              {
              int a,b;
              a = *ptr;
              b = *ptr;
              return a * b;
              }
              由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
              long square(volatile int *ptr)
              {
              int a;
              a = *ptr;
              return a * a;
              }
              講講我的理解: (歡迎打板子...~~!)
              關鍵在于兩個地方:
              1. 編譯器的優化 (請高手幫我看看下面的理解)
              在本次線程內,當讀取一個變量時,為提高存取速度,編譯器優化時有時會先把變量讀取到一個寄存器中;以后,再取變量值時,就直接從寄存器中取值;
              當變量值在本線程里改變時,會同時把變量的新值copy到該寄存器中,以便保持一致
              當變量在因別的線程等而改變了值,該寄存器的值不會相應改變,從而造成應用程序讀取的值和實際的變量值不一致
              當該寄存器在因別的線程等而改變了值,原變量的值不會改變,從而造成應用程序讀取的值和實際的變量值不一致
              舉一個不太準確的例子:
              發薪資時,會計每次都把員工叫來登記他們的銀行卡號;一次會計為了省事,沒有即時登記,用了以前登記的銀行卡號;剛好一個員工的銀行卡丟了,已掛失該銀行卡號;從而造成該員工領不到工資
              員工 -- 原始變量地址
              銀行卡號 -- 原始變量在寄存器的備份
              2. 在什么情況下會出現(如1樓所說)
              1). 并行設備的硬件寄存器(如:狀態寄存器)
              2). 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
              3). 多線程應用中被幾個任務共享的變量
              補充: volatile應該解釋為“直接存取原始內存地址”比較合適,“易變的”這種解釋簡直有點誤導人;
              “易變”是因為外在因素引起的,象多線程,中斷等,并不是因為用volatile修飾了的變量就是“易變”了,假如沒有外因,即使用volatile定義,它也不會變化;
              而用volatile定義之后,其實這個變量就不會因外因而變化了,可以放心使用了; 大家看看前面那種解釋(易變的)是不是在誤導人
              ------------簡明示例如下:------------------
              volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如:操作系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。
              使用該關鍵字的例子如下:
              int volatile nVint;
              >>>>當要求使用volatile聲明的變量的值的時候,系統總是重新從它所在的內存讀取數據,即使它前面的指令剛剛從該處讀取過數據。而且讀取的數據立刻被保存。
              例如:
              volatile int i=10;
              int a = i;
              ...
              //其他代碼,并未明確告訴編譯器,對i進行過操作
              int b = i;
              >>>>volatile 指出i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的匯編代碼會重新從i的地址讀取數據放在b中。而優化做法是,由于編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操作,它會自動把上次讀的數據放在b中。而不是重新從i里面讀。這樣以來,如果i是一個寄存器變量或者表示一個端口數據就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問。
              >>>>注意,在vc6中,一般調試模式沒有進行代碼優化,所以這個關鍵字的作用看不出來。下面通過插入匯編代碼,測試有無volatile關鍵字,對程序最終代碼的影響:
              >>>>首先,用classwizard建一個win32 console工程,插入一個voltest.cpp文件,輸入下面的代碼:
              >>
              #i nclude
              void main()
              {
              int i=10;
              int a = i;
              printf("i= %d",a);
              //下面匯編語句的作用就是改變內存中i的值,但是又不讓編譯器知道
              __asm {
              mov dword ptr [ebp-4], 20h
              }
              int b = i;
              printf("i= %d",b);
              }
              然后,在調試版本模式運行程序,輸出結果如下:
              i = 10
              i = 32
              然后,在release版本模式運行程序,輸出結果如下:
              i = 10
              i = 10
              輸出的結果明顯表明,release模式下,編譯器對代碼進行了優化,第二次沒有輸出正確的i值。下面,我們把i的聲明加上volatile關鍵字,看看有什么變化:
              #i nclude
              void main()
              {
              volatile int i=10;
              int a = i;
              printf("i= %d",a);
              __asm {
              mov dword ptr [ebp-4], 20h
              }
              int b = i;
              printf("i= %d",b);
              }
              分別在調試版本和release版本運行程序,輸出都是:
              i = 10
              i = 32
              這說明這個關鍵字發揮了它的作用!
              ------------------------------------
              volatile對應的變量可能在你的程序本身不知道的情況下發生改變
              比如多線程的程序,共同訪問的內存當中,多個程序都可以操縱這個變量
              你自己的程序,是無法判定何時這個變量會發生變化
              還比如,他和一個外部設備的某個狀態對應,當外部設備發生操作的時候,通過驅動程序和中斷事件,系統改變了這個變量的數值,而你的程序并不知道。
              對于volatile類型的變量,系統每次用到他的時候都是直接從對應的內存當中提取,而不會利用cache當中的原有數值,以適應它的未知何時會發生的變化,系統對這種變量的處理不會做優化——顯然也是因為它的數值隨時都可能變化的情況。
              --------------------------------------------------------------------------------
              典型的例子
              for ( int i=0; i<100000; i++);
              這個語句用來測試空循環的速度的
              但是編譯器肯定要把它優化掉,根本就不執行
              如果你寫成
              for ( volatile int i=0; i<100000; i++);
              它就會執行了
              volatile的本意是“易變的”
              由于訪問寄存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優化。比如:
              static int i=0;
              int main(void)
              {
              ...
              while (1)
              {
              if (i) dosomething();
              }
              }
              /* Interrupt service routine. */
              void ISR_2(void)
              {
              i=1;
              }
              程序的本意是希望ISR_2中斷產生時,在main當中調用dosomething函數,但是,由于編譯器判斷在main函數里面沒有修改過i,因此
              可能只執行一次對從i到某寄存器的讀操作,然后每次if判斷都只使用這個寄存器里面的“i副本”,導致dosomething永遠也不會被
              調用。如果將將變量加上volatile修飾,則編譯器保證對此變量的讀寫操作都不會被優化(肯定執行)。此例中i也應該如此說明。
              一般說來,volatile用在如下的幾個地方:
              1、中斷服務程序中修改的供其它程序檢測的變量需要加volatile;
              2、多任務環境下各任務間共享的標志應該加volatile;
              3、存儲器映射的硬件寄存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;
              另外,以上這幾種情況經常還要同時考慮數據的完整性(相互關聯的幾個標志讀了一半被打斷了重寫),在1中可以通過關中斷來實
              現,2中可以禁止任務調度,3中則只能依靠硬件的良好設計了。
            posted @ 2012-11-11 22:40 Jacc.Kim 閱讀(255) | 評論 (0)編輯 收藏

            由于在處理游戲服務端時,必須考慮到使用的處理。在此,使用了boost中的時間相關的內容。
            用boost的好處很多,下面是項目中對此的一點應用小結:
            1) 支持跨平臺。
            2) 時間的精確度極高。取得毫秒級的絕對沒問題。(似乎微秒級的也可以)
            3) 用法方便。

            using namespace boost::posix_time;
            using namespace boost::gregorian;
            //using boost::gregorian::date;

            #include <boost/date_time/posix_time/posix_time.hpp>
            #define BOOST_DATE_TIME_SOURCE


            static ptime const epoch(date(1970, 1, 1));//一個基準點。
            std::string currtimestamp = CommonHelper::format("%I64u", (WE::uint64)((microsec_clock::universal_time() - epoch).total_milliseconds()));
            //提示:
               1) microsec_clock::universal_time()是取得格林威治時間。
               2) 如果是:microsec_clock::local_time()則取得的是本地時間。
               3) 如果想取得北京時間,則只需要加上8小時即可。microsec_clock::universal_time() + hours(8);
               4) 上面的語句是計算當前格林威治時間的時間戳。

            4) boost::posix_time::ptime 因重載了許多操作符運算。因此,對它的使用就如同基本類型一樣。如:可以很方便計算兩點時間點的差距。

            boost::posix_time::ptime pt1(date(xxxx, x, x), time_duration(10, 1, 23));
            //上面也可以這樣寫:boost::posix_time::ptime pt1(date(xxxx, x, x), time_duration(hours(10), minutes(1), seconds(23)));
            //再比如:boost::posix_time::ptime pt1(date(xxxx, x, x), hours(10));如果想要詳細了解,請自行研究boost源碼。

            boost::posix_time::ptime pt2(.....);//這邊的構造函數參數就不寫了。請自行參考上面的語句。

            time_duration td = pt1 - pt2;//注意:ptime是不支持 + 的。因為它沒有重載該操作。計算它們的加,是沒有意義的。而 - 有意義。所以可如此操作。

            5) 取得日期,取得時間都很方便。


            此次,就暫時就寫這么多吧。
            posted @ 2012-11-06 20:18 Jacc.Kim 閱讀(5853) | 評論 (0)編輯 收藏

            原文轉處:http://m.shnenglu.com/software8/archive/2012/10/09/193049.html

            一、面向對象是C++的重要特性.
               但是c++在c的基礎上新增加的幾點優化也是很耀眼的,就const直接可以取代c中的#define,以下幾點很重要,學不好后果也也很嚴重
            1. 限定符聲明變量只能被讀
            [cpp] view plaincopyprint?
            const int i=5;  
              int j=0;  
              ...  
              i=j; //非法,導致編譯錯誤  
              j=i; //合法  
            2. 必須初始化
            [cpp] view plaincopyprint?
            const int i=5; //合法  
              const int j; //非法,導致編譯錯誤  
            3. 在另一連接文件中引用const常量
            [cpp] view plaincopyprint?
            extern const int i; //合法  
            extern const int j=10; //非法,常量不可以被再次賦值  
                  在C++中const默認具有內部鏈接性,也就是說如果聲明const int i = 10;等價于 static const int i =10;通常可以將const 常量和inline函數放在頭文件中。所以要在A文件定義時用extern const int i=10;B文件中引用時用extern const int i。在C中恰好相反,const 默認具有外部鏈接屬性,所以在引用外部const 常量時無需加extern。
            4. 便于進行類型檢查
              用const方法可以使編譯器對處理內容有更多了解。 
            [cpp] view plaincopyprint?
            #define I=10  
            const long &i=10; /*dapingguo提醒:由于編譯器的優化,使得在const long i=10; 時i不被分配內存,而是已10直接代入以后的引用中,以致在以后的代碼中沒有錯誤,為達到說教效 
              果,特別地用&i明確地給出了i的內存分配。不過一旦你關閉所 
              有優化措施,即使const long i=10;也會引起后面的編譯錯誤。*/  
              char h=I; //沒有錯  
              char h=i; //編譯警告,可能由于數的截短帶來錯誤賦值。  
            5. 可以避免不必要的內存分配
            [cpp] view plaincopyprint?
            #define STRING "abcdefghijklmn\n"  
              const char string[]="abcdefghijklm\n";  
              ...  
              printf(STRING); //為STRING分配了第一次內存  
              printf(string); //為string一次分配了內存,以后不再分配  
              ...  
              printf(STRING); //為STRING分配了第二次內存  
              printf(string);  
              ...    
              由于const定義常量從匯編的角度來看,只是給出了對應的內存地址, 而不是象#define一樣給出的是立即數,所以,const定義的常量在程序運行過程中只有一份拷貝,而#define定義的常量在內存中有若干個拷貝。但是這個地方也有點其他問題,大家自己好好研究下吧,給個例子:
            [cpp] view plaincopyprint?
            #include <stdio.h>   
            #define ABCD    "ABCD"  
            const char a[] = "ABCD";  
            static void  
            p(const char *s)  
            {  
                    printf("%X\n", s);  
            }  
            int   main(void)  
            {  
                    const char b[] = "ABCD";  
              
                    p(ABCD);  
                    p(ABCD);  
                    p(a);  
                    p(b);  
                    return 0;  
            }  
            [plain] view plaincopyprint?
            程序輸出為:  
            400748  
            400748  
            40073F  
            FFFFEB00  
            編譯器管不了你運行是更改所謂的const int 變量。 但對于你聲明的const int 變量。在它的編譯空間里,它保留了這個數值。 所以,調用的時候,把立即數傳過去了(這個值在編譯時就確定了)。
            6. 可以通過函數對常量進行初始化
            [cpp] view plaincopyprint?
            int value();    
            const int i=value();  
              dapingguo說:假定對ROM編寫程序時,由于目標代碼的不可改寫,本語句將會無效,不過可以變通一下:
              const int &i=value();
              只要令i的地址處于ROM之外,即可實現:i通過函數初始化,而其值有不會被修改。
            7. 是不是const的常量值一定不可以被修改呢?
              觀察以下一段代碼:  
            [cpp] view plaincopyprint?
            const int i=0;  
            int *p=(int*)&i;  
            p=100;  
                 通過強制類型轉換,將地址賦給變量,再作修改即可以改變const常量值。
                 說明了申明為常量的數據也可能被改變。我這里補充的是不要對const 的濫用。強制繞過const 檢查可能引起運行錯誤。把const int i=0 聲明在函數內,能夠達到你的目的把const int i=0 聲明為全局變量,雖然仍然能夠用強制轉換繞過編譯器檢查,但會引起運行錯誤。
            可參考下例: 
            [cpp] view plaincopyprint?
            const int j=50;  
            void main()  
            {  
                const int i=0;  
                int *p=(int*)&i;  
                *p=100;   
                int *p2=(int *)&j;  
                *p2=200;  // runtime error  
                cout << &i << &j;  
                system("pause");  
            }  
            8. 請分清數值常量和指針常量,以下聲明頗為玩味:
            [cpp] view plaincopyprint?
            int ii=0;  
            const int i=0; //i是常量,i的值不會被修改  
            const int *p1i=&i; //指針p1i所指內容是常量,可以不初始化  
            int * const p2i=&ii //指針p2i是常量,所指內容可修改  
            const int * const p3i=&i; //指針p3i是常量,所指內容也是常量  
            p1i=&ii //不合法 左操作數包含“int *”類型  
            *p2i=100; //不合法  右操作數包含“int *const ”類型  
            指向常量的指針并不能保證所指向的值不被改變
            [cpp] view plaincopyprint?
            const int i=10;  
            void main()  
            {  
                const int j=20;  
                int k = 30;  
                const int * p1=&i;  
                const int * p2 = &j;  
                const int * p3 = &k;  
                // i=80; fail  
                // j= 20; fail  
                // *p3 = 50; fail  
                // 以上三種均未逃過編譯器檢查  
                k=80;   // succeed 逃過了編譯器檢查。 *p3 不行,但直接改k 允許。  
                system("pause");  
            }  
            所以對const 的理解,全局變量不僅有編譯的保護,還有運行的保護。對局部變量,則只有編譯的保護。
            所以,當你聲明一個局部const變量時,它可能在運行期被改變。
            二、關于C++中的const關鍵字的用法非常靈活
            1. const常量,如:
            [cpp] view plaincopyprint?
            const int max = 100;  <span style="font-family: simsun; "> </span>  
            優點:const常量有數據類型,而宏常量沒有數據類型。編譯器可以對前者進行類型安全檢查,而對后者只進行字符替換,沒有類型安全檢查,并且在字符替換時可能會產生意料不到的錯誤(邊際效應)
            2. const 修飾類的數據成員。
            如:
            [cpp] view plaincopyprint?
            class A  
            {  
              const int size;  
              …    
            }  
            const數據成員只在某個對象生存期內是常量,而對于整個類而言卻是可變的。因為類可以創建多個對象,不同的對象其const數據成員的值可以不同。所以不能在類聲明中初始化const數據成員,因為類的對象未被創建時,編譯器不知道const 數據成員的值是什么。如
            [cpp] view plaincopyprint?
            class A  
            {  
                const int size = 100; //錯誤  
                int array[size]; //錯誤,未知的size  
            }  
            const數據成員的初始化只能在類的構造函數的初始化表中進行。要想建立在整個類中都恒定的常量,應該用類中的枚舉常量來實現。如
            [cpp] view plaincopyprint?
            class A  
            {  
                 …  
                 enum {size1=100, size2 = 200 };  
                 int array1[size1];  
                 int array2[size2];    
            }  
            枚舉常量不會占用對象的存儲空間,他們在編譯時被全部求值。但是枚舉常量的隱含數據類型是整數,其最大值有限,且不能表示浮點數。
            3. const修飾指針的情況,見下式:
            [cpp] view plaincopyprint?
            int b = 500;    
            const int* a = &b;//[1]  
            int const *a = &b;//[2]  
            int* const a = &b;//[3]   
            const int* const a = &b;//[4]   
            如果你能區分出上述四種情況,那么,恭喜你,你已經邁出了可喜的一步。不知道,也沒關系,我們可以參考《Effective c++》Item21上的做法,如果const位于星號的左側,則const就是用來修飾指針所指向的變量,即指針指向為常量;如果const位于星號的右側,const就是修飾指針本身,即指針本身是常量。因此,[1]和[2]的情況相同,都是指針所指向的內容為常量(const放在變量聲明符的位置無關),這種情況下不允許對內容進行更改操作,如不能*a = 3 ;[3]為指針本身是常量,而指針所指向的內容不是常量,這種情況下不能對指針本身進行更改操作,如a++是錯誤的;[4]為指針本身和指向的內容均為常量。  
            4. const的初始化  
            先看一下const變量初始化的情況  
            1) 非指針const常量初始化的情況:A b;  
            [cpp] view plaincopyprint?
            const A a = b;    
            2) 指針const常量初始化的情況:
            [cpp] view plaincopyprint?
            A* d = new A();    
            const A* c = d;  //或者:const A* c = new A();    
            3)引用const常量初始化的情況:  
            [cpp] view plaincopyprint?
            A f;    
            const A& e = f; // 這樣作e只能訪問聲明為const的函數,而不能訪問一般的成員函數<span style="font-family: simsun; font-size: 14px; line-height: 23px; text-align: left; ">;  </span>  
              [思考1]: 以下的這種賦值方法正確嗎?  
              const A* c=new A();  
              A* e = c;  
              [思考2]: 以下的這種賦值方法正確嗎?  
              A* const c = new A();  
              A* b = c;
            5. 函數聲明中的運用
                  另外const 的一些強大的功能在于它在函數聲明中的應用。在一個函數聲明中,const 可以修飾函數的返回值,或某個參數;對于成員函數,還可以修飾是整個函數。有如下幾種情況,以下會逐漸的說明用法:A& operator=(const A& a);  
            [cpp] view plaincopyprint?
            void fun0(const A* a );    
            void fun1( ) const; // fun1( ) 為類成員函數   
            const A fun2( );  
            1) 修飾參數的const,如:
            [cpp] view plaincopyprint?
            void fun0(const A* a );  
            void fun1(const A& a);    
                 調用函數的時候,用相應的變量初始化const常量,則在函數體中,按照const所修飾的部分進行常量化,如形參為const A* a,則不能對傳遞進來的指針的內容進行改變,保護了原指針所指向的內容;如形參為const A& a,則不能對傳遞進來的引用對象進行改變,保護了原對象的屬性。  
            [注意]:參數const通常用于參數為指針或引用的情況,且只能修飾輸入參數;若輸入參數采用“值傳遞”方式,由于函數將自動產生臨時變量用于復制該參數,該參數本就不需要保護,所以不用const修飾。
            [總結]     
                   對于非內部數據類型的輸入參數,因該將“值傳遞”的方式改為“const引用傳遞”,目的是為了提高效率。例如,將void Func(A a)改為void Func(const A &a)。對于內部數據類型的輸入參數,不要將“值傳遞”的方式改為“const引用傳遞”。否則既達不到提高效率的目的,又降低了函數的可理解性。例如void Func(int x)不應該改為void Func(const int &x); 修飾返回值的const,如:
            [cpp] view plaincopyprint?
            const A fun2( );  
            const A* fun3( );<span style="font-family: simsun; ">  </span>  
            這樣聲明了返回值后,const按照"修飾原則"進行修飾,起到相應的保護作用。
            [cpp] view plaincopyprint?
            const Rational operator*(const Rational& lhs, const Rational& rhs)    
            {    
                  return Rational(lhs.numerator() * rhs.numerator(),    
                  lhs.denominator() * rhs.denominator());    
            } <span style="font-family: simsun; "> </span>  
            返回值用const修飾可以防止允許這樣的操作發生:
            [cpp] view plaincopyprint?
            Rational a,b;    
            Radional c;    
            (a*b) = c; <span style="font-family: simsun; "> </span>  
            一般用const修飾返回值為對象本身(非引用和指針)的情況多用于二目操作符重載函數并產生新對象的時候。  
                   一般情況下,函數的返回值為某個對象時,如果將其聲明為const時,多用于操作符的重載。通常,不建議用const修飾函數的返回值類型為某個對象或對某個對象引用的情況。原因如下:如果返回值為某個對象為const(const A test = A 實例)或某個對象的引用為const(const A& test = A實例) ,則返回值具有const屬性,則返回實例只能訪問類A中的公有(保護)數據成員和const成員函數,并且不允許對其進行賦值操作,這在一般情況下很少用到。如果給采用“指針傳遞”方式的函數返回值加const修飾,那么函數返回值(即指針)的內容不能被修改,該返回值只能被賦給加const 修飾的同類型指針。如:
            [cpp] view plaincopyprint?
            const char * GetString(void);  
            如下語句將出現編譯錯誤:
            [cpp] view plaincopyprint?
            char *str=GetString();  
            正確的用法是:
            [cpp] view plaincopyprint?
            const char *str=GetString();  
                 函數返回值采用“引用傳遞”的場合不多,這種方式一般只出現在類的賻值函數中,目的是為了實現鏈式表達。如:
            [cpp] view plaincopyprint?
            class A  
            {  
                …  
                A &operate = (const A &other); //負值函數  
            }  
               A a,b,c; //a,b,c為A的對象  
               …  
               a=b=c; //正常  
               (a=b)=c; //不正常,但是合法  
            若負值函數的返回值加const修飾,那么該返回值的內容不允許修改,上例中a=b=c依然正確。(a=b)=c就不正確了。
            [思考3]: 這樣定義賦值操作符重載函數可以嗎?  
            const A& operator=(const A& a);
                類成員函數中const的使用,一般放在函數體后,形如:void fun() const;  任何不會修改數據成員的函數都因該聲明為const類型。如果在編寫const成員函數時,不慎修改了數據成員,或者調用了其他非const成員函數,編譯器將報錯,這大大提高了程序的健壯性。如:
            [cpp] view plaincopyprint?
            class Stack  
            {  
            public:  
              void Push(int elem);  
              int Pop(void);  
              int GetCount(void) const; //const 成員函數  
            private:    
              int m_num;  
              int m_data[100];  
            };  
            int Stack::GetCount(void) const  
            {  
              ++m_num; //編譯錯誤,企圖修改數據成員m_num  
              Pop(); //編譯錯誤,企圖調用非const函數  
              Return m_num;  
            }  
            [思考題答案]  
            1 這種方法不正確,因為聲明指針的目的是為了對其指向的內容進行改變,而聲明的指針e指向的是一個常量,所以不正確;  
            2 這種方法正確,因為聲明指針所指向的內容可變;  
            3 這種做法不正確;  
            在const A::operator=(const A& a)中,參數列表中的const的用法正確,而當這樣連續賦值的時侯,問題就出現了:  
            [cpp] view plaincopyprint?
            A a,b,c:    
            (a=b)=c; <span style="font-family: simsun; "> </span>  
            因為a.operator=(b)的返回值是對a的const引用,不能再將c賦值給const常量。
            posted @ 2012-10-09 10:40 Jacc.Kim 閱讀(244) | 評論 (0)編輯 收藏

            原文出處:http://zhidao.baidu.com/question/385301217.html

            [轉載] #include<iomanip>怎么用?在c++中

            在c++程序里面經常見到下面的頭文件
              #include <iomanip>
              io代表輸入輸出,manip是manipulator(操縱器)的縮寫(在c++上只能通過輸入縮寫才有效。)
              iomanip的作用:
              主要是對cin,cout之類的一些操縱運算子,比如setfill,setw,setbase,setprecision等等。它是I/O流控制頭文件,就像C里面的格式化輸出一樣.以下是一些常見的控制函數的:
              dec 置基數為10 相當于"%d"
              hex 置基數為16 相當于"%X"
              oct 置基數為8 相當于"%o"
              setfill( 'c' ) 設填充字符為c
              setprecision( n ) 設顯示小數精度為n位
              setw( n ) 設域寬為n個字符
              這個控制符的意思是保證輸出寬度為n。如:
              cout << setw( 3 ) << 1 << setw( 3 ) << 10 << setw( 3 ) << 100 << endl; 輸出結果為
              1 10100 (默認是右對齊)當輸出長度大于3時(<<1000),setw(3)不起作用。
              ▲setw(n)用法: 通俗地講就是預設寬度
              如 cout<<setw(5)<<255<<endl;
              結果是:
              (空格)(空格)255
              ▲setfill(char c) 用法 : 就是在預設寬度中如果已存在沒用完的寬度大小,則用設置的字符c填充
              如 cout<<setfill(‘@‘)<<setw(5)<<255<<endl;
              結果是:
              @@255
              ▲setbase(int n) : 將數字轉換為 n 進制.
              如 cout<<setbase(8)<<setw(5)<<255<<endl;
              cout<<setbase(10)<<setw(5)<<255<<endl;
              cout<<setbase(16)<<255<<endl;
              結果是:
              (空格)(空格)377
              (空格)(空格) 255
              (空格)(空格)(空格) f f
              ▲ setprecision用法
              使用setprecision(n)可控制輸出流顯示浮點數的數字個數。C++默認的流輸出數值有效位是6。
              如果setprecision(n)與setiosflags(ios::fixed)合用,可以控制小數點右邊的數字個數。setiosflags(ios::fixed)是用定點方式表示實數。
              如果與setiosflags(ios::scientific)合用, 可以控制指數表示法的小數位數。setiosflags(ios::scientific)是用指數方式表示實數。
              setiosflags(ios::fixed) 固定的浮點顯示
              setiosflags(ios::scientific) 指數表示
              setiosflags(ios::left) 左對齊
              setiosflags(ios::right) 右對齊
              setiosflags(ios::skipws) 忽略前導空白
              setiosflags(ios::uppercase) 16進制數大寫輸出
              setiosflags(ios::lowercase) 16進制小寫輸出
              setiosflags(ios::showpoint) 強制顯示小數點
              setiosflags(ios::showpos) 強制顯示符號
              舉例:
              #include <iostream.h>
              #include <iomanip.h>
              using namespace std;
              int main()
              {
              cout<<12345.0<<endl;//輸出"12345"
              cout<<setiosflags(ios::fixed)<<setprecision(3)<<1.2345<<endl;輸出"1.235"(應該輸出"1.235",而不是"1.234".因為要遵循4舍5入的原則)
              cout<<setiosflags(ios::scientific)<<12345.0<<endl;//輸出"1.234500e+004 "
              cout<<setprecision(3)<<12345.0<<endl;//輸出"1.23e+004 "
              return 0;
              }
            posted @ 2012-09-27 17:14 Jacc.Kim 閱讀(410) | 評論 (0)編輯 收藏

            原來出處:http://blog.csdn.net/beyondhaven/article/details/4204345

            [轉載] C++模板:函數模板和模板函數

            1.函數模板的聲明和模板函數的生成

             

            1.1函數模板的聲明

            函數模板可以用來創建一個通用的函數,以支持多種不同的形參,避免重載函數的函數體重復設計。它的最大特點是把函數使用的數據類型作為參數。

            函數模板的聲明形式為:

            template<typename 數據類型參數標識符>

            <返回類型><函數名>(參數表)

            {

                函數體

            }

            其中,template是定義模板函數的關鍵字;template后面的尖括號不能省略;typename(或class)是聲明數據類型參數標識符的關鍵字,用以說明它后面的標識符是數據類型標識符。這樣,在以后定義的這個函數中,凡希望根據實參數據類型來確定數據類型的變量,都可以用數據類型參數標識符來說明,從而使這個變量可以適應不同的數據類型。例如:

            template<typename T>

            T fuc(T x, int y)

            {

                T x;

                //……

            }

            如果主調函數中有以下語句:

            double d;

            int a;

            fuc(d,a);

            則系統將用實參d的數據類型double去代替函數模板中的T生成函數:

            double fuc(double x,int y)

            {

                double x;

                //……

            }

            函數模板只是聲明了一個函數的描述即模板,不是一個可以直接執行的函數,只有根據實際情況用實參的數據類型代替類型參數標識符之后,才能產生真正的函數。

            關鍵字typename也可以使用關鍵字class,這時數據類型參數標識符就可以使用所有的C++數據類型。

            1.2.模板函數的生成

            函數模板的數據類型參數標識符實際上是一個類型形參,在使用函數模板時,要將這個形參實例化為確定的數據類型。將類型形參實例化的參數稱為模板實參,用模板實參實例化的函數稱為模板函數。模板函數的生成就是將函數模板的類型形參實例化的過程。例如:

            使用中應注意的幾個問題:

            ⑴ 函數模板允許使用多個類型參數,但在template定義部分的每個形參前必須有關鍵字typename或class,即:

            template<class 數據類型參數標識符1,…,class 數據類型參數標識符n>

            <返回類型><函數名>(參數表)

            {

                 函數體

            }

            ⑵ 在template語句與函數模板定義語句<返回類型>之間不允許有別的語句。如下面的聲明是錯誤的:

            template<class T>

            int I;

            T min(T x,T y)

            {

               函數體

            }

            ⑶ 模板函數類似于重載函數,但兩者有很大區別:函數重載時,每個函數體內可以執行不同的動作,但同一個函數模板實例化后的模板函數都必須執行相同的動作。


            2 函數模板的異常處理

            函數模板中的模板形參可實例化為各種類型,但當實例化模板形參的各模板實參之間不完全一致時,就可能發生錯誤,如:

            template<typename T>       

            void min(T &x, T &y)

            {  return (x<y)?x:y;  }

            void func(int i, char j)

            {

               min(i, i);

               min(j, j);

               min(i, j);

               min(j, i);

            }

            例子中的后兩個調用是錯誤的,出現錯誤的原因是,在調用時,編譯器按最先遇到的實參的類型隱含地生成一個模板函數,并用它對所有模板函數進行一致性檢查,例如對語句

            min(i, j);

            先遇到的實參i是整型的,編譯器就將模板形參解釋為整型,此后出現的模板實參j不能解釋為整型而產生錯誤,此時沒有隱含的類型轉換功能。解決此種異常的方法有兩種:

            ⑴采用強制類型轉換,如將語句min(i, j);改寫為min(i,int( j));

            ⑵用非模板函數重載函數模板

            方法有兩種:

            ① 借用函數模板的函數體

            此時只聲明非模板函數的原型,它的函數體借用函數模板的函數體。如改寫上面的例子如下:

            template<typename T>       

            void min(T &x, T &y)

            {  return (x<y)?x:y;  }

            int min(int,int);

            void func(int i, char j)

            {

               min(i, i);

               min(j, j);

               min(i, j);

               min(j, i);

            }

            執行該程序就不會出錯了,因為重載函數支持數據間的隱式類型轉換。

            ② 重新定義函數體

            就像一般的重載函數一樣,重新定義一個完整的非模板函數,它所帶的參數可以隨意。C++中,函數模板與同名的非模板函數重載時,應遵循下列調用原則:

            • 尋找一個參數完全匹配的函數,若找到就調用它。若參數完全匹配的函數多于一個,則這個調用是一個錯誤的調用。

            • 尋找一個函數模板,若找到就將其實例化生成一個匹配的模板函數并調用它。

            • 若上面兩條都失敗,則使用函數重載的方法,通過類型轉換產生參數匹配,若找到就調用它。

            •若上面三條都失敗,還沒有找都匹配的函數,則這個調用是一個錯誤的調用。


            此為轉載文章,文章出處沒有了。請大家見諒。


            posted @ 2012-09-27 16:59 Jacc.Kim 閱讀(214) | 評論 (0)編輯 收藏

            僅列出標題
            共14頁: First 3 4 5 6 7 8 9 10 11 Last 
            97久久国产综合精品女不卡| 91精品国产高清91久久久久久| 亚洲国产精品一区二区久久| 91精品国产综合久久香蕉| 91精品婷婷国产综合久久| 久久综合给合综合久久| 伊人久久综合无码成人网| 国产999精品久久久久久| 狠狠色丁香婷婷久久综合| AV无码久久久久不卡网站下载 | 日本精品久久久久影院日本| 久久人人爽人人爽人人片AV麻烦| 国产欧美一区二区久久| 午夜精品久久久久久久无码| 国产成人精品久久二区二区| 三级三级久久三级久久| 91久久精品电影| 久久亚洲AV成人出白浆无码国产 | 久久精品国产久精国产果冻传媒| 99久久精品国产免看国产一区| 伊人久久大香线蕉综合网站| 国产无套内射久久久国产| 久久久久免费看成人影片| 久久久久亚洲AV成人网人人网站 | 欧美伊人久久大香线蕉综合| 久久精品国产99国产精品| 久久国产免费观看精品| 日产精品久久久久久久| 久久精品国产亚洲AV忘忧草18| 国产精品免费久久久久电影网| 精品久久香蕉国产线看观看亚洲| 久久亚洲精品人成综合网| 久久狠狠爱亚洲综合影院| 亚洲性久久久影院| 色偷偷88欧美精品久久久| 久久天天日天天操综合伊人av| 久久精品成人欧美大片| 久久亚洲国产精品五月天婷| 四虎久久影院| 亚洲国产美女精品久久久久∴ | 久久久久亚洲?V成人无码|