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

每天早晨叫醒你的不是鬧鐘,而是夢(mèng)想

  C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
  62 Posts :: 0 Stories :: 5 Comments :: 0 Trackbacks

常用鏈接

留言簿(1)

我參與的團(tuán)隊(duì)

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

#

一、函數(shù):

    在Lua中函數(shù)的調(diào)用方式和C語(yǔ)言基本相同,如:print("Hello World")和a = add(x, y)。唯一的差別是,如果函數(shù)只有一個(gè)參數(shù),并且該參數(shù)的類型為字符串常量或table的構(gòu)造器,那么圓括號(hào)可以省略,如print "Hello World"和f {x = 20, y = 20}。
    Lua為面對(duì)對(duì)象式的調(diào)用也提供了一種特殊的語(yǔ)法--冒號(hào)操作符。表達(dá)式o.foo(o,x)的另一種寫(xiě)法是o:foo(x)。冒號(hào)操作符使調(diào)用o.foo時(shí)將o隱含的作為函數(shù)的第一個(gè)參數(shù)。
    Lua中函數(shù)的聲明方式如下:
    function add(a)
        local sum = 0
        for i, v in ipairs(a) do
            sum = sum + v
        end
        return sum
    end
    在以上聲明中,包含了函數(shù)名(add),參數(shù)列表(a),以及函數(shù)體。需要說(shuō)明的是,Lua中實(shí)參和形參的數(shù)量可以不一致,一旦出現(xiàn)這種情況,Lua的處理規(guī)則等同于多重賦值,即實(shí)參多于形參,多出的部分被忽略,如果相反,沒(méi)有被初始化的形參的缺省值為nil。

    1. 多重返回值:
    Lua支持返回多個(gè)結(jié)果值。如:

1 s,e = string.find("Hello Lua users","Lua")
2 print("The begin index is " .. s .. ", the end index is " .. e .. ".");
3 -- The begin index is 7, the end index is 9.

    以上的代碼示例只是演示了如何獲取Lua函數(shù)的多個(gè)返回值,下面的示例將給出如何聲明返回多個(gè)值的Lua函數(shù)。如:

復(fù)制代碼
 1 function maximum(a)
2 local mi = 1
3 local m = a[mi]
4 for i, val in ipairs(a) do
5 if val > m then
6 mi,m = i,val
7 end
8 end
9 return m,mi
10 end
11 print(maximum{8,10,23,12,5})
12 --23 3
復(fù)制代碼

    Lua會(huì)調(diào)整一個(gè)函數(shù)的返回值數(shù)量以適應(yīng)不同的調(diào)用情況。若將函數(shù)調(diào)用作為一條單獨(dú)語(yǔ)句時(shí),Lua會(huì)丟棄函數(shù)的所有返回值。若將函數(shù)作為表達(dá)式的一部分來(lái)調(diào)用時(shí),Lua只保留函數(shù)的第一個(gè)返回值。只有當(dāng)一個(gè)函數(shù)調(diào)用是一系列表達(dá)式中的最后一個(gè)元素時(shí),才能獲得所有返回值。這里先給出三個(gè)樣例函數(shù),如:
    function foo0() end
    function foo1() return "a" end
    function foo2() return "a","b" end

示例代碼結(jié)果注釋
x,y = foo2()x = "a", y = "b"函數(shù)調(diào)用時(shí)最后的(或僅有的)一個(gè)表達(dá)式,Lua會(huì)保留其盡可能多的返回值,用于匹配賦值變量。
x = foo2()x = "a", 返回值"b"被忽略
x,y,z = 10,foo2()x = 10, y = "a", z = "b"
x,y = foo0()x = nil, y = nil如果一個(gè)函數(shù)沒(méi)有返回值或者沒(méi)有足夠多的返回值,那么Lua會(huì)用nil來(lái)填補(bǔ)。
x,y = foo1()x = "a", y = nil
x,y,z = foo2()x = "a", y = "b", z = nil
x,y = foo2(),20x = "a", y = 20如果一個(gè)函數(shù)調(diào)用不是一系列表達(dá)式的最后一個(gè)元素,那么將只產(chǎn)生一個(gè)值。
x,y = foo0(),20,30x = nil, y = 20, 30被忽略。
print(foo0()) 當(dāng)一個(gè)函數(shù)調(diào)用左右另一個(gè)函數(shù)調(diào)用的最后一個(gè)實(shí)參時(shí),第一個(gè)函數(shù)的所有返回值都將作為實(shí)參傳入第二個(gè)函數(shù)。
print(foo1()) a
print(foo2())a    b
print(foo2(),1)a    1
t = {foo0()} t = {} --空table table構(gòu)造器可以完整的接收一個(gè)函數(shù)調(diào)用的所有結(jié)果,即不會(huì)有任何數(shù)量方面的調(diào)整。  
t = {foo1()} t = {"a"} 
t = {foo2()}t = {"a", "b"}
t = { foo0(), foo2(), 4}t[1] = nil, t[2] = "a", t[3] = 4如果函數(shù)調(diào)用不是作為最后一個(gè)元素,那么只返回函數(shù)的第一個(gè)結(jié)果值。
print((foo2()))a如果函數(shù)調(diào)用放入圓括號(hào)中,那么Lua將只返回該函數(shù)的第一個(gè)結(jié)果值。

    最后一個(gè)需要介紹的是Lua中unpack函數(shù),該函數(shù)將接收數(shù)組作為參數(shù),并從下標(biāo)1開(kāi)始返回該數(shù)組的所有元素。如:
    /> lua
    > print(unpack{10,20,30})
    10  20  30
    > a,b = unpack{10,20,30}
    > print(a,b)
    10  20
    > string.find(unpack{"hello","ll"})  --等同于string.find("hello","ll")
    在Lua中unpack函數(shù)是用C語(yǔ)言實(shí)現(xiàn)的。為了便于理解,下面給出在Lua中通過(guò)遞歸實(shí)現(xiàn)一樣的效果,如:

1 function unpack(t,i)
2 i = i or 1
3 if t[i] then
4 return t[i], unpack(t,i + 1)
5 end
6 end


    2. 變長(zhǎng)參數(shù):
    Lua中的函數(shù)可以接受不同數(shù)量的實(shí)參,其聲明和使用方式如下:

復(fù)制代碼
1 function add(...)
2 local s = 0
3 for i, v in ipairs{...} do
4 s = s + v
5 end
6 return s
7 end
8 print(add(3,4,5,6,7))
9 --輸出結(jié)果為:25
復(fù)制代碼

    解釋一下,函數(shù)聲明中的(...)表示該函數(shù)可以接受不同數(shù)量的參數(shù)。當(dāng)這個(gè)函數(shù)被調(diào)用時(shí),所有的參數(shù)都被匯聚在一起,函數(shù)中訪問(wèn)它時(shí),仍需用3個(gè)點(diǎn)(...)。但不同的是,此時(shí)這3個(gè)點(diǎn)將作為表達(dá)式來(lái)使用,如{...}表示一個(gè)由所有變參構(gòu)成的數(shù)組。在含有變長(zhǎng)參數(shù)的函數(shù)中個(gè),同樣可以帶有固定參數(shù),但是固定參數(shù)一定要在變長(zhǎng)參數(shù)之前聲明,如:
    function test(arg1,arg2,...)
        ...
    end
    關(guān)于Lua的變長(zhǎng)參數(shù)最后需要說(shuō)明的是,由于變長(zhǎng)參數(shù)中可能包含nil值,因此再使用類似獲取table元素?cái)?shù)量(#)的方式獲取變參的數(shù)量就會(huì)出現(xiàn)問(wèn)題。如果要想始終獲得正確的參數(shù)數(shù)量,可以使用Lua提供的select函數(shù),如:

1 for i = 1, select('#',...) do  --這里'#'值表示讓select返回變參的數(shù)量(其中包括nil)。
2 local arg = select(i, ...) --這里的i表示獲取第i個(gè)變參,1為第一個(gè)。
3 --do something
4 end


    3. 具名實(shí)參:
    在函數(shù)調(diào)用時(shí),Lua的傳參規(guī)則和C語(yǔ)言相同,并不真正支持具名實(shí)參。但是我們可以通過(guò)table來(lái)模擬,比如:
    function rename(old,new)
        ...
    end
    這里我們可以讓上面的rename函數(shù)只接收一個(gè)參數(shù),即table類型的參數(shù),與此同時(shí),該table對(duì)象將含有old和new兩個(gè)key。如:
    function rename(arg)
        local old = arg.old
        local new = arg.new
        ...
    end
    這種修改方式有些類似于JavaBean,即將多個(gè)參數(shù)合并為一個(gè)JavaBean。然而在使用時(shí),Lua的table存在一個(gè)天然的優(yōu)勢(shì),即如果函數(shù)只有一個(gè)參數(shù)且為string或table類型,在調(diào)用該函數(shù)時(shí),可以不用加圓括號(hào),如:
    rename {old = "oldfile.txt", new = "newfile.txt"}

二、深入函數(shù):

    在Lua中函數(shù)和所有其它值一樣都是匿名的,即它們都沒(méi)有名稱。在使用時(shí)都是操作持有該函數(shù)的變量,如:
    a = { p = print }
    a.p("Hello World")
    b = print
    b("Hello World")
    在聲明Lua函數(shù)時(shí),可以直接給出所謂的函數(shù)名,如:
    function foo(x) return 2 * x end
    我們同樣可以使用下面這種更為簡(jiǎn)化的方式聲明Lua中的函數(shù),如:
    foo = function(x) return 2 * x end
    因此,我們可以將函數(shù)理解為由語(yǔ)句構(gòu)成的類型值,同時(shí)將這個(gè)值賦值給一個(gè)變量。由此我們可以將表達(dá)式"function(x) <body> end"視為一種函數(shù)的構(gòu)造式,就想table的{}一樣。我們將這種函數(shù)構(gòu)造式的結(jié)果稱為一個(gè)"匿名函數(shù)"。下面的示例顯示了匿名函數(shù)的方便性,它的使用方式有些類似于Java中的匿名類,如:
    table.sort(test_table,function(a,b) return (a.name > b.name) end)

    1. closure(閉合函數(shù)):
    若將一個(gè)函數(shù)寫(xiě)在另一個(gè)函數(shù)之內(nèi),那么這個(gè)位于內(nèi)部的函數(shù)便可以訪問(wèn)外部函數(shù)中的局部變量,見(jiàn)如下示例:

復(fù)制代碼
 1 function newCounter() 
2 local i = 0
3 return function() --匿名函數(shù)
4 i = i + 1
5 return i
6 end
7 end
8 c1 = newCounter()
9 print("The return value of first call is " .. c1())
10 print("The return value of second call is " .. c1())
11 --輸出結(jié)果為:
12 --The return value of first call is 1
13 --The return value of second call is 2
復(fù)制代碼

    在上面的示例中,我們將newCounter()函數(shù)稱為閉包函數(shù)。其函數(shù)體內(nèi)的局部變量i被稱為"非局部變量",和普通局部變量不同的是該變量被newCounter函數(shù)體內(nèi)的匿名函數(shù)訪問(wèn)并操作。再有就是在函數(shù)newCounter返回后,其值仍然被保留并可用于下一次計(jì)算。再看一下下面的調(diào)用方式。

復(fù)制代碼
 1 function newCounter() 
2 local i = 0
3 return function() --匿名函數(shù)
4 i = i + 1
5 return i
6 end
7 end
8 c1 = newCounter()
9 c2 = newCounter()
10 print("The return value of first call with c1 is " .. c1())
11 print("The return value of first call with c2 is " .. c2())
12 print("The return value of second call with c1 is " .. c1())
13 --輸出結(jié)果為:
14 --The return value of first call with c1 is 1
15 --The return value of first call with c2 is 1
16 --The return value of second call with c1 is 2
復(fù)制代碼

    由此可以推出,Lua每次在給新的閉包變量賦值時(shí),都會(huì)讓不同的閉包變量擁有獨(dú)立的"非局部變量"。下面的示例將給出基于閉包的更為通用性的用法:

復(fù)制代碼
 1 do
2 --這里將原有的文件打開(kāi)函數(shù)賦值給"私有變量"oldOpen,該變量在塊外無(wú)法訪問(wèn)。
3 local oldOpen = io.open
4 --新增一個(gè)匿名函數(shù),用于判斷本次文件打開(kāi)操作的合法性。
5 local access_OK = function(filename,mode) <檢查訪問(wèn)權(quán)限> end
6 --將原有的io.open函數(shù)變量指向新的函數(shù),同時(shí)在新函數(shù)中調(diào)用老函數(shù)以完成真正的打開(kāi)操作。
7 io.open = function(filename,mode)
8 if access_OK(filename,mode) then
9 return oldOpen(filename,mode)
10 else
11 return nil,"Access denied"
12 end
13 end
14 end
復(fù)制代碼

    上面的這個(gè)例子有些類似于設(shè)計(jì)模式中裝飾者模式。

    2. 非全局函數(shù):
    從上一小節(jié)中可以看出,Lua中的函數(shù)不僅可以直接賦值給全局變量,同時(shí)也可以賦值給其他類型的變量,如局部變量和table中的字段等。事實(shí)上,Lua庫(kù)中大多數(shù)table都帶有函數(shù),如io.read、math.sin等。這種寫(xiě)法有些類似于C++中的結(jié)構(gòu)體。如:
    Lib = {}
    Lib.add = function(x,y) return x + y end
    Lib.sub = function(x,y) return x - y end
    或者是在table的構(gòu)造式中直接初始化,如:
    Lib = { add = function(x,y) return x + y end, 
               sub = function(x,y) return x - y end
             }
    除此之外,Lua還提供另外一種語(yǔ)法來(lái)定義此類函數(shù),如:
    Lib = {}
    function Lib.add(x,y) return x + y end
    function Lib.sub(x,y) return x - y end
    對(duì)于Lua中的局部函數(shù),其語(yǔ)義在理解上也是非常簡(jiǎn)單的。由于Lua中都是以程序塊作為執(zhí)行單元,因此程序塊內(nèi)的局部函數(shù)在程序塊外是無(wú)法訪問(wèn)的,如:

1 do
2 local f = function(x,y) return x + y end
3 --do something with f.
4 f(4,5)
5 end

    對(duì)于這種局部函數(shù),Lua還提供另外一種更為簡(jiǎn)潔的定義方式,如:
    local function f(x,y) return x + y end
    該寫(xiě)法等價(jià)于:
    local f
    f = function(x,y) return x + y end

    3. 正確的尾調(diào)用:
    在Lua中支持這樣一種函數(shù)調(diào)用的優(yōu)化,即“尾調(diào)用消除”。我們可以將這種函數(shù)調(diào)用方式視為goto語(yǔ)句,如:
    function f(x) return g(x) end
    由于g(x)函數(shù)是f(x)函數(shù)的最后一條語(yǔ)句,在函數(shù)g返回之后,f()函數(shù)將沒(méi)有任何指令需要被執(zhí)行,因此在函數(shù)g()返回時(shí),可以直接返回到f()函數(shù)的調(diào)用點(diǎn)。由此可見(jiàn),Lua解釋器一旦發(fā)現(xiàn)g()函數(shù)是f()函數(shù)的尾調(diào)用,那么在調(diào)用g()時(shí)將不會(huì)產(chǎn)生因函數(shù)調(diào)用而引起的棧開(kāi)銷(xiāo)。這里需要強(qiáng)調(diào)的是,尾調(diào)用函數(shù)一定是其調(diào)用函數(shù)的最后一條語(yǔ)句,否則Lua不會(huì)進(jìn)行優(yōu)化。然而事實(shí)上,我們?cè)诤芏嗫此剖俏舱{(diào)用的場(chǎng)景中,實(shí)際上并不是真正的尾調(diào)用,如:
    function f(x) g(x) end            --沒(méi)有return語(yǔ)句的明確提示
    function f(x) return g(x) + 1  --在g()函數(shù)返回之后仍需執(zhí)行一次加一的指令。
    function f(x) return x or g(x) --如果g()函數(shù)返回多個(gè)值,該操作會(huì)強(qiáng)制要求g()函數(shù)只返回一個(gè)值。
    function f(x) return (g(x))     --原因同上。
    在Lua中,只有"return <func>(<args>)"形式才是標(biāo)準(zhǔn)的尾調(diào)用,至于參數(shù)中(args)是否包含表達(dá)式,由于表達(dá)式的執(zhí)行是在函數(shù)調(diào)用之前完成的,因此不會(huì)影響該函數(shù)成為尾調(diào)用函數(shù)。

posted @ 2014-02-17 17:36 沛沛 閱讀(277) | 評(píng)論 (0)編輯 收藏

一、表達(dá)式:

    1. 算術(shù)操作符:
    Lua支持常規(guī)算術(shù)操作符有:二元的“+”、“-”、“*”、“/”、“^”(指數(shù))、“%”(取模),一元的“-”(負(fù)號(hào))。所有這些操作符都可用于實(shí)數(shù)。然而需要特別說(shuō)明的是取模操作符(%),Lua中對(duì)該操作符的定義為:
    a % b == a - floor(a / b) * b
    由此可以推演出x % 1的結(jié)果為x的小數(shù)部分,而x - x % 1的結(jié)果則為x的整數(shù)部分。類似的,x - x % 0.01則是x精確到小數(shù)點(diǎn)后兩位的結(jié)果。
    
    2. 關(guān)系操作符:
    Lua支持的關(guān)系操作符有:>、<、>=、<=、==、~=,所有這些操作符的結(jié)果均為true或false。
    操作符==用于相等性測(cè)試,操作符~=用于不等性測(cè)試。這兩個(gè)操作符可以應(yīng)用于任意兩個(gè)值。如果兩個(gè)值的類型不同,Lua就認(rèn)為他們不等。nil值與其自身相等。對(duì)于table、userdata和函數(shù),Lua是通過(guò)引用進(jìn)行比較的。也就是說(shuō),只有當(dāng)他們引用同一個(gè)對(duì)象時(shí),才視為相等。如:

復(fù)制代碼
1 a = {}
2 a.x = 1
3 a.y = 0
4 b = {}
5 b.x = 1
6 b.y = 1
7 c = a
復(fù)制代碼

    其結(jié)果是a == c,但a ~= b。
    對(duì)于字符串的比較,Lua是按照字符次序比較的。
    
    3. 邏輯操作符:
    Lua支持的邏輯操作符有:and、or和not。與條件控制語(yǔ)句一樣,所有的邏輯操作符都將false和nil視為假,其他的結(jié)果均為真。和其他大多數(shù)語(yǔ)言一樣,Lua中的and和or都使用“短路原則”。在Lua中有一種慣用寫(xiě)法"x = x or v",它等價(jià)于:if not x then x = v end。這里還有一種基于“短路原則”的慣用寫(xiě)法,如:
    max = (x > y) and x or y
    這等價(jià)于C語(yǔ)言中max = (x > y) ? x : y。由于x和y均為數(shù)值,因此它們的結(jié)果將始終為true。
    
    4. 字符串連接:
    前一篇Blog已經(jīng)提到了字符串連接操作符(..),這里再給出一些簡(jiǎn)單的示例。
    /> lua
    > print("Hello " .. "World)
    Hello World
    > print(0 .. 1)  --即使連接操作符的操作數(shù)為數(shù)值類型,在執(zhí)行時(shí)Lua仍會(huì)將其自動(dòng)轉(zhuǎn)換為字符串。
    01

    5. table構(gòu)造器:
    構(gòu)造器用于構(gòu)建和初始化table的表達(dá)式。這是Lua特有的表達(dá)式,也是Lua中最有用、最通用的機(jī)制之一。其中最簡(jiǎn)單的構(gòu)造器是空構(gòu)造器{},用于創(chuàng)建空table。我們通過(guò)構(gòu)造器還可以初始化數(shù)組,如:

復(fù)制代碼
 1 days = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
2 for i = 1,#days do
3 print(days[i])
4 end
5 --輸出結(jié)果為
6 --Sunday
7 --Monday
8 --Tuesday
9 --Wednesday
10 --Thursday
11 --Friday
12 --Saturday
復(fù)制代碼

    從輸出結(jié)果可以看出,days在構(gòu)造后會(huì)將自動(dòng)初始化,其中days[1]被初始化為"Sunday",days[2]為"Monday",以此類推。
    Lua中還提供了另外一種特殊的語(yǔ)法用于初始化記錄風(fēng)格的table。如:a = { x = 10, y = 20 },其等價(jià)于:a = {}; a.x = 10; a.y = 20
    在實(shí)際編程時(shí)我們也可以將這兩種初始化方式組合在一起使用,如:

復(fù)制代碼
polyline = {color = "blue", thickness = 2, npoints = 4, 
{x = 0, y = 0},
{x = 10, y = 0},
{x = -10, y = 1},
{x = 0, y = 1} }
print(polyline["color"]);
print(polyline[2].x)
print(polyline[4].y)
--輸出結(jié)果如下:
--
blue
--
10
--
1
復(fù)制代碼

    除了以上兩種構(gòu)造初始化方式之外,Lua還提供另外一種更為通用的方式,如:

1 opnames = { ["+"] = "add", ["-"] = "sub", ["*"] = "mul", ["/"] = "div"}
2 print(opnames["+"])
3 i = 20; s = "-"
4 a = { [i + 0] = s, [i + 1] = s .. s, [i + 2] = s..s..s }
5 print(a[22])

    對(duì)于table的構(gòu)造器,還有兩個(gè)需要了解的語(yǔ)法規(guī)則,如:
    a = { [1] = "red", [2] = "green", [3] = "blue", }  
    這里需要注意最后一個(gè)元素的后面仍然可以保留逗號(hào)(,),這一點(diǎn)類似于C語(yǔ)言中的枚舉。
    a = {x = 10, y = 45; "one", "two", "three" }
    可以看到上面的聲明中同時(shí)存在逗號(hào)(,)和分號(hào)(;)兩種元素分隔符,這種寫(xiě)法在Lua中是允許的。我們通常會(huì)將分號(hào)(;)用于分隔不同初始化類型的元素,如上例中分號(hào)之前的初始化方式為記錄初始化方式,而后面則是數(shù)組初始化方式。

二、語(yǔ)句:

    1. 賦值語(yǔ)句:
    Lua中的賦值語(yǔ)句和其它編程語(yǔ)言基本相同,唯一的差別是Lua支持“多重賦值”,如:a, b = 10, 2 * x,其等價(jià)于a = 10; b = 2 * x。然而需要說(shuō)明的是,Lua在賦值之前需要先計(jì)算等號(hào)右邊的表達(dá)式,在每一個(gè)表達(dá)式都得到結(jié)果之后再進(jìn)行賦值。因此,我們可以這樣寫(xiě)變量交互:x,y = y,x。如果等號(hào)右側(cè)的表達(dá)式數(shù)量少于左側(cè)變量的數(shù)量,Lua會(huì)將左側(cè)多出的變量的值置為nil,如果相反,Lua將忽略右側(cè)多出的表達(dá)式。

    2. 局部變量與塊:
    Lua中的局部變量定義語(yǔ)法為:local i = 1,其中l(wèi)ocal關(guān)鍵字表示該變量為局部變量。和全局變量不同的是,局部變量的作用范圍僅限于其所在的程序塊。Lua中的程序可以為控制結(jié)構(gòu)的執(zhí)行體、函數(shù)執(zhí)行體或者是一個(gè)程序塊,如:
    下面的x變量?jī)H在while循環(huán)內(nèi)有效。

1 while i <= x do
2 local x = i * 2
3 print(x)
4 i = i + 1
5 end

    如果是在交互模式下,當(dāng)執(zhí)行l(wèi)ocal x = 0之后,該變量x所在的程序即以結(jié)束,后面的Lua語(yǔ)句將被視為新的程序塊。如果想避免此類問(wèn)題,我們可以顯式的聲明程序塊,這樣即便是在交互模式下,局部變量仍然能保持其塊內(nèi)有效性,如:

1 do
2 local a2 = 2 * a
3 local d = (b ^ 2 - 4 * a) ^ (1 / 2)
4 x1 = (-b + d) / a2
5 x2 = (-b - d) / a2
6 end --a2和d的作用域至此結(jié)束。

    和其它編程語(yǔ)言一樣,如果有可能盡量使用局部變量,以免造成全局環(huán)境的變量名污染。同時(shí)由于局部變量的有效期更短,這樣垃圾收集器可以及時(shí)對(duì)其進(jìn)行清理,從而得到更多的可用內(nèi)存。    

    3. 控制結(jié)構(gòu):
    Lua中提供的控制語(yǔ)句和其它大多數(shù)開(kāi)發(fā)語(yǔ)言所提供的基本相同,因此這里僅僅是進(jìn)行簡(jiǎn)單的列舉。然后再給出差異部分的詳細(xì)介紹。如:
    1). if then else
    if a < 0 then 
        b = 0
    else
        b = 1
    end
    
    2). if elseif else then
    if a < 0 then 
        b = 0
    elseif a == 0 then
        b = 1
    else
        b = 2
    end
    
    3). while
    local i= 1
    while a[i] do
        print(a[i])
        i = i + 1
    end
    
    4). repeat
    repeat
        line = io.read()
    until line ~= "" --直到until的條件為真時(shí)結(jié)束。
    print(line)
    
    5). for
    for var = begin, end, step do --如果沒(méi)有step變量,begin的缺省步長(zhǎng)為1。
        i = i + 1
    end
    需要說(shuō)明的是,for循環(huán)開(kāi)始處的三個(gè)變量begin、end和step,如果它們使表達(dá)式的返回值,那么該表達(dá)式將僅執(zhí)行一次。再有就是不要在for的循環(huán)體內(nèi)修改變量var的值,否則會(huì)導(dǎo)致不可預(yù)知的結(jié)果。
    
    6). foreach
    for i, v in ipairs(a) do  --ipairs是Lua自帶的系統(tǒng)函數(shù),返回遍歷數(shù)組的迭代器。
        print(v)
    end
    
    for k in pairs(t) do      --打印table t中的所有key。
        print(k)
    end
    見(jiàn)如下示例代碼:

復(fù)制代碼
 1 days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }
2 revDays = {}
3 for k, v in ipairs(days) do
4 revDays[v] = k
5 end
6
7 for k in pairs(revDays) do
8 print(k .. " = " .. revDays[k])
9 end
10
11 --輸出結(jié)果為:
12 --Saturday = 7
13 --Tuesday = 3
14 --Wednesday = 4
15 --Friday = 6
16 --Sunday = 1
17 --Thursday = 5
18 --Monday = 2
復(fù)制代碼

    7). break
    和C語(yǔ)言中的break語(yǔ)義完全相同,即跳出最內(nèi)層循環(huán)。

posted @ 2014-02-17 17:35 沛沛 閱讀(231) | 評(píng)論 (0)編輯 收藏

一、基礎(chǔ)知識(shí):

    1. 第一個(gè)程序和函數(shù):
    在目前這個(gè)學(xué)習(xí)階段,運(yùn)行Lua程序最好的方式就是通過(guò)Lua自帶的解釋器程序,如:
    /> lua
    > print("Hello World")
    Hello World
    這樣我們就可以以交互性的方式輸入lua代碼,并立即得到執(zhí)行結(jié)果了。對(duì)于代碼塊較少的測(cè)試程序來(lái)說(shuō),這種方式確實(shí)是非常方便的,然而對(duì)于相對(duì)復(fù)雜的程序而言,這種方式就不是很合適了。如果是這樣,我們可以將Lua代碼保存到一個(gè)獨(dú)立的Lua程序文件中,之后再通過(guò)Lua解釋器程序以命令行參數(shù)的形式執(zhí)行文件中的Lua代碼。如我們將下面的Lua代碼保存到test.lua的文件中:

復(fù)制代碼
 1 function fact(n)
2 if n == 0 then
3 return 1
4 else
5 return n * fact(n - 1)
6 end
7 end
8 print("Enter a number:")
9 a = io.read("*number")
10 print(fact(a))
復(fù)制代碼

    /> lua D:/test.lua
    Enter a number:
    4
    24

    2. 代碼規(guī)范:
    1). Lua的多條語(yǔ)句之間并不要求任何分隔符,如C語(yǔ)言的分號(hào)(;),其中換行符也同樣不能起到語(yǔ)句分隔的作用。因此下面的寫(xiě)法均是合法的。如:

復(fù)制代碼
1 a = 1
2 b = a * 2
3
4 a = 1;
5 b = a * 2;
6
7 a = 1; b = a * 2;
8 a = 1 b = a * 2
復(fù)制代碼

    2). 通過(guò)dofile()方法引用其他Lua文件中的函數(shù),如:

復(fù)制代碼
1 function fact(n)
2 if n == 0 then
3 return 1
4 else
5 return n * fact(n - 1)
6 end
7 end
復(fù)制代碼

    將上面的函數(shù)保存到test2.lua文件中。
    /> lua
    > dofile("d:/test2.lua")
    > print(fact(4))
    24
    3). 詞法規(guī)范。
    和大多數(shù)其它語(yǔ)言一樣,在聲明變量時(shí),變量名可以由任意字母、數(shù)字和下劃線構(gòu)成,但是不能以數(shù)字開(kāi)頭。在Lua中還有一個(gè)特殊的規(guī)則,即以下劃線(_)開(kāi)頭,后面緊隨多個(gè)大寫(xiě)字母(_VERSION),這些變量一般被Lua保留并用于特殊用途,因此我們?cè)诼暶髯兞繒r(shí)需要盡量避免這樣的聲明方式,以免給后期的維護(hù)帶來(lái)不必要的麻煩。
    Lua是大小寫(xiě)敏感的,因此對(duì)于一些Lua保留關(guān)鍵字的使用要特別小心,如and。但是And和AND則不是Lua的保留字。
    4). Lua中的注釋分為兩種,一種是單行注釋,如:
    --This is a single line comment.
    另外一種是多行注釋,如:
    --[[
    This is a multi-lines comment.
    --]]

    3. 全局變量:
    在Lua中全局變量不需要聲明,直接賦值即可。如果直接訪問(wèn)未初始化的全局變量,Lua也不會(huì)報(bào)錯(cuò),直接返回nil。如果不想再使用該全局變量,可直接將其置為nil。如:
    /> lua
    > print(b)
    nil
    > b = 10
    > print(b)
    10
    > b = nil
    > print(b)
    nil
    
    4. 解釋器程序:
    命令行用法如下:
    lua [options] [lua-script [arguments] ]
    該工具的命令行選項(xiàng)主要有以下3個(gè):
    -e: 可以直接執(zhí)行命令行中Lua代碼,如:lua -e "print(\"Hello World\")"
    -l: 加載該選項(xiàng)后的Lua庫(kù)文件,如:lua -l mylib -e "x = 10",該命令在執(zhí)行之前先將mylib中的Lua代碼加載到內(nèi)存中,在后面的命令中就可以直接使用該文件中定義的Lua函數(shù)了。
    -i: 在執(zhí)行完指定的Lua程序文件之后,并不退出解釋器程序,而是直接進(jìn)入該程序的交互模式。    
    在解釋器程序的交互模式下,我們可以通過(guò)在表達(dá)式前加等號(hào)(=)標(biāo)識(shí)符的方式直接輸出表達(dá)式的執(zhí)行結(jié)果。通過(guò)該方式,我們可以將該程序用于計(jì)算器,如:
    /> lua
    > = 3 + 1 + 4
    8
    該小節(jié)最后需要介紹的是lua腳本的命令行參數(shù)訪問(wèn)規(guī)則。如:
    /> lua lua-script.lua a b c
    在該腳本的程序入口,lua解釋器會(huì)將所有命令行參數(shù)創(chuàng)建一個(gè)名為arg的table。其中腳本名(lua-script.lua)位于table索引的0位置上。它的第一個(gè)參數(shù)(a)則位于索引1,其它的參數(shù)以此類推。這種索引方式和C語(yǔ)言中讀取命令行參數(shù)的規(guī)則相同。但是不同的是,Lua提供了負(fù)數(shù)索引,用以訪問(wèn)腳本名稱之前的命令行參數(shù),如:
    arg[-1] = lua
    arg[0] = lua-script.lua
    arg[1] = a
    arg[2] = b
    arg[3] = c

二、類型與值:

    Lua是一種動(dòng)態(tài)類型的語(yǔ)言。其語(yǔ)言本身沒(méi)有提供類型定義的語(yǔ)法,每個(gè)值都“攜帶”了它自身的類型信息。在Lua中有8中基礎(chǔ)類型,分別是:nil、boolean、number、string、userdata、function、thread和table。我們可以通過(guò)type函數(shù)獲得變量的類型信息,該類型信息將以字符串的形式返回。如:
    > print(type("hello world"))
    string
    > print(type(10.4))
    number
    > print(type(print))
    function
    > print(type(true))
    boolean
    > print(type(nil))
    nil
    > print(type(type(X)))
    string

    1. nil(空):
    nil是一種類型,它只有一個(gè)值nil,它的主要功能是由于區(qū)別其他任何值。就像之前所說(shuō)的,一個(gè)全局變量在第一次賦值前的默認(rèn)值的默認(rèn)值就是nil,將nil賦予一個(gè)全局變量等同于刪除它。Lua將nil用于表示一種“無(wú)效值”的情況。
    
    2. boolean(布爾):
    該類型有兩個(gè)可選值:false和true。在Lua中只有當(dāng)值是false和nil時(shí)才視為“假”,其它值均視為真,如數(shù)字零和空字符串,這一點(diǎn)和C語(yǔ)言是不同的。
    
    3. number(數(shù)字):
    Lua中的number用于表示實(shí)數(shù)。Lua中沒(méi)有專門(mén)的類型表示整數(shù)。
    
    4. string(字符串):
    Lua中的字符串通常表示“一個(gè)字符序列”。字符串類型的變量是不可變的,因此不能像C語(yǔ)言中那樣直接修改字符串的某一個(gè)字符,而是在修改的同時(shí)創(chuàng)建了新的字符串。如:

1 a = "one string"
2 b = string.gsub(a,"one","another")
3 print(a)
4 print(b)

    /> lua d:/test.lua    
    one string
    anotner string
    Lua支持和C語(yǔ)言類似的字符轉(zhuǎn)義序列,見(jiàn)下表:

轉(zhuǎn)義符描述
\a響鈴
\b退格
\n換行
\r回車(chē)
\t水平Tab
\\反斜杠
\"雙引號(hào)
\'單引號(hào)

    在Lua中還可以通過(guò)[[ all strings ]]的方式來(lái)禁用[[ ]]中轉(zhuǎn)義字符,如:
    page = [[ <html> <head> <title> An Html Page </title> </head> ]]
    如果兩個(gè)方括號(hào)中包含這樣的內(nèi)容:a = b[c[i]],這樣將會(huì)導(dǎo)致Lua的誤解析,因此在這種情況下,我們可以將其改為[===[ 和 ]===]的形式,從而避免了誤解析的發(fā)生。
    Lua提供了運(yùn)行時(shí)的數(shù)字與字符串的自動(dòng)轉(zhuǎn)換。如:
    > print("10" + 1)
    11
    > print("10 + 1")
    10 + 1
    如果在實(shí)際編程中,不希望兩個(gè)數(shù)字字符串被自動(dòng)轉(zhuǎn)換,而是實(shí)現(xiàn)字符串之間的連接,可以通過(guò)" .. "操作符來(lái)完成。如:
    > print(10 .. 20)
    1020
    注意..和兩邊的數(shù)字之間必須留有空格,否則就會(huì)被Lua誤解析為小數(shù)點(diǎn)兒。
    盡管Lua提供了這種自動(dòng)轉(zhuǎn)換的功能,為了避免一些不可預(yù)測(cè)的行為發(fā)生,特別是因?yàn)長(zhǎng)ua版本升級(jí)而導(dǎo)致的行為不一致現(xiàn)象。鑒于此,還是應(yīng)該盡可能使用顯示的轉(zhuǎn)換,如字符串轉(zhuǎn)數(shù)字的函數(shù)tonumber(),或者是數(shù)字轉(zhuǎn)字符串的函數(shù)tostring()。對(duì)于前者,如果函數(shù)參數(shù)不能轉(zhuǎn)換為數(shù)字,該函數(shù)返回nil。如:

復(fù)制代碼
1 line = io.read()
2 n = tonumber(line)
3 if n == nil then
4 error(line .. " is not a valid number")
5 else
6 print(n * 2)
7 end
復(fù)制代碼

    關(guān)于Lua的字符串最后需要介紹的是"#"標(biāo)識(shí)符,該標(biāo)識(shí)符在字符串變量的前面將返回其后字符串的長(zhǎng)度,如:

1 a = "hello"
2 print(#a)

    /> lua d:/test.lua
    5
        
    5. table(表):
    我們可以將Lua中table類型視為“關(guān)聯(lián)數(shù)組”,如C++標(biāo)準(zhǔn)庫(kù)中的map,差別是Lua中table的鍵(key)可以為任意類型(nil除外),而map中的鍵只能為模參類型。此外,table沒(méi)有固定的大小,可以動(dòng)態(tài)的添加任意數(shù)量的元素到一個(gè)table中。table是Lua中最主要數(shù)據(jù)結(jié)構(gòu),其功能非常強(qiáng)大,可用于實(shí)現(xiàn)數(shù)組、集合、記錄和隊(duì)列數(shù)據(jù)結(jié)構(gòu)。以下為table的變量聲明,以及關(guān)聯(lián)數(shù)據(jù)的初始化方式:

復(fù)制代碼
1 a = {}              -- 創(chuàng)建一個(gè)table對(duì)象,并將它的引用存儲(chǔ)到a
2 k = "x"
3 a[k] = 10 -- 創(chuàng)建了新條目,key = "x", value = 10
4 a[20] = "great" -- 新條目,key = 20, value = "great"
5 print(a["x"])
6 k = 20
7 print(a[k]) -- 打印great
8 a["x"] = a["x"] + 1
9 print(a["x"]) -- 打印11
復(fù)制代碼

    所有的table都可以用不同類型的索引來(lái)訪問(wèn)value,當(dāng)需要容納新條目時(shí),table會(huì)自動(dòng)增長(zhǎng)。

復(fù)制代碼
 1 a = {}
2 for i = 1, 100 do
3 a[i] = i * 2
4 end
5 print(a[9])
6 a["x"] = 10
7 print(a["x"])
8 print(a["y"]) --table中的變量和全局變量一樣,沒(méi)有賦值之前均為nil。
9
10 --輸出結(jié)果為
11 --18
12 --10
13 --nil
復(fù)制代碼

    在Lua中還提供了另外一種方法用于訪問(wèn)table中的值,見(jiàn)如下示例:

1 a.x = 10      --等同于a["x"] = 10
2 print(a.x) --等同于print(a["x"])
3 print(a.y) --等同于print(a["y"])

    對(duì)于Lua來(lái)說(shuō),這兩種方式是等價(jià)的。但是對(duì)于開(kāi)發(fā)者而言,點(diǎn)的寫(xiě)法隱式的將table表示為記錄,既C語(yǔ)言中的結(jié)構(gòu)體。而之前講述的字符串表示法則意味著任何字符串均可作為table的key。
    如果需要將table表示為傳統(tǒng)的數(shù)組,只需將整數(shù)作為table的key即可。如:

復(fù)制代碼
1 a = {}
2 for i = 1,10 do
3 a[i] = i * 2
4 end
5
6 for i = 1,10 do
7 print(a[i])
8 end
復(fù)制代碼

    在Lua中,我通常習(xí)慣以1作為數(shù)組索引的起始值。而且還有不少內(nèi)部機(jī)制依賴于這個(gè)慣例。如:

復(fù)制代碼
1 a = {}
2 for i = 1,10 do
3 a[i] = i * 2
4 end
5
6 for i = 1,#a do
7 print(a[i])
8 end
復(fù)制代碼

    由于數(shù)組實(shí)際上仍為一個(gè)table,所以對(duì)于數(shù)組大小的計(jì)算需要留意某些特殊的場(chǎng)景,如:
    a = {}
    a[1000] = 1
    在上面的示例中,數(shù)組a中索引值為1--999的元素的值均為nil。而Lua則將nil作為界定數(shù)據(jù)結(jié)尾的標(biāo)志。當(dāng)一個(gè)數(shù)組含有“空隙”時(shí),即中間含有nil值,長(zhǎng)度操作符#會(huì)認(rèn)為這些nil元素就是結(jié)尾標(biāo)志。當(dāng)然這肯定不是我們想要的結(jié)果。因此對(duì)于這些含有“空隙”的數(shù)組,我們可以通過(guò)函數(shù)table.maxn()返回table的最大正數(shù)索引值。如:

1 a = {}
2 a[1000] = 1
3 print(table.maxn(a))
4
5 -- 輸出1000


    6. function(函數(shù)):
    在Lua中,函數(shù)可以存儲(chǔ)在變量中,可以通過(guò)參數(shù)傳遞其它函數(shù),還可以作為其它函數(shù)的返回值。這種特性使語(yǔ)言具有了極大的靈活性。

    7. userdata(自定義類型):
    由于userdata類型可以將任意C語(yǔ)言數(shù)據(jù)存儲(chǔ)到Lua變量中。在Lua中,這種類型沒(méi)有太多預(yù)定義的操作,只能進(jìn)行賦值和相等性測(cè)試。userdata用于表示一種由應(yīng)用程序或C語(yǔ)言庫(kù)所創(chuàng)建的新類型。

posted @ 2014-02-17 17:33 沛沛 閱讀(242) | 評(píng)論 (0)編輯 收藏

     摘要: 概述——什么是makefile?或許很多Winodws的程序員都不知道這個(gè)東西,因?yàn)槟切¦indows的IDE都為你做了這個(gè)工作,但我覺(jué)得要作一個(gè)好的和professional的程序員,makefile還是要懂。這就好像現(xiàn)在有這么多的HTML的編輯器,但如果你想成為一個(gè)專業(yè)人士,你還是要了解HTML的標(biāo)識(shí)的含義。特別在Unix下的軟件編譯,你就不能不自己寫(xiě)makefile了...  閱讀全文
posted @ 2014-02-17 17:30 沛沛 閱讀(252) | 評(píng)論 (0)編輯 收藏

上午一個(gè)師弟在QQ上問(wèn)我一道筆試題,是他前兩天去KONAMI面試時(shí)做的,這道題大致是這樣的:
        解釋以下語(yǔ)句的含義:
         1、new A;
         2、new A();   
 
   也許很多人包括我自己,都可以馬上給出第一種情況的答案:在堆上為A類分配內(nèi)存,然后調(diào)用A的構(gòu)造函數(shù)。這種說(shuō)法被大家所熟知,因?yàn)榘ā禨TL源碼剖析》等大作在內(nèi)也都是這么寫(xiě)的(但是你認(rèn)為這種說(shuō)法完全正確嗎?其實(shí)不盡然,答案后面揭曉)
    第二種情況,對(duì)象構(gòu)造的時(shí)候初始化列表為空會(huì)和第一種有什么不同呢?對(duì)于這種在實(shí)際工程中很少使用的情況,我一時(shí)還真給不出確切的答案。
   網(wǎng)上搜了一下,看到CSDN里面還有專門(mén)針對(duì)這個(gè)問(wèn)題的一個(gè)帖子(原帖鏈接 http://bbs.csdn.net/topics/320161716)。
   好像最終也沒(méi)有可以信服的答案,認(rèn)同度比較高的是這樣的說(shuō)法:“加括號(hào)調(diào)用沒(méi)有參數(shù)的構(gòu)造函數(shù),不加括號(hào)調(diào)用默認(rèn)構(gòu)造函數(shù)或唯一的構(gòu)造函數(shù),看需求” (peakflys注:這種說(shuō)法是錯(cuò)誤的,答案后面揭曉)
   既然沒(méi)有特別靠譜的答案,不如自己動(dòng)手找出答案。
   構(gòu)造以下示例:
/**
 *\brief example1 difference between new and new()
 *\author peakflys
 *\data 12:10:24 Monday, April 08, 2013
 
*/

class A
{
public:
    int a;
};

int main()
{
    A *pa = new A;
    A *paa = new A();
    return 0;
}
查看main函數(shù)的匯編代碼(編譯器:gcc (GCC) 4.4.6 20120305 (Red Hat 4.4.6-4) )
int main()
{
  4005c4:   55                      push   %rbp
  4005c5:   48 89 e5                mov    %rsp,%rbp
  4005c8:   48 83 ec 10             sub    $0x10,%rsp
    A *pa = new A;
  4005cc:   bf 04 00 00 00          mov    $0x4,%edi
  4005d1:   e8 f2 fe ff ff          callq  4004c8 <_Znwm@plt>         //調(diào)用new
  4005d6:   48 89 45 f0             mov    %rax,-0x10(%rbp)           //rax寄存器內(nèi)容賦給指針pa(rax寄存器里是new調(diào)用產(chǎn)生的A對(duì)象堆內(nèi)存地址)
    A *paa = new A();
  4005da:   bf 04 00 00 00          mov    $0x4,%edi
  4005df:   e8 e4 fe ff ff          callq  4004c8 <_Znwm@plt>         //調(diào)用new
  4005e4:   48 89 c2                mov    %rax,%rdx                      //rax的內(nèi)容放入rdx,執(zhí)行之后,rdx里存放的即是通過(guò)new A()產(chǎn)生的內(nèi)存地址
  4005e7:   c7 02 00 00 00 00       movl   $0x0,(%rdx)                 //把rdx內(nèi)存指向的內(nèi)容賦為0值,即把A::a賦值為0
  4005ed:   48 89 45 f8             mov    %rax,-0x8(%rbp)             //rax寄存器內(nèi)容賦給指針paa(rax寄存器里是new()調(diào)用產(chǎn)生的A對(duì)象堆內(nèi)存地址)
     return 0;
  4005f1:   b8 00 00 00 00          mov    $0x0,%eax
}
  4005f6:   c9                      leaveq 
  4005f7:   c3                      retq
    通過(guò)上面產(chǎn)生的匯編代碼(對(duì)AT&T匯編不熟悉的可以看注釋)可以很容易看出,new A()的執(zhí)行,在調(diào)用完operator new分配內(nèi)存后,馬上對(duì)新分配內(nèi)存中的對(duì)象使用0值初始化,而new A 僅僅是調(diào)用了operator new分配內(nèi)存!
   是不是這樣就可以下結(jié)論 new A()比new A多了一步,即初始化對(duì)象的步驟呢?
   我們?cè)倏纯聪旅孢@種情況:
/**
 *\brief example2 difference between new and new()
 *\author peakflys
 *\data 12:23:20 Monday, April 08, 2013
 
*/

class A
{
public:
    A(){a = 10;}
    int a;
};

int main()
{
    A *pa = new A;
    A *paa = new A();
    return 0;
}
   這種情況是類顯示提供含默認(rèn)值的構(gòu)造函數(shù)。
   查看匯編實(shí)現(xiàn)如下:
int main()
{
  4005c4:   55                      push   %rbp
  4005c5:   48 89 e5                mov    %rsp,%rbp
  4005c8:   53                      push   %rbx
  4005c9:   48 83 ec 18             sub    $0x18,%rsp
    A *pa = new A;
  4005cd:   bf 04 00 00 00          mov    $0x4,%edi
  4005d2:   e8 f1 fe ff ff          callq  4004c8 <_Znwm@plt>
  4005d7:   48 89 c3                mov    %rax,%rbx
  4005da:   48 89 d8                mov    %rbx,%rax
  4005dd:   48 89 c7                mov    %rax,%rdi
  4005e0:   e8 2d 00 00 00          callq  400612 <_ZN1AC1Ev>
  4005e5:   48 89 5d e0             mov    %rbx,-0x20(%rbp)
    A *paa = new A();
  4005e9:   bf 04 00 00 00          mov    $0x4,%edi
  4005ee:   e8 d5 fe ff ff          callq  4004c8 <_Znwm@plt>
  4005f3:   48 89 c3                mov    %rax,%rbx
  4005f6:   48 89 d8                mov    %rbx,%rax
  4005f9:   48 89 c7                mov    %rax,%rdi
  4005fc:   e8 11 00 00 00          callq  400612 <_ZN1AC1Ev>
  400601:   48 89 5d e8             mov    %rbx,-0x18(%rbp)
    return 0;
  400605:   b8 00 00 00 00          mov    $0x0,%eax
}
  40060a:   48 83 c4 18             add    $0x18,%rsp
  40060e:   5b                      pop    %rbx
  40060f:   c9                      leaveq 
  400610:   c3                      retq 
   上面的匯編代碼就不在添加注釋了,因?yàn)閮煞N操作產(chǎn)生的匯編代碼是一樣的,都是先調(diào)用operator new分配內(nèi)存,然后調(diào)用構(gòu)造函數(shù)。
   上面的情況在VS2010下驗(yàn)證是一樣的情況,有興趣的朋友可以自己去看,這里就不再貼出VS2010下的匯編代碼了。
   通過(guò)上面的分析,對(duì)于new A和 new A() 的區(qū)別,我們可以得出下面的結(jié)論:
      1、類體含有顯示適合地默認(rèn)構(gòu)造函數(shù)時(shí),new A和new A()的作用一致,都是首先調(diào)用operator new分配內(nèi)存,然后調(diào)用默認(rèn)構(gòu)造函數(shù)初始化對(duì)象。
      2、類體無(wú)顯示構(gòu)造函數(shù)時(shí),new A()首先調(diào)用operator new來(lái)為對(duì)象分配內(nèi)存,然后使用空值初始化對(duì)象成員變量,而new A僅僅是調(diào)用operator new分配內(nèi)存,對(duì)象的成員變量是無(wú)意義的隨機(jī)值!  (peakflys注:對(duì)于基本數(shù)據(jù)類型,如int等 適用此條)
   注意到,現(xiàn)在很多書(shū)籍對(duì)new操作符的說(shuō)明都存在紕漏,例如《STL源碼剖析》中2.2.2節(jié)中有以下的描述:

事實(shí)證明,new Foo的操作是否有構(gòu)造函數(shù)的調(diào)用是不確定的,具體要看Foo類體里是否有顯示構(gòu)造函數(shù)的出現(xiàn)。

                                                                                                by peakflys 13:40:00 Monday, April 08, 2013

/*****************************************華麗分割線**************************************
補(bǔ)充:剛才發(fā)現(xiàn),在C++Primer第四版5.11節(jié)中,已經(jīng)有了對(duì)于new A()的說(shuō)明:
   We indicate that we want to value-initialize the newly allocated object by following the type nameby a pair of empty parentheses. The empty parentheses signal that we want initialization but arenot supplying a specific initial value. In the case of class types (such as string) that define their own constructors, requesting value-initialization is of no consequence: The object is initialized by running the default constructor whether we leave it apparently uninitialized orask for value-initialization. In the case of built-in types or types that do not define any constructors, the difference is significant:
     int *pi = new int;         // pi points to an uninitialized int 
     int *pi = new int();       // pi points to an int value-initialized to 0 
In the first case, the int is uninitialized; in the second case, the int is initialized to zero.
   這里給出的解釋和上面自己分析的new A()的行為是一致的。
/***************************************再次華麗分割線************************************
鑒于上面的結(jié)論是通過(guò)GCC和VS2010得出的,而且有朋友也提出同樣的質(zhì)疑,為了確定這種結(jié)果是否是編譯器相關(guān)的,剛才特意查看了一下C++的標(biāo)準(zhǔn)化文檔。
摘自:ISO/IEC 14882:2003(E) 5.3.4 - 15
— If the new-initializer is omitted:
      — If T is a (possibly cv-qualified) non-POD class type (or array thereof), the object is default-initialized(8.5). If T is a const-qualified type, the underlying class type shall have a user-declared default constructor.
      — Otherwise, the object created has indeterminate value. If T is a const-qualified type, or a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of const-qualified type, the program is ill-formed;
— If the new-initializer is of the form (), the item is value-initialized (8.5);
所以可以確定,這種情況完全是編譯器無(wú)關(guān)的(當(dāng)然那些不完全按照標(biāo)準(zhǔn)實(shí)現(xiàn)的編譯器除外)。
但是通過(guò)上面標(biāo)準(zhǔn)化文檔的描述,我們可以看出文中對(duì)new A在無(wú)顯示構(gòu)造函數(shù)時(shí)的總結(jié)并不是特別準(zhǔn)確,鑒于很多公司都有這道面試題(撇去這些題目的實(shí)際考察意義不說(shuō)),我們有必要再補(bǔ)充一下:   對(duì)于new A: 這樣的語(yǔ)句,再調(diào)用完operator new分配內(nèi)存之后,如果A類體內(nèi)含有POD類型,則POD類型的成員變量處于未定義狀態(tài),如果含有非POD類型則調(diào)用該類型的默認(rèn)構(gòu)造函數(shù)。而 new A()在這些情況下都會(huì)初始化。
posted @ 2013-04-14 00:51 沛沛 閱讀(397) | 評(píng)論 (0)編輯 收藏

     摘要: 作者:fengge8ylf  博客:http://blog.csdn.net/fengge8ylf 對(duì)于基于TCP開(kāi)發(fā)的通訊程序,有個(gè)很重要的問(wèn)題需要解決,就是封包和拆包.自從我從事網(wǎng)絡(luò)通訊編程工作以來(lái)(大概有三年的時(shí)間了),我一直在思索和改進(jìn)封包和拆包的方法.下面就針對(duì)這個(gè)問(wèn)題談?wù)勎业南敕?拋磚引玉.若有不對(duì),不妥之處,懇求大家指正.在此先謝過(guò)大家了. 一.為什么基于TCP的通訊程...  閱讀全文
posted @ 2011-05-13 13:26 沛沛 閱讀(887) | 評(píng)論 (1)編輯 收藏

關(guān)于低耦合的消息傳遞,實(shí)現(xiàn)的方式有很多,哪種方法更好與具體的使用環(huán)境有關(guān),本文使用試錯(cuò)的方法,逐步探索達(dá)成這一目的具體方式,并理解實(shí)現(xiàn)方式背后的原因。

面向?qū)ο蟮南到y(tǒng)當(dāng)中,不可避免的有大量的類間消息傳遞的需求:一個(gè)類需要通知另一個(gè)或幾個(gè)類做些什么。

這種類間消息傳遞,簡(jiǎn)單的說(shuō),就是調(diào)用其他類的方法。

如下:

1void A::OnMessageXX()
2{
3         B::GetInstance()->DoSomething();
4
5}

6
7

 

在這里,類A需要通知類B做些事情。這種調(diào)用在所有的面向?qū)ο蟪绦蛑卸际菢O其常見(jiàn)的。

但是如果類A需要調(diào)用類B,就不可避免的產(chǎn)生了耦合性。雖然耦合性終歸是不可能完全避免的,但是在一定程度上降低耦合性是完全可能的。

(至于為什么在設(shè)計(jì)中應(yīng)該盡可能降低耦合性,不在本文的探討范圍之內(nèi))

上面的例子,我們使用了Singleton的模式,從全局作用域中獲取了B的實(shí)例,并調(diào)用了B的相關(guān)方法。使用Singleton的一個(gè)缺點(diǎn)是,假若我們希望對(duì)類A編寫(xiě)測(cè)試代碼,我們需要做一些額外的解耦合工作。(關(guān)于編寫(xiě)測(cè)試與解耦合,可以參考Robert C. Martin Series 的Working Effectively with Legacy Code一書(shū),該書(shū)的中譯版在這 )

我們也可以通過(guò)將B參數(shù)化的方法降低A與B間的耦合程度,像下面這樣:

1 void A::OnMessageXX(B* pBInstance)
2 {
3          pBInstance->DoSomething();
4 
5 }
6 
7 

 

現(xiàn)在的寫(xiě)法要比之前的做法耦合性低,通過(guò)使用多態(tài)的方法,現(xiàn)在傳入函數(shù)的類B指針可能是另一個(gè)實(shí)現(xiàn)了B的相應(yīng)接口的派生類,A并不關(guān)心B接口背后的具體實(shí)現(xiàn)。

但是等等,你說(shuō),現(xiàn)在對(duì)類B的耦合性雖然在A中被降低了,但是依舊存在于調(diào)用A::OnMessageXX的地方。在那里我們還是需要取得B的實(shí)例,然后傳遞給A。

沒(méi)錯(cuò),是這樣。

通過(guò)參數(shù)化類A的方法,我們把類A與類B間的耦合轉(zhuǎn)移了一部分到A的調(diào)用者那里。實(shí)際上總的耦合并沒(méi)有消除,只是被分解了。但是程序設(shè)計(jì)中不可能完全不存在耦合,我們需要做的是”正確”,而不是”完美”。類A的耦合性降低了,使得我們?cè)谖磥?lái)需求變更的時(shí)候,類A有更大的可能性不需要被修改,并且對(duì)功能的擴(kuò)展更加友好,這就達(dá)成了我們的目標(biāo)了。

基于上述做法,如果我們?cè)谖磥?lái)擴(kuò)展是派生出一個(gè)B的子類,override相關(guān)的方法,那么類A的代碼基本是不需要修改的。

不過(guò),問(wèn)題是,假若A::OnMessageXX中,并不僅僅需要對(duì)類B發(fā)出消息,還需要對(duì)一系列相關(guān)的類B1,B2,B3等等發(fā)出消息呢?

哦,或許我們可以這樣做:

 

void A::OnMessageXX(const std::list<B*>& lstBInstances)
{
         
for (std::list<B*>::const_iterator itr = lstBInstances.begin();
                   itr 
!= lstBInstances.end();
                   
++itr)
         
{
                   (
*itr)->DoSomething();

         }

}



是的,上面這是一種做法,有一系列B的對(duì)象需要被通知到,所以我們可以用一個(gè)列表把他們串起來(lái),然后在循環(huán)中通知他們?nèi)ジ苫睢2贿^(guò)這樣做的前提是,這一系列B對(duì)象都是派生自一個(gè)公共基類B,有共通的接口;此外,我們需要在A的OnMessageXX被調(diào)用之前構(gòu)造一個(gè)需要接受通知的B對(duì)象列表。

當(dāng)A需要通知B,C,D等一系列沒(méi)有公共接口的對(duì)象的時(shí)候,上面的這種做法就無(wú)法處理了。

對(duì)于B、C、D等需要由A來(lái)調(diào)用的類來(lái)說(shuō),它們需要在A通知它們的時(shí)候,做一些特定的事情。而又A則是在某些特定的時(shí)刻需要通知B、C、D。這樣,我們可以把問(wèn)題看成一個(gè)消息響應(yīng)機(jī)制。

B、C、D可以在A的某些事件上注冊(cè)一些回調(diào)函數(shù),當(dāng)事件發(fā)生時(shí),A確保注冊(cè)該事件的函數(shù)被調(diào)用到。

如下:

typedef void(callback*)();

class A {

public:

         enum EventIds {

         EVENT_MSG1,

         EVENT_MSG2,

};

void RegisterEvent(int nEventId, callback pfn);

private:

callback m_pfnCallback;

};

現(xiàn)在,B可以調(diào)用A::RegisterEvent注冊(cè)一個(gè)事件,并傳遞一個(gè)函數(shù)指針給A。

當(dāng)A中發(fā)生了注冊(cè)的事件時(shí),這個(gè)函數(shù)指針會(huì)被回調(diào)到。

不過(guò)這種簡(jiǎn)單的做法適應(yīng)性很差:

1、  不能支持單個(gè)事件的多個(gè)callback (可能有很多類都需要注冊(cè)該事件,并在事件發(fā)生時(shí)依次被回調(diào))

2、  不能支持多個(gè)事件的同時(shí)存在

3、  回調(diào)函數(shù)沒(méi)有參數(shù)’

針對(duì)問(wèn)題1,2,我們可以使用一個(gè)事件映射解決問(wèn)題,做法如下:

typedef int EventId;

typedef void (callback*)();

typedef std::list<callback> CallbackList;

typedef std::map<EventId, CallbackList> CallbackMap;

現(xiàn)在這個(gè)數(shù)據(jù)結(jié)構(gòu)就能夠支持多個(gè)event同時(shí)存在,且每個(gè)event都可以支持多個(gè)回調(diào)函數(shù)了。

但是這種用法依舊很不方便,如果類B想要注冊(cè)A上的一個(gè)事件,他需要定義一個(gè) callback類型的函數(shù),并把這個(gè)函數(shù)的地址傳遞給A。問(wèn)題是,往往我們希望類B的回調(diào)函數(shù)在被調(diào)用到的時(shí)候,對(duì)類B中的數(shù)據(jù)和狀態(tài)進(jìn)行修改,而一個(gè)單獨(dú)的函數(shù),若想獲得/修改B中的狀態(tài),則必須要與類B緊密耦合。(通過(guò)獲取全局對(duì)象,或者Singleton的方式)

這種緊密耦合引發(fā)我們的思考,能否在Callback中同時(shí)包含類B的指針與類B的成員函數(shù)。

答案是肯定的:泛型回調(diào) 就可以做到這一點(diǎn)。關(guān)于泛型回調(diào)(Generic callback)的信息,在Herb Sutter的Exceptional C++ Style 的35條中有詳細(xì)介紹。

一下比較簡(jiǎn)單的泛型回調(diào)的定義如下:

class callbackbase {

public:

virtual void operator()() const {};

virtual ~callbackbase() = 0 {};

};

template <class T>

class callback : public callbackbase {

public:

typedef void (T::*Func)();

callback(T& t, Func func) : object(t), f(func) {}     // 綁定到實(shí)際對(duì)象

void operator() () const { (object->*f)(); }              // 調(diào)用回調(diào)函數(shù)

private:

T* object;

Func f;

};

有了這種泛型回調(diào)類,我們就可以將類B的實(shí)例與B的成員回調(diào)函數(shù)綁定在一起注冊(cè)到容器當(dāng)中了,而不必再被如何在普通函數(shù)中修改B對(duì)象狀態(tài)的問(wèn)題所困擾了。不過(guò)回調(diào)函數(shù)的參數(shù)問(wèn)題依舊。如果想支持參數(shù),我們不得不對(duì)每一種參數(shù)類型做一個(gè)不同的typedef,像上面定義的這樣 typedef void (T::*Func)();(如:typedef void (T::*Func)(int);)

一種解決方案是借助于Any(一種任意類型類)進(jìn)行參數(shù)傳遞。

但是還有更完善的解決方案,不需要id號(hào),也不需要泛型回調(diào),Ogre采用Listener的方式實(shí)現(xiàn)的類間消息傳遞不僅可以支持單個(gè)類B對(duì)類A中某個(gè)事件的單次/多次注冊(cè),也可以支持類B、C、D對(duì)同一個(gè)事件的注冊(cè)。而且可以完美的解決參數(shù)傳遞問(wèn)題。

具體的方案如下:

 1class A {
 2public:
 3         class Listener 
 4        {
 5           public:
 6
 7                   virtual void OnMessageXX(int param1, float param2) = 0;
 8
 9                   virtual void OnMessageYY(int param1, const std::string& param2) = 0;
10
11        }
;
12
13void registerListener(Listener* obj) 
14
15   m_lstListener.push_back(obj); 
16}

17
18void removeListener(Listener* obj)
19{
20         ListenerList::iterator itr = std::find(m_lstListener.begin(), m_lstListener.end(), obj); 
21
22         if (itr != m_lstListener.end())
23                   m_lstListener.erase(itr);
24}

25
26private:
27         typedef std::list<Listener*> ListenerList;
28
29         ListenerList m_lstListeners;
30}
;
31
32

 

有了以上定義,當(dāng)類A收到某個(gè)消息XX之后,只需遍歷m_lstListeners列表,調(diào)用所有列表成員的OnMessageXX即可。

而所有注冊(cè)A的消息的類,都必須從A::Listener派生一個(gè)類,在它感興趣的消息處理函數(shù)中做出相應(yīng)處理,而對(duì)不感興趣的消息,只需設(shè)為空函數(shù)即可。

一個(gè)簡(jiǎn)單的類B的定義如下:

class B {

public:

         friend class BListener;

         class BListener : public A::Listener {

         public:

                   BListener(B* pBInstance) : m_pBInstance(pBInstance) {}

                   virtual void OnMessageXX(int param1, float param2)

{ m_pBInstance->DoSomething(); }

                   virtual void OnMessageYY(int param1, const std::string& param2) {}

         private:

                   B* m_pBInstance;

};

explicit B(A* pAInstance) : m_pAInstance(pAInstance)

{

m_pListener(new BListener(this));

m_pAInstance->registerListener(m_pListener);

}

         ~B() { m_pAInstance->removeListener(m_pListener); delete m_pListener; }

void DoSomething();

private:

         BListener* m_pListener;

}

類B在創(chuàng)建自身實(shí)例時(shí),接受一個(gè)A的指針(這是合理的,因?yàn)轭怋需要監(jiān)聽(tīng)類A的消息,理應(yīng)知道A的存在),并創(chuàng)建一個(gè)派生自A::Listener 的監(jiān)聽(tīng)者對(duì)象,并把自身的指針傳遞給該對(duì)象,以使得該監(jiān)聽(tīng)者改變類B的狀態(tài),而后類B將創(chuàng)建好的監(jiān)聽(tīng)者對(duì)象加入到A的監(jiān)聽(tīng)者列表中。

在B進(jìn)行析構(gòu)的時(shí)候,需要從A中刪除自己注冊(cè)的監(jiān)聽(tīng)者。而后將該對(duì)象釋放。

這種做法的好處:

1、  類B(以及類C等)對(duì)類A實(shí)現(xiàn)了信息隱藏,類A不再關(guān)注任何需要監(jiān)聽(tīng)它自身消息的其他類,只需關(guān)注其自身的狀態(tài)。從而減低了類A與其他與之關(guān)聯(lián)的類之間的耦合。(類A不必再費(fèi)盡心機(jī)的去獲取B的指針,不管是通過(guò)全局變量,還是Singleton,還是參數(shù),還是類成員變量,都不再需要了,A只關(guān)心在 Listener中定義好的一組接口即可)而且,如果有必要類B可以對(duì)同一個(gè)消息注冊(cè)多次,且可以對(duì)同一消息有不同的反應(yīng)(通過(guò)定義不同的 BListener實(shí)現(xiàn)達(dá)到這一目的),只需在B不再需要監(jiān)聽(tīng)相關(guān)消息時(shí)將所注冊(cè)過(guò)的對(duì)象注銷(xiāo)掉即可。

2、  由于1中所述,類A的實(shí)現(xiàn)無(wú)需關(guān)心類B的實(shí)現(xiàn),因此類A的邏輯中不需要包含任何類B的方法調(diào)用,從而,類A的cpp文件中,無(wú)需包含類B的頭文件,(可能還包括類C,D等等,此處類B指代需要根據(jù)類A狀態(tài)而做出動(dòng)作的類)從而降低編譯時(shí)間,這是解耦合所帶來(lái)的附加好處。

3、  同樣是解耦合帶來(lái)的好處:因?yàn)闊o(wú)需關(guān)注類B等等其他類的實(shí)現(xiàn),類A的代碼邏輯變得更加清晰,并且減少未來(lái)邏輯需求變更的改動(dòng)所需要付出的代價(jià)(邏輯變更可能需要更改接口,需要增加狀態(tài)判斷,無(wú)論是調(diào)試時(shí)間還是編譯時(shí)間都是不可忽視的代價(jià))。

 

本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/zougangx/archive/2009/07/30/4395775.aspx

posted @ 2011-05-12 18:12 沛沛 閱讀(409) | 評(píng)論 (0)編輯 收藏

在編寫(xiě)程序的時(shí)候,我們經(jīng)常要用到#pragma指令來(lái)設(shè)定編譯器的狀態(tài)或者是指示編譯器完成一些特定的動(dòng)作.
 下面介紹了一下該指令的一些常用參數(shù),希望對(duì)大家有所幫助!

 一. message 參數(shù)。
 message
 它能夠在編譯信息輸出窗口中輸出相應(yīng)的信息,這對(duì)于源代碼信息的控制是非常重要的。

 其使用方法為:  #pragma message("消息文本")

 當(dāng)編譯器遇到這條指令時(shí)就在編譯輸出窗口中將消息文本打印出來(lái)。
 當(dāng)我們?cè)诔绦蛑卸x了許多宏來(lái)控制源代碼版本的時(shí)候,我們自己有可能都會(huì)忘記有沒(méi)有正確的設(shè)置這些宏,此時(shí)我們可以用這條指令在編譯的時(shí)候就進(jìn)行檢查。假設(shè)我們希望判斷自己有沒(méi)有在源代碼的什么地方定義了_X86這個(gè)宏可以用下面的方法:
#ifdef _X86
#pragma message("_X86 macro activated!")
#endif
 當(dāng)我們定義了_X86這個(gè)宏以后,應(yīng)用程序在編譯時(shí)就會(huì)在編譯輸出窗口里顯示
 "_X86 macro activated!"
 這樣,我們就不會(huì)因?yàn)椴挥浀米约憾x的一些特定的宏而抓耳撓腮了。

 二. 另一個(gè)使用得比較多的#pragma參數(shù)是code_seg。
 格式如:
 #pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
 該指令用來(lái)指定函數(shù)在.obj文件中存放的節(jié),觀察OBJ文件可以使用VC自帶的dumpbin命令行程序,函數(shù)在.obj文件中默認(rèn)的存放節(jié)為.text節(jié),如果code_seg沒(méi)有帶參數(shù)的話,則函數(shù)存放在.text節(jié)中。

 push (可選參數(shù)) 將一個(gè)記錄放到內(nèi)部編譯器的堆棧中,可選參數(shù)可以為一個(gè)標(biāo)識(shí)符或者節(jié)名
 pop(可選參數(shù)) 將一個(gè)記錄從堆棧頂端彈出,該記錄可以為一個(gè)標(biāo)識(shí)符或者節(jié)名
 identifier (可選參數(shù)) 當(dāng)使用push指令時(shí),為壓入堆棧的記錄指派的一個(gè)標(biāo)識(shí)符,當(dāng)該標(biāo)識(shí)符被刪除的時(shí)候和其相關(guān)的堆棧中的記錄將被彈出堆棧
   "segment-name" (可選參數(shù)) 表示函數(shù)存放的節(jié)名
 例如:
 //默認(rèn)情況下,函數(shù)被存放在.text節(jié)中
 void func1() {   // stored in .text
 }

 //將函數(shù)存放在.my_data1節(jié)中
 #pragma code_seg(".my_data1")
 void func2() {   // stored in my_data1
 }

 //r1為標(biāo)識(shí)符,將函數(shù)放入.my_data2節(jié)中
 #pragma code_seg(push, r1, ".my_data2")
 void func3() {   // stored in my_data2
 }

 int main() {
 }

 三. #pragma once (比較常用)
 這是一個(gè)比較常用的指令,只要在頭文件的最開(kāi)始加入這條指令就能夠保證頭文件被編譯一次

 四. #pragma hdrstop表示預(yù)編譯頭文件到此為止,后面的頭文件不進(jìn)行預(yù)編譯。
 BCB可以預(yù)編譯頭文件以加快鏈接的速度,但如果所有頭文件都進(jìn)行預(yù)編譯又可能占太多磁盤(pán)空間,所以使用這個(gè)選項(xiàng)排除一些頭文件。
 有時(shí)單元之間有依賴關(guān)系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用#pragma startup指定編譯優(yōu)先級(jí),如果使用了#pragma package(smart_init) ,BCB就會(huì)根據(jù)優(yōu)先級(jí)的大小先后編譯。

 五. #pragma warning指令
 該指令允許有選擇性的修改編譯器的警告消息的行為
 指令格式如下:
 #pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...]
 #pragma warning( push[ ,n ] )
 #pragma warning( pop )

 主要用到的警告表示有如下幾個(gè):

 once:只顯示一次(警告/錯(cuò)誤等)消息
 default:重置編譯器的警告行為到默認(rèn)狀態(tài)
 1,2,3,4:四個(gè)警告級(jí)別
 disable:禁止指定的警告信息
 error:將指定的警告信息作為錯(cuò)誤報(bào)告

 如果大家對(duì)上面的解釋不是很理解,可以參考一下下面的例子及說(shuō)明

 #pragma warning( disable : 4507 34; once : 4385; error : 164 )
 等價(jià)于:
 #pragma warning(disable:4507 34)  // 不顯示4507和34號(hào)警告信息
 #pragma warning(once:4385)   // 4385號(hào)警告信息僅報(bào)告一次
 #pragma warning(error:164)   // 把164號(hào)警告信息作為一個(gè)錯(cuò)誤。
 同時(shí)這個(gè)pragma warning 也支持如下格式:
 #pragma warning( push [ ,n ] )
 #pragma warning( pop )
 這里n代表一個(gè)警告等級(jí)(1---4)。
 #pragma warning( push )保存所有警告信息的現(xiàn)有的警告狀態(tài)。
 #pragma warning( push, n)保存所有警告信息的現(xiàn)有的警告狀態(tài),并且把全局警告等級(jí)設(shè)定為n。
 #pragma warning( pop )向棧中彈出最后一個(gè)警告信息,在入棧和出棧之間所作的一切改動(dòng)取消。例如:
 #pragma warning( push )
 #pragma warning( disable : 4705 )
 #pragma warning( disable : 4706 )
 #pragma warning( disable : 4707 )
 #pragma warning( pop )

 在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707)

 在使用標(biāo)準(zhǔn)C++進(jìn)行編程的時(shí)候經(jīng)常會(huì)得到很多的警告信息,而這些警告信息都是不必要的提示,所以我們可以使用#pragma warning(disable:4786)來(lái)禁止該類型的警告在vc中使用ADO的時(shí)候也會(huì)得到不必要的警告信息,這個(gè)時(shí)候我們可以通過(guò)#pragma warning(disable:4146)來(lái)消除該類型的警告信息

 六. pragma comment(...)
 該指令的格式為:  #pragma comment( "comment-type" [, commentstring] )
 該指令將一個(gè)注釋記錄放入一個(gè)對(duì)象文件或可執(zhí)行文件中,comment-type(注釋類型):可以指定為五種預(yù)定義的標(biāo)識(shí)符的其中一種。
 五種預(yù)定義的標(biāo)識(shí)符為:

 1、compiler:將編譯器的版本號(hào)和名稱放入目標(biāo)文件中,本條注釋記錄將被編譯器忽略
如果你為該記錄類型提供了commentstring參數(shù),編譯器將會(huì)產(chǎn)生一個(gè)警告
例如:#pragma comment( compiler )

 2、exestr:將commentstring參數(shù)放入目標(biāo)文件中,在鏈接的時(shí)候這個(gè)字符串將被放入到可執(zhí)行文件中,當(dāng)操作系統(tǒng)加載可執(zhí)行文件的時(shí)候,該參數(shù)字符串不會(huì)被加載到內(nèi)存中.但是,該字符串可以被dumpbin之類的程序查找出并打印出來(lái),你可以用這個(gè)標(biāo)識(shí)符將版本號(hào)碼之類的信息嵌入到可執(zhí)行文件中!

 3、lib:這是一個(gè)非常常用的關(guān)鍵字,用來(lái)將一個(gè)庫(kù)文件鏈接到目標(biāo)文件中常用的lib關(guān)鍵字,可以幫我們連入一個(gè)庫(kù)文件。
 例如:
 #pragma comment(lib, "user32.lib")
 該指令用來(lái)將user32.lib庫(kù)文件加入到本工程中

 4、linker:將一個(gè)鏈接選項(xiàng)放入目標(biāo)文件中,你可以使用這個(gè)指令來(lái)代替由命令行傳入的或者在開(kāi)發(fā)環(huán)境中設(shè)置的鏈接選項(xiàng),你可以指定/include選項(xiàng)來(lái)強(qiáng)制包含某個(gè)對(duì)象,例如:
#pragma comment(linker, "/include:__mySymbol")
你可以在程序中設(shè)置下列鏈接選項(xiàng)
/DEFAULTLIB
/EXPORT
/INCLUDE
/MERGE
/SECTION

 這些選項(xiàng)在這里就不一一說(shuō)明了,詳細(xì)信息請(qǐng)看msdn!

 5、user:將一般的注釋信息放入目標(biāo)文件中commentstring參數(shù)包含注釋的文本信息,這個(gè)注釋記錄將被鏈接器忽略
 例如:
 #pragma comment( user, "Compiled on " __DATE__ " at " __TIME__ )

posted @ 2011-05-03 15:50 沛沛 閱讀(313) | 評(píng)論 (0)編輯 收藏

1、讀取當(dāng)前錯(cuò)誤值:每次發(fā)生錯(cuò)誤時(shí),如果要對(duì)具體問(wèn)題進(jìn)行處理,那么就應(yīng)該調(diào)用這個(gè)函數(shù)取得錯(cuò)誤代碼。
      int  WSAGetLastError(void );
#define h_errno   WSAGetLastError()
錯(cuò)誤值請(qǐng)自己閱讀Winsock2.h。

2、將主機(jī)的unsigned long值轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序(32位):為什么要這樣做呢?因?yàn)椴煌挠?jì)算機(jī)使用不同的字節(jié)順序存儲(chǔ)數(shù)據(jù)。因此任何從Winsock函數(shù)對(duì)IP地址和端口號(hào)的引用和傳給Winsock函數(shù)的IP地址和端口號(hào)均時(shí)按照網(wǎng)絡(luò)順序組織的。
      u_long  htonl(u_long hostlong);
舉例:htonl(0)=0
htonl(80)= 1342177280
3、將unsigned long數(shù)從網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換位主機(jī)字節(jié)順序,是上面函數(shù)的逆函數(shù)。
      u_long  ntohl(u_long netlong);
舉例:ntohl(0)=0
ntohl(1342177280)= 80
4、將主機(jī)的unsigned short值轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序(16位):原因同2:
      u_short  htons(u_short hostshort);
舉例:htonl(0)=0
htonl(80)= 20480
5、將unsigned short數(shù)從網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換位主機(jī)字節(jié)順序,是上面函數(shù)的逆函數(shù)。
      u_short  ntohs(u_short netshort);
舉例:ntohs(0)=0
ntohsl(20480)= 80
6、將用點(diǎn)分割的IP地址轉(zhuǎn)換位一個(gè)in_addr結(jié)構(gòu)的地址,這個(gè)結(jié)構(gòu)的定義見(jiàn)筆記(一),實(shí)際上就是一個(gè)unsigned long值。計(jì)算機(jī)內(nèi)部處理IP地址可是不認(rèn)識(shí)如192.1.8.84之類的數(shù)據(jù)。
      unsigned long  inet_addr( const char FAR * cp );
舉例:inet_addr("192.1.8.84")=1409810880
inet_addr("127.0.0.1")= 16777343
如果發(fā)生錯(cuò)誤,函數(shù)返回INADDR_NONE值。

7、將網(wǎng)絡(luò)地址轉(zhuǎn)換位用點(diǎn)分割的IP地址,是上面函數(shù)的逆函數(shù)。
      char FAR *  inet_ntoa( struct in_addr in );
舉例:char * ipaddr=NULL;
char addr[20];
in_addr inaddr;
inaddr. s_addr=16777343;
ipaddr= inet_ntoa(inaddr);
strcpy(addr,ipaddr); 
這樣addr的值就變?yōu)?27.0.0.1。
注意意不要修改返回值或者進(jìn)行釋放動(dòng)作。如果函數(shù)失敗就會(huì)返回NULL值。

8、獲取套接字的本地地址結(jié)構(gòu):
      int  getsockname(SOCKET s, struct sockaddr FAR * name, int FAR * namelen );
s為套接字
name為函數(shù)調(diào)用后獲得的地址值
namelen為緩沖區(qū)的大小。
9、獲取與套接字相連的端地址結(jié)構(gòu):
      int  getpeername(SOCKET s, struct sockaddr FAR * name, int FAR * namelen );
s為套接字
name為函數(shù)調(diào)用后獲得的端地址值
namelen為緩沖區(qū)的大小。
10、獲取計(jì)算機(jī)名:
      int  gethostname( char FAR * name, int namelen );
name是存放計(jì)算機(jī)名的緩沖區(qū)
namelen是緩沖區(qū)的大小
用法:
char szName[255];
memset(szName,0,255);
if(gethostname(szName,255)==SOCKET_ERROR)
{
//錯(cuò)誤處理
}
返回值為:szNmae="xiaojin"
11、根據(jù)計(jì)算機(jī)名獲取主機(jī)地址:
      struct hostent FAR *  gethostbyname( const char FAR * name );
name為計(jì)算機(jī)名。
用法:
hostent * host;
char* ip;
host= gethostbyname("xiaojin");
if(host->h_addr_list[0])
{
struct in_addr addr;
memmove(&addr, host->h_addr_list[0],4);
//獲得標(biāo)準(zhǔn)IP地址
ip=inet_ ntoa (addr);
}
返回值為:hostent->h_name="xiaojin"
hostent->h_addrtype=2    //AF_INET
hostent->length=4
ip="127.0.0.1"
Winsock 的I/O操作:

1、 兩種I/O模式
  • 阻塞模式:執(zhí)行I/O操作完成前會(huì)一直進(jìn)行等待,不會(huì)將控制權(quán)交給程序。套接字 默認(rèn)為阻塞模式。可以通過(guò)多線程技術(shù)進(jìn)行處理。
  • 非阻塞模式:執(zhí)行I/O操作時(shí),Winsock函數(shù)會(huì)返回并交出控制權(quán)。這種模式使用 起來(lái)比較復(fù)雜,因?yàn)楹瘮?shù)在沒(méi)有運(yùn)行完成就進(jìn)行返回,會(huì)不斷地返回 WSAEWOULDBLOCK錯(cuò)誤。但功能強(qiáng)大。
為了解決這個(gè)問(wèn)題,提出了進(jìn)行I/O操作的一些I/O模型,下面介紹最常見(jiàn)的三種:

2、select模型:

  通過(guò)調(diào)用select函數(shù)可以確定一個(gè)或多個(gè)套接字的狀態(tài),判斷套接字上是否有數(shù)據(jù),或
者能否向一個(gè)套接字寫(xiě)入數(shù)據(jù)。
      int  select( int nfds, fd_set FAR * readfds, fd_set FAR * writefds, 
fd_set FAR *exceptfds, const struct timeval FAR * timeout );
◆先來(lái)看看涉及到的結(jié)構(gòu)的定義:
a、 d_set結(jié)構(gòu):
#define FD_SETSIZE 64?
typedef struct fd_set {
u_int fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;      
fd_count為已設(shè)定socket的數(shù)量
fd_array為socket列表,F(xiàn)D_SETSIZE為最大socket數(shù)量,建議不小于64。這是微軟建
議的。

B、timeval結(jié)構(gòu):
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
tv_sec為時(shí)間的秒值。
tv_usec為時(shí)間的毫秒值。
這個(gè)結(jié)構(gòu)主要是設(shè)置select()函數(shù)的等待值,如果將該結(jié)構(gòu)設(shè)置為(0,0),則select()函數(shù)
會(huì)立即返回。

◆再來(lái)看看select函數(shù)各參數(shù)的作用:
  1. nfds:沒(méi)有任何用處,主要用來(lái)進(jìn)行系統(tǒng)兼容用,一般設(shè)置為0。
  2. readfds:等待可讀性檢查的套接字組。
  3. writefds;等待可寫(xiě)性檢查的套接字組。
  4. exceptfds:等待錯(cuò)誤檢查的套接字組。
  5. timeout:超時(shí)時(shí)間。
  6. 函數(shù)失敗的返回值:調(diào)用失敗返回SOCKET_ERROR,超時(shí)返回0。
readfds、writefds、exceptfds三個(gè)變量至少有一個(gè)不為空,同時(shí)這個(gè)不為空的套接字組
種至少有一個(gè)socket,道理很簡(jiǎn)單,否則要select干什么呢。 舉例:測(cè)試一個(gè)套接字是否可讀:
fd_set fdread;
//FD_ZERO定義
// #define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)
FD_ZERO(&fdread);
FD_SET(s,&fdread); //加入套接字,詳細(xì)定義請(qǐng)看winsock2.h
if(select(0,%fdread,NULL,NULL,NULL)>0
{
//成功
if(FD_ISSET(s,&fread) //是否存在fread中,詳細(xì)定義請(qǐng)看winsock2.h
{
//是可讀的
}
}

◆I/O操作函數(shù):主要用于獲取與套接字相關(guān)的操作參數(shù)。

 int  ioctlsocket(SOCKET s, long cmd, u_long FAR * argp );     
s為I/O操作的套接字。
cmd為對(duì)套接字的操作命令。
argp為命令所帶參數(shù)的指針。

常見(jiàn)的命令:
//確定套接字自動(dòng)讀入的數(shù)據(jù)量
#define FIONREAD _IOR(''''f'''', 127, u_long) /* get # bytes to read */
//允許或禁止套接字的非阻塞模式,允許為非0,禁止為0
#define FIONBIO _IOW(''''f'''', 126, u_long) /* set/clear non-blocking i/o */
//確定是否所有帶外數(shù)據(jù)都已被讀入
#define SIOCATMARK _IOR(''''s'''', 7, u_long) /* at oob mark? */
3、WSAAsynSelect模型:
WSAAsynSelect模型也是一個(gè)常用的異步I/O模型。應(yīng)用程序可以在一個(gè)套接字上接收以
WINDOWS消息為基礎(chǔ)的網(wǎng)絡(luò)事件通知。該模型的實(shí)現(xiàn)方法是通過(guò)調(diào)用WSAAsynSelect函
數(shù) 自動(dòng)將套接字設(shè)置為非阻塞模式,并向WINDOWS注冊(cè)一個(gè)或多個(gè)網(wǎng)絡(luò)時(shí)間,并提供一
個(gè)通知時(shí)使用的窗口句柄。當(dāng)注冊(cè)的事件發(fā)生時(shí),對(duì)應(yīng)的窗口將收到一個(gè)基于消息的通知。
      int  WSAAsyncSelect( SOCKET s, HWND hWnd, u_int wMsg, long lEvent);       
s為需要事件通知的套接字
hWnd為接收消息的窗口句柄
wMsg為要接收的消息
lEvent為掩碼,指定應(yīng)用程序感興趣的網(wǎng)絡(luò)事件組合,主要如下:
#define FD_READ_BIT 0
#define FD_READ (1 << FD_READ_BIT)
#define FD_WRITE_BIT 1
#define FD_WRITE (1 << FD_WRITE_BIT)
#define FD_OOB_BIT 2
#define FD_OOB (1 << FD_OOB_BIT)
#define FD_ACCEPT_BIT 3
#define FD_ACCEPT (1 << FD_ACCEPT_BIT)
#define FD_CONNECT_BIT 4
#define FD_CONNECT (1 << FD_CONNECT_BIT)
#define FD_CLOSE_BIT 5
#define FD_CLOSE (1 << FD_CLOSE_BIT)
用法:要接收讀寫(xiě)通知:
int nResult= WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE);
if(nResult==SOCKET_ERROR)
{
//錯(cuò)誤處理
}
取消通知:
      int nResult= WSAAsyncSelect(s,hWnd,0,0);
當(dāng)應(yīng)用程序窗口hWnd收到消息時(shí),wMsg.wParam參數(shù)標(biāo)識(shí)了套接字,lParam的低字標(biāo)明
了網(wǎng)絡(luò)事件,高字則包含錯(cuò)誤代碼。

4、WSAEventSelect模型
WSAEventSelect模型類似WSAAsynSelect模型,但最主要的區(qū)別是網(wǎng)絡(luò)事件發(fā)生時(shí)會(huì)被發(fā)
送到一個(gè)事件對(duì)象句柄,而不是發(fā)送到一個(gè)窗口。

使用步驟如下:
a、 創(chuàng)建事件對(duì)象來(lái)接收網(wǎng)絡(luò)事件:
#define WSAEVENT HANDLE
#define LPWSAEVENT LPHANDLE
WSAEVENT WSACreateEvent( void );
該函數(shù)的返回值為一個(gè)事件對(duì)象句柄,它具有兩種工作狀態(tài):已傳信(signaled)和未傳信
(nonsignaled)以及兩種工作模式:人工重設(shè)(manual reset)和自動(dòng)重設(shè)(auto reset)。默認(rèn)未
未傳信的工作狀態(tài)和人工重設(shè)模式。

b、將事件對(duì)象與套接字關(guān)聯(lián),同時(shí)注冊(cè)事件,使事件對(duì)象的工作狀態(tài)從未傳信轉(zhuǎn)變未
已傳信。
      int  WSAEventSelect( SOCKET s,WSAEVENT hEventObject,long lNetworkEvents );  
s為套接字
hEventObject為剛才創(chuàng)建的事件對(duì)象句柄
lNetworkEvents為掩碼,定義如上面所述

c、I/O處理后,設(shè)置事件對(duì)象為未傳信
BOOL WSAResetEvent( WSAEVENT hEvent );

Hevent為事件對(duì)象

成功返回TRUE,失敗返回FALSE。

d、等待網(wǎng)絡(luò)事件來(lái)觸發(fā)事件句柄的工作狀態(tài):

DWORD WSAWaitForMultipleEvents( DWORD cEvents,
const WSAEVENT FAR * lphEvents, BOOL fWaitAll,
DWORD dwTimeout, BOOL fAlertable );

lpEvent為事件句柄數(shù)組的指針
cEvent為為事件句柄的數(shù)目,其最大值為WSA_MAXIMUM_WAIT_EVENTS 
fWaitAll指定等待類型:TRUE:當(dāng)lphEvent數(shù)組重所有事件對(duì)象同時(shí)有信號(hào)時(shí)返回;
FALSE:任一事件有信號(hào)就返回。
dwTimeout為等待超時(shí)(毫秒)
fAlertable為指定函數(shù)返回時(shí)是否執(zhí)行完成例程

對(duì)事件數(shù)組中的事件進(jìn)行引用時(shí),應(yīng)該用WSAWaitForMultipleEvents的返回值,減去
預(yù)聲明值WSA_WAIT_EVENT_0,得到具體的引用值。例如:

nIndex=WSAWaitForMultipleEvents(…);
MyEvent=EventArray[Index- WSA_WAIT_EVENT_0];

e、判斷網(wǎng)絡(luò)事件類型:

int WSAEnumNetworkEvents( SOCKET s,
WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents );

s為套接字
hEventObject為需要重設(shè)的事件對(duì)象
lpNetworkEvents為記錄網(wǎng)絡(luò)事件和錯(cuò)誤代碼,其結(jié)構(gòu)定義如下:

typedef struct _WSANETWORKEVENTS {
long lNetworkEvents;
int iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;

f、關(guān)閉事件對(duì)象句柄:

BOOL WSACloseEvent(WSAEVENT hEvent);

調(diào)用成功返回TRUE,否則返回FALSE。

posted @ 2011-05-03 15:44 沛沛 閱讀(253) | 評(píng)論 (0)編輯 收藏

1、C++各大有名庫(kù)的介紹——C++標(biāo)準(zhǔn)庫(kù)
2、C++各大有名庫(kù)的介紹——準(zhǔn)標(biāo)準(zhǔn)庫(kù)Boost
3、C++各大有名庫(kù)的介紹——GUI
4、C++各大有名庫(kù)的介紹——網(wǎng)絡(luò)通信
5、C++各大有名庫(kù)的介紹——XML
6、C++各大有名庫(kù)的介紹——科學(xué)計(jì)算
7、C++各大有名庫(kù)的介紹——游戲開(kāi)發(fā)
8、C++各大有名庫(kù)的介紹——線程
9、C++各大有名庫(kù)的介紹——序列化
10、C++各大有名庫(kù)的介紹——字符串
11、C++各大有名庫(kù)的介紹——綜合
12、C++各大有名庫(kù)的介紹——其他庫(kù)
13、C++名人的網(wǎng)站

在 C++中,庫(kù)的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了設(shè)計(jì)庫(kù)來(lái)擴(kuò)充功能要好過(guò)設(shè)計(jì)更多的語(yǔ)法的言論。現(xiàn)實(shí)中,C++的庫(kù)門(mén)類繁多,解決的問(wèn)題也是極其廣泛,庫(kù)從輕量級(jí)到重量級(jí)的都有。不少都是讓人眼界大開(kāi),亦或是望而生嘆的思維杰作。由于庫(kù)的數(shù)量非常龐大,而且限于筆者水平,其中很多并不了解。所以文中所提的一些庫(kù)都是比較著名的大型庫(kù)。


1、C++各大有名庫(kù)的介紹——C++標(biāo)準(zhǔn)庫(kù)
標(biāo)準(zhǔn)庫(kù)中提供了C++程序的基本設(shè)施。雖然C++標(biāo)準(zhǔn)庫(kù)隨著C++標(biāo)準(zhǔn)折騰了許多年,直到標(biāo)準(zhǔn)的出臺(tái)才正式定型,但是在標(biāo)準(zhǔn)庫(kù)的實(shí)現(xiàn)上卻很令人欣慰得看到多種實(shí)現(xiàn),并且已被實(shí)踐證明為有工業(yè)級(jí)別強(qiáng)度的佳作。

1.1、Dinkumware C++ Library

參考站點(diǎn):http://www.dinkumware.com/

P.J. Plauger編寫(xiě)的高品質(zhì)的標(biāo)準(zhǔn)庫(kù)。P.J. Plauger博士是Dr. Dobb's程序設(shè)計(jì)杰出獎(jiǎng)的獲得者。其編寫(xiě)的庫(kù)長(zhǎng)期被Microsoft采用,并且最近Borland也取得了其OEM的license,在其 C/C++的產(chǎn)品中采用Dinkumware的庫(kù)。

1.2、RogueWave Standard C++ Library

參考站點(diǎn):http://www.roguewave.com/

這個(gè)庫(kù)在Borland C++ Builder的早期版本中曾經(jīng)被采用,后來(lái)被其他的庫(kù)給替換了。筆者不推薦使用。

1.3、SGI STL

參考站點(diǎn):http://www.roguewave.com/

SGI公司的C++標(biāo)準(zhǔn)模版庫(kù)。

1.4、STLport

參考站點(diǎn):http://www.stlport.org/

SGI STL庫(kù)的跨平臺(tái)可移植版本。



2、C++各大有名庫(kù)的介紹——準(zhǔn)標(biāo)準(zhǔn)庫(kù)Boost
Boost庫(kù)是一個(gè)經(jīng)過(guò)千錘百煉、可移植、提供源代碼的C++庫(kù),作為標(biāo)準(zhǔn)庫(kù)的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的發(fā)動(dòng)機(jī)之一。 Boost庫(kù)由C++標(biāo)準(zhǔn)委員會(huì)庫(kù)工作組成員發(fā)起,在C++社區(qū)中影響甚大,其成員已近2000人。 Boost庫(kù)為我們帶來(lái)了最新、最酷、最實(shí)用的技術(shù),是不折不扣的“準(zhǔn)”標(biāo)準(zhǔn)庫(kù)。

Boost中比較有名氣的有這么幾個(gè)庫(kù):

2.1 Regex  正則表達(dá)式庫(kù)

2.2 Spirit   LL parser framework,用C++代碼直接表達(dá)EBNF

2.3 Graph  圖組件和算法

2.4 Lambda  在調(diào)用的地方定義短小匿名的函數(shù)對(duì)象,很實(shí)用的functional功能

2.5 concept check   檢查泛型編程中的concept

2.6 Mpl   用模板實(shí)現(xiàn)的元編程框架

2.7 Thread   可移植的C++多線程庫(kù)

2.8 Python   把C++類和函數(shù)映射到Python之中

2.9 Pool    內(nèi)存池管理

2.10 smart_ptr   5個(gè)智能指針,學(xué)習(xí)智能指針必讀,一份不錯(cuò)的參考是來(lái)自CUJ的文章:

Smart Pointers in Boost,哦,這篇文章可以查到,CUJ是提供在線瀏覽的。中文版見(jiàn)筆者在《Dr.Dobb's Journal軟件研發(fā)雜志》第7輯上的譯文。

  Boost總體來(lái)說(shuō)是實(shí)用價(jià)值很高,質(zhì)量很高的庫(kù)。并且由于其對(duì)跨平臺(tái)的強(qiáng)調(diào),對(duì)標(biāo)準(zhǔn)C++的強(qiáng)調(diào),是編寫(xiě)平臺(tái)無(wú)關(guān),現(xiàn)代C++的開(kāi)發(fā)者必備的 工具。但是Boost中也有很多是實(shí)驗(yàn)性質(zhì)的東西,在實(shí)際的開(kāi)發(fā)中實(shí)用需要謹(jǐn)慎。并且很多Boost中的庫(kù)功能堪稱對(duì)語(yǔ)言功能的擴(kuò)展,其構(gòu)造用盡精巧的手 法,不要貿(mào)然的花費(fèi)時(shí)間研讀。Boost另外一面,比如Graph這樣的庫(kù)則是具有工業(yè)強(qiáng)度,結(jié)構(gòu)良好,非常值得研讀的精品代碼,并且也可以放心的在產(chǎn)品 代碼中多多利用。

參考站點(diǎn):http://www.boost.org


3、C++各大有名庫(kù)的介紹——GUI
在眾多C++的庫(kù)中,GUI部分的庫(kù)算是比較繁榮,也比較引人注目的。在實(shí)際開(kāi)發(fā)中,GUI庫(kù)的選擇也是非常重要的一件事情,下面我們綜述一下可選擇的GUI庫(kù),各自的特點(diǎn)以及相關(guān)工具的支持。

3.1、MFC

  大名鼎鼎的微軟基礎(chǔ)類庫(kù)(Microsoft Foundation Class)。大凡學(xué)過(guò)VC++的人都應(yīng)該知道這個(gè)庫(kù)。雖然從技術(shù)角度講,MFC是不大漂亮的,但是它構(gòu)建于Windows API 之上,能夠使程序員的工作更容易,編程效率高,減少了大量在建立 Windows 程序時(shí)必須編寫(xiě)的代碼,同時(shí)它還提供了所有一般 C++ 編程的優(yōu)點(diǎn),例如繼承和封裝。MFC 編寫(xiě)的程序在各個(gè)版本的Windows操作系統(tǒng)上是可移植的,例如,在Windows 3.1下編寫(xiě)的代碼可以很容易地移植到 Windows NT 或 Windows 95 上。但是在最近發(fā)展以及官方支持上日漸勢(shì)微。

3.2、QT

參考網(wǎng)站:http://www.trolltech.com

  Qt是Trolltech公司的一個(gè)多平臺(tái)的C++圖形用戶界面應(yīng)用程序框架。它提供給應(yīng)用程序開(kāi)發(fā)者建立藝術(shù)級(jí)的圖形用戶界面所需的所用功 能。Qt是完全面向?qū)ο蟮暮苋菀讛U(kuò)展,并且允許真正地組件編程。自從1996年早些時(shí)候,Qt進(jìn)入商業(yè)領(lǐng)域,它已經(jīng)成為全世界范圍內(nèi)數(shù)千種成功的應(yīng)用程序 的基礎(chǔ)。Qt也是流行的Linux桌面環(huán)境KDE 的基礎(chǔ),同時(shí)它還支持Windows、Macintosh、Unix/X11等多種平臺(tái)。[wangxinus注:QT目前已經(jīng)是Nokia旗下的產(chǎn)品,原官方網(wǎng)站已經(jīng)失效,目前為http://qt.nokia.com.2009年初發(fā)布的Qt4.5版本開(kāi)始使用LGPL協(xié)議,諾基亞希望以此來(lái)吸引更多的開(kāi)發(fā)人員使用Qt庫(kù)]

3.3、WxWindows

參考網(wǎng)站:http://www.wxwindows.org

  跨平臺(tái)的GUI庫(kù)。因?yàn)槠漕悓哟螛O像MFC,所以有文章介紹從MFC到WxWindows的代碼移植以實(shí)現(xiàn)跨平臺(tái)的功能。通過(guò)多年的開(kāi)發(fā)也是一個(gè)日趨完善的GUI庫(kù),支持同樣不弱于前面兩個(gè)庫(kù)。并且是完全開(kāi)放源代碼的。新近的C++ Builder X的GUI設(shè)計(jì)器就是基于這個(gè)庫(kù)的。[wangxinus注:迫于微軟的施壓,已經(jīng)由WxWindows更名為wxWidgets]

3.4、Fox

參考網(wǎng)站:http://www.fox-toolkit.org/

  開(kāi)放源代碼的GUI庫(kù)。作者從自己親身的開(kāi)發(fā)經(jīng)驗(yàn)中得出了一個(gè)理想的GUI庫(kù)應(yīng)該是什么樣子的感受出發(fā),從而開(kāi)始了對(duì)這個(gè)庫(kù)的開(kāi)發(fā)。有興趣的可以嘗試一下。

3.5、WTL

  基于ATL的一個(gè)庫(kù)。因?yàn)槭褂昧舜罅緼TL的輕量級(jí)手法,模板等技術(shù),在代碼尺寸,以及速度優(yōu)化方面做得非常到位。主要面向的使用群體是開(kāi)發(fā)COM輕量級(jí)供網(wǎng)絡(luò)下載的可視化控件的開(kāi)發(fā)者。

3.6、GTK

參考網(wǎng)站:http://gtkmm.sourceforge.net/

  GTK是一個(gè)大名鼎鼎的C的開(kāi)源GUI庫(kù)。在Linux世界中有Gnome這樣的殺手應(yīng)用。而Qt就是這個(gè)庫(kù)的C++封裝版本。[wangxinus注:“Qt 就是這個(gè)庫(kù)的C++封裝版本”是錯(cuò)誤的。Qt早于GTK,最初Qt由于協(xié)議的原因引起社區(qū)的不滿,另外開(kāi)發(fā)了一個(gè)基于C語(yǔ)言的GTK庫(kù),后面的擴(kuò)展版本為 GTK+。GTK+的Gnome和Qt的KDE是目前l(fā)inux桌面的兩大陣營(yíng),曾有水火不容之勢(shì)。目前雙方都以及開(kāi)源社區(qū)的精神,已經(jīng)和解。]


4、C++各大有名庫(kù)的介紹——網(wǎng)絡(luò)通信

4.1、ACE

參考網(wǎng)站:http://www.cs.wustl.edu/~schmidt/ACE.html

  C++庫(kù)的代表,超重量級(jí)的網(wǎng)絡(luò)通信開(kāi)發(fā)框架。ACE自適配通信環(huán)境(Adaptive Communication Environment)是可以自由使用、開(kāi)放源代碼的面向?qū)ο罂蚣埽谄渲袑?shí)現(xiàn)了許多用于并發(fā)通信軟件的核心模式。ACE提供了一組豐富的可復(fù)用C++ 包裝外觀(Wrapper Facade)和框架組件,可跨越多種平臺(tái)完成通用的通信軟件任務(wù),其中包括:事件多路分離和事件處理器分派、信號(hào)處理、服務(wù)初始化、進(jìn)程間通信、共享內(nèi)存管理、消息路由、分布式服務(wù)動(dòng)態(tài)(重)配置、并發(fā)執(zhí)行和同步,等等。

4.2、StreamModule

參考網(wǎng)站:http://www.omnifarious.org/StrMod

  設(shè)計(jì)用于簡(jiǎn)化編寫(xiě)分布式程序的庫(kù)。嘗試著使得編寫(xiě)處理異步行為的程序更容易,而不是用同步的外殼包起異步的本質(zhì)。

4.3、SimpleSocket

參考網(wǎng)站:http://home.hetnet.nl/~lcbokkers/simsock.htm

  這個(gè)類庫(kù)讓編寫(xiě)基于socket的客戶/服務(wù)器程序更加容易。

4.4、A Stream Socket API for C++

參考網(wǎng)站:http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html

  又一個(gè)對(duì)Socket的封裝庫(kù)。


5、C++各大有名庫(kù)的介紹——XML

5.1、Xerces

參考網(wǎng)站:http://xml.apache.org/xerces-c/

  Xerces-C++ 是一個(gè)非常健壯的XML解析器,它提供了驗(yàn)證,以及SAX和DOM API。XML驗(yàn)證在文檔類型定義(Document Type Definition,DTD)方面有很好的支持,并且在2001年12月增加了支持W3C XMLSchema 的基本完整的開(kāi)放標(biāo)準(zhǔn)。

5.2、XMLBooster

參考網(wǎng)站:http://www.xmlbooster.com/

  這個(gè)庫(kù)通過(guò)產(chǎn)生特制的parser的辦法極大的提高了XML解析的速度,并且能夠產(chǎn)生相應(yīng)的GUI程序來(lái)修改這個(gè)parser。在DOM和SAX兩大主流XML解析辦法之外提供了另外一個(gè)可行的解決方案。

5.3、Pull Parser

參考網(wǎng)站:http://www.extreme.indiana.edu/xgws/xsoap/xpp

  這個(gè)庫(kù)采用pull方法的parser。在每個(gè)SAX的parser底層都有一個(gè)pull的parser,這個(gè)xpp把這層暴露出來(lái)直接給大家使用。在要充分考慮速度的時(shí)候值得嘗試。

5.4、Xalan

參考網(wǎng)站:http://xml.apache.org/xalan-c/

  Xalan是一個(gè)用于把XML文檔轉(zhuǎn)換為HTML,純文本或者其他XML類型文檔的XSLT處理器。

5.5、CMarkup

參考網(wǎng)站:http://www.firstobject.com/xml.htm

  這是一種使用EDOM的XML解析器。在很多思路上面非常靈活實(shí)用。值得大家在DOM和SAX之外尋求一點(diǎn)靈感。

5.6、libxml++

http://libxmlplusplus.sourceforge.net/

  libxml++是對(duì)著名的libxml XML解析器的C++封裝版本。

5.7. TinyXML [wangxinus注:一個(gè)非常小巧的XML解析庫(kù),基于DOM的。]


6、C++各大有名庫(kù)的介紹——科學(xué)計(jì)算

6.1、Blitz++

參考網(wǎng)站:http://www.oonumerics.org/blitz

  Blitz++ 是一個(gè)高效率的數(shù)值計(jì)算函數(shù)庫(kù),它的設(shè)計(jì)目的是希望建立一套既具像C++ 一樣方便,同時(shí)又比Fortran速度更快的數(shù)值計(jì)算環(huán)境。通常,用C++所寫(xiě)出的數(shù)值程序,比 Fortran慢20%左右,因此Blitz++正是要改掉這個(gè)缺點(diǎn)。方法是利用C++的template技術(shù),程序執(zhí)行甚至可以比Fortran更快。

  Blitz++目前仍在發(fā)展中,對(duì)于常見(jiàn)的SVD,F(xiàn)FTs,QMRES等常見(jiàn)的線性代數(shù)方法并不提供,不過(guò)使用者可以很容易地利用Blitz++所提供的函數(shù)來(lái)構(gòu)建。

6.2、POOMA

參考網(wǎng)站:http://www.codesourcery.com/pooma/pooma

  POOMA是一個(gè)免費(fèi)的高性能的C++庫(kù),用于處理并行式科學(xué)計(jì)算。POOMA的面向?qū)ο笤O(shè)計(jì)方便了快速的程序開(kāi)發(fā),對(duì)并行機(jī)器進(jìn)行了優(yōu)化以達(dá)到最高的效率,方便在工業(yè)和研究環(huán)境中使用。

6.3、MTL

參考網(wǎng)站:http://www.osl.iu.edu/research/mtl

  Matrix Template Library(MTL)是一個(gè)高性能的泛型組件庫(kù),提供了各種格式矩陣的大量線性代數(shù)方面的功能。在某些應(yīng)用使用高性能編譯器的情況下,比如Intel的編譯器,從產(chǎn)生的匯編代碼可以看出其與手寫(xiě)幾乎沒(méi)有兩樣的效能。

6.4、CGAL

參考網(wǎng)站:www.cgal.org

  Computational Geometry Algorithms Library的目的是把在計(jì)算幾何方面的大部分重要的解決方案和方法以C++庫(kù)的形式提供給工業(yè)和學(xué)術(shù)界的用戶。


7、C++各大有名庫(kù)的介紹——游戲開(kāi)發(fā)

7.1、Audio/Video 3D C++ Programming Library

參考網(wǎng)站:http://www.galacticasoftware.com/products/av/

  AV3D是一個(gè)跨平臺(tái),高性能的C++庫(kù)。主要的特性是提供3D圖形,聲效支持(SB,以及S3M),控制接口(鍵盤(pán),鼠標(biāo)和遙感),XMS。

7.2、KlayGE

參考網(wǎng)站:http://home.g365.net/enginedev/

  國(guó)內(nèi)游戲開(kāi)發(fā)高手自己用C++開(kāi)發(fā)的游戲引擎。KlayGE是一個(gè)開(kāi)放源代碼、跨平臺(tái)的游戲引擎,并使用Python作腳本語(yǔ)言。KlayGE在LGPL協(xié)議下發(fā)行。感謝龔敏敏先生為中國(guó)游戲開(kāi)發(fā)事業(yè)所做出的貢獻(xiàn)。

[wangxinus注:這個(gè)庫(kù)國(guó)人了解很少,百度百科的KlayGE詞條還是本人創(chuàng)建的。一個(gè)人開(kāi)發(fā)一個(gè)游戲引擎庫(kù),是在讓筆者汗顏,對(duì)作者表示欽佩!]

7.3、OGRE

參考網(wǎng)站:http://www.ogre3d.org

  OGRE(面向?qū)ο蟮膱D形渲染引擎)是用C++開(kāi)發(fā)的,使用靈活的面向?qū)ο?D引擎。它的目的是讓開(kāi)發(fā)者能更方便和直接地開(kāi)發(fā)基于3D硬件設(shè)備 的應(yīng)用程序或游戲。引擎中的類庫(kù)對(duì)更底層的系統(tǒng)庫(kù)(如:Direct3D和OpenGL)的全部使用細(xì)節(jié)進(jìn)行了抽象,并提供了基于現(xiàn)實(shí)世界對(duì)象的接口和其 它類。


8、C++各大有名庫(kù)的介紹——線程

8.1、C++ Threads

參考網(wǎng)站:http://threads.sourceforge.net/

  這個(gè)庫(kù)的目標(biāo)是給程序員提供易于使用的類,這些類被繼承以提供在Linux環(huán)境中很難看到的大量的線程方面的功能。

8.2、ZThreads

參考網(wǎng)站:http://zthread.sourceforge.net/

  一個(gè)先進(jìn)的面向?qū)ο螅缙脚_(tái)的C++線程和同步庫(kù)。


9、C++各大有名庫(kù)的介紹——序列化

9.1、s11n

參考網(wǎng)站:http://s11n.net/

  一個(gè)基于STL的C++庫(kù),用于序列化POD,STL容器以及用戶定義的類型。

9.2、Simple XML Persistence Library

參考網(wǎng)站:http://sxp.sourceforge.net/

  這是一個(gè)把對(duì)象序列化為XML的輕量級(jí)的C++庫(kù)。


10、C++各大有名庫(kù)的介紹——字符串

10.1、C++ Str Library

參考網(wǎng)站:http://www.utilitycode.com/str/

  操作字符串和字符的庫(kù),支持Windows和支持gcc的多種平臺(tái)。提供高度優(yōu)化的代碼,并且支持多線程環(huán)境和Unicode,同時(shí)還有正則表達(dá)式的支持。

10.2、Common Text Transformation Library

參考網(wǎng)站:http://cttl.sourceforge.net/

  這是一個(gè)解析和修改STL字符串的庫(kù)。CTTL substring類可以用來(lái)比較,插入,替換以及用EBNF的語(yǔ)法進(jìn)行解析。

10.3、GRETA

參考網(wǎng)站:http://research.microsoft.com/projects/greta/

  這是由微軟研究院的研究人員開(kāi)發(fā)的處理正則表達(dá)式的庫(kù)。在小型匹配的情況下有非常優(yōu)秀的表現(xiàn)。


11、C++各大有名庫(kù)的介紹——綜合

11.1、P::Classes

參考網(wǎng)站:http://pclasses.com/

  一個(gè)高度可移植的C++應(yīng)用程序框架。當(dāng)前關(guān)注類型和線程安全的signal/slot機(jī)制,i/o系統(tǒng)包括基于插件的網(wǎng)絡(luò)協(xié)議透明的i/o架構(gòu),基于插件的應(yīng)用程序消息日志框架,訪問(wèn)sql數(shù)據(jù)庫(kù)的類等等。

11.2、ACDK - Artefaktur Component Development Kit

參考網(wǎng)站:http://acdk.sourceforge.net/

  這是一個(gè)平臺(tái)無(wú)關(guān)的C++組件框架,類似于Java或者.NET中的框架(反射機(jī)制,線程,Unicode,廢料收集,I/O,網(wǎng)絡(luò),實(shí)用工具,XML,等等),以及對(duì)Java, Perl, Python, TCL, Lisp, COM 和 CORBA的集成。

11.3、dlib C++ library

參考網(wǎng)站:http://www.cis.ohio-state.edu/~kingd/dlib/

  各種各樣的類的一個(gè)綜合。大整數(shù),Socket,線程,GUI,容器類,以及瀏覽目錄的API等等。

11.4、Chilkat C++ Libraries

參考網(wǎng)站:http://www.chilkatsoft.com/cpp_libraries.asp

  這是提供zip,e-mail,編碼,S/MIME,XML等方面的庫(kù)。

11.5、C++ Portable Types Library (PTypes)

參考網(wǎng)站:http://www.melikyan.com/ptypes/

  這是STL的比較簡(jiǎn)單的替代品,以及可移植的多線程和網(wǎng)絡(luò)庫(kù)。

11.6、LFC

參考網(wǎng)站:http://lfc.sourceforge.net/

  哦,這又是一個(gè)嘗試提供一切的C++庫(kù)


12、C++各大有名庫(kù)的介紹——其他庫(kù)

12.1、Loki

參考網(wǎng)站:http://www.moderncppdesign.com/

  哦,你可能抱怨我早該和Boost一起介紹它,一個(gè)實(shí)驗(yàn)性質(zhì)的庫(kù)。作者在loki中把C++模板的功能發(fā)揮到了極致。并且嘗試把類似設(shè)計(jì)模式這樣思想層面的東西通過(guò)庫(kù)來(lái)提供。同時(shí)還提供了智能指針這樣比較實(shí)用的功能。

12.2、ATL

  ATL(Active Template Library)是一組小巧、高效、靈活的類,這些類為創(chuàng)建可互操作的COM組件提供了基本的設(shè)施。

12.3、FC++: The Functional C++ Library

  這個(gè)庫(kù)提供了一些函數(shù)式語(yǔ)言中才有的要素。屬于用庫(kù)來(lái)擴(kuò)充語(yǔ)言的一個(gè)代表作。如果想要在OOP之外尋找另一分的樂(lè)趣,可以去看看函數(shù)式程序設(shè)計(jì)的世界。大師Peter Norvig在 “Teach Yourself Programming in Ten Years”一文中就將函數(shù)式語(yǔ)言列為至少應(yīng)當(dāng)學(xué)習(xí)的6類編程語(yǔ)言之一。

12.4、FACT!

參考網(wǎng)站:http://www.kfa-juelich.de/zam/FACT/start/index.html

  另外一個(gè)實(shí)現(xiàn)函數(shù)式語(yǔ)言特性的庫(kù)

12.5、Crypto++

  提供處理密碼,消息驗(yàn)證,單向hash,公匙加密系統(tǒng)等功能的免費(fèi)庫(kù)。

  還有很多非常激動(dòng)人心或者是極其實(shí)用的C++庫(kù),限于我們的水平以及文章的篇幅不能包括進(jìn)來(lái)。在對(duì)于這些已經(jīng)包含近來(lái)的庫(kù)的介紹中,由于并不是每一個(gè)我們都使用過(guò),所以難免有偏頗之處,請(qǐng)讀者見(jiàn)諒。


13、C++名人的網(wǎng)站
正如我們可以通過(guò)計(jì)算機(jī)歷史上的重要人物了解計(jì)算機(jī)史的發(fā)展,C++相關(guān)人物的網(wǎng)站也可以使我們得到最有價(jià)值的參考與借鑒,下面的人物我們認(rèn)為沒(méi)有介紹的必要,只因下面的人物在C++領(lǐng)域的地位眾所周知,我們只將相關(guān)的資源進(jìn)行羅列以供讀者學(xué)習(xí),他們有的工作于貝爾實(shí)驗(yàn)室,有的工作于知名編譯器廠商,有的在不斷推進(jìn)語(yǔ)言的標(biāo)準(zhǔn)化,有的為讀者撰寫(xiě)了多部千古奇作……

1、Bjarne Stroustrup

http://www.research.att.com/~bs/

2、Stanley B. Lippman

http://blogs.msdn.com/slippman/ (中文版)

http://www.zengyihome.net/slippman/index.htm

3、Scott Meyers

http://www.aristeia.com/

4、David Musser

http://www.cs.rpi.edu/~musser/

5、Bruce Eckel

http://www.bruceeckel.com

http://blog.csdn.net/beckel Bruce Eckel 博客中文版

6、Nicolai M. Josuttis

http://www.josuttis.com/

7、Herb Sutter

http://www.gotw.ca/

http://blog.csdn.net/hsutter/ Herb Sutter 中文博客

8、Andrei Alexandrescu

http://www.moderncppdesign.com



posted @ 2011-04-29 17:59 沛沛 閱讀(286) | 評(píng)論 (0)編輯 收藏

僅列出標(biāo)題
共7頁(yè): 1 2 3 4 5 6 7 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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国产精品| 国产麻豆91精品| 米奇777在线欧美播放| 欧美黄色日本| 香蕉乱码成人久久天堂爱免费| 性色av一区二区三区在线观看| 在线观看一区视频| 亚洲精品国精品久久99热| 国产精品久久午夜夜伦鲁鲁| 久久久噜噜噜久久| 欧美国产精品v| 欧美一级网站| 免费一级欧美片在线播放| 亚洲香蕉成视频在线观看 | 夜夜嗨av色一区二区不卡| 国产伦精品一区二区| 欧美a级在线| 国产精品福利在线观看网址| 久久人人爽爽爽人久久久| 欧美激情女人20p| 欧美一区二区三区视频在线观看| 久久三级视频| 亚洲在线播放电影| 久久久之久亚州精品露出| 一本一本大道香蕉久在线精品| 午夜精品久久久久久久99水蜜桃 | 久久精品国产久精国产爱| 欧美mv日韩mv国产网站app| 亚洲女人av| 免费观看一级特黄欧美大片| 亚洲免费视频在线观看| 久久亚洲国产成人| 午夜精品美女自拍福到在线| 麻豆成人精品| 久久国产66| 欧美伦理一区二区| 久久躁日日躁aaaaxxxx| 欧美视频一区二区三区| 美日韩精品免费| 国产精品免费网站在线观看| 亚洲成色www久久网站| 亚洲欧美日韩成人高清在线一区| 亚洲国产精品va在线看黑人 | 亚洲欧美日本另类| 米奇777超碰欧美日韩亚洲| 欧美亚洲免费在线| 欧美精品情趣视频| 美女图片一区二区| 国产精品亚洲综合色区韩国| 亚洲国产aⅴ天堂久久| 国产亚洲视频在线观看| 一区二区三区www| 亚洲欧洲日韩女同| 久久福利一区| 亚洲欧美制服另类日韩| 欧美久久婷婷综合色| 美女图片一区二区| 国产视频久久| 国产精品99久久久久久久vr| 亚洲美女视频网| 久久精品最新地址| 欧美怡红院视频| 欧美私人网站| 亚洲日本中文字幕免费在线不卡| 尤物在线观看一区| 欧美一区二区三区四区视频| 亚洲永久精品大片| 欧美日韩福利在线观看| 欧美黄色成人网| 精品9999| 久久国产精品一区二区| 新狼窝色av性久久久久久| 欧美日韩视频免费播放| 最近中文字幕日韩精品| 最新日韩精品| 久久日韩粉嫩一区二区三区| 久久久伊人欧美| 国产日韩精品久久| 午夜精品福利一区二区蜜股av| 亚洲一区二区高清| 欧美日韩午夜激情| 最新日韩在线视频| 日韩一二三在线视频播| 欧美国产1区2区| 亚洲精品1区2区| 亚洲精品一级| 欧美国产在线观看| 91久久久亚洲精品| 亚洲毛片一区二区| 欧美激情一区在线观看| 91久久线看在观草草青青| 亚洲理伦在线| 欧美精品一区二区三区在线看午夜| 亚洲成人自拍视频| 亚洲欧洲一区二区三区久久| 欧美ed2k| 亚洲日本免费| 亚洲视频电影图片偷拍一区| 欧美日韩精品免费观看视频| 日韩视频免费观看高清在线视频 | 夜夜爽夜夜爽精品视频| 中文国产成人精品久久一| 欧美日韩精品一区二区在线播放| 亚洲精品永久免费| 中文国产一区| 国产精品久久久久一区二区| 亚洲一区二区在| 久久精品国产亚洲高清剧情介绍| 国产亚洲午夜| 久久久视频精品| 欧美国产三区| 日韩亚洲欧美在线观看| 欧美日韩一区二区三区高清| 中国亚洲黄色| 欧美中文字幕久久| 一区二区亚洲| 欧美成人一品| 一区二区三区高清不卡| 欧美一区二区视频在线| 国产综合香蕉五月婷在线| 久久躁狠狠躁夜夜爽| 亚洲精品乱码| 午夜精品理论片| 精品av久久久久电影| 欧美~级网站不卡| 99精品国产在热久久| 欧美一区二区三区在线视频| 好吊日精品视频| 免费h精品视频在线播放| 亚洲毛片网站| 欧美在线播放高清精品| 伊人久久大香线蕉av超碰演员| 欧美www视频在线观看| 一区二区三区色| 久久裸体艺术| 亚洲精品视频在线看| 国产精品乱码| 久久综合色婷婷| 99热这里只有精品8| 久久久国产一区二区| 亚洲精品国产精品乱码不99按摩| 欧美色图一区二区三区| 欧美在线一级va免费观看| 亚洲第一黄色网| 亚洲欧美在线观看| 在线看视频不卡| 欧美午夜美女看片| 久久精品天堂| 99在线热播精品免费| 久久香蕉国产线看观看av| 99成人在线| 国产在线精品一区二区中文| 欧美国产精品一区| 亚洲欧美怡红院| 亚洲国产精品悠悠久久琪琪| 欧美一区二区视频97| 亚洲欧洲一级| 国产三级欧美三级| 欧美激情区在线播放| 午夜天堂精品久久久久| 亚洲黄色影院| 久久精品国产亚洲a| 一区二区欧美日韩| 国产在线一区二区三区四区| 欧美日韩国产色视频| 久久精品免费电影| 一区二区三区国产精品| 欧美ab在线视频| 欧美在线不卡视频| 99视频精品免费观看| 狠狠爱成人网| 国产精品久久国产愉拍 | 欧美黄色一区| 久久成人国产精品| 一区二区三区av| 在线精品一区二区| 国产麻豆午夜三级精品| 欧美激情五月| 久久中文字幕导航| 性欧美18~19sex高清播放| 99www免费人成精品| 欧美成人黑人xx视频免费观看| 性欧美激情精品| 亚洲少妇自拍| 亚洲人成免费| 依依成人综合视频| 国产人成一区二区三区影院| 欧美日韩综合久久| 蜜臀久久久99精品久久久久久| 欧美一区影院| 亚洲在线电影| 一区二区高清在线| 最新热久久免费视频| 欧美电影免费观看| 久久综合色影院| 久久精品亚洲精品国产欧美kt∨| 亚洲一区国产一区|