• <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>
            asm, c, c++ are my all
            -- Core In Computer
            posts - 139,  comments - 123,  trackbacks - 0

            第一條就是: const原則:在C++中最好趨向與使用const來修飾它前面的那個類型。

            如:const int a;和int const b;中,雖然兩種具有相同的意義,但是最好利用后面的那種情況,后面的更加可讀,因為:int const我們可以很清楚地看到const是修飾int,而前面的那種方法中,我們就不那么輕易知道到底const 的具體涵義。由后面的那種方法我們可以知道const指一個恒定的整形。Int *const b,指的是一個恒定的指針b,這個指針指向一個整型,所以這個指針的內容可以改變,但是它的指針值也就是b的值不能變,相應的int const *b,指的是一個指向恒定整型內容的指針b,也就是說這個b的內容可以變,但是開始的b所指向的地址中的內容不能在程序當中通過b來改變。

            volatile對上面的原則也適用。
            使用類型T做為類型變量已經作為了模板參數的慣例,用來表示函數或者類所接受的類型參量可以用所有的類型。

            在C++的模板的類型參量前,最好盡量使用typename來代替class。
            在編譯時期,模板被編譯兩次;
            實例化之前:檢查模板代碼本身,查看語法是否正確;
            在實例化期間,檢查模板代碼,查看是否所有的調用都有效。

            當使用函數模板,并且引發模板實例化的時候,編譯器需要查看模板定義。
            在函數實參的類型的推導中,如果類型出現不匹配現象則會出現編譯錯誤。如果要解決這編譯錯誤則有以下幾種方法解決:
            對傳入的實參進行類型轉換成匹配類型后傳入。
            顯示指定模板函數的全特化(不能為偏特化,因為函數不支持偏特化)類型。
            例子如下所示:

            template < typename?T >
            T?
            & max(T? & value1,T? & value2)
            {

            ??
            return ?value1 > value2 ? value1:value2;
            }


            調用max(
            3 , 4.1 )會出現編譯錯誤,解決的辦法:
            1 .max(static_cast < double > ( 3 ), 4.1 );
            2 .max < double > ( 3 );
            3 .將函數的模板參數從一個改為兩個。


            在模板函數內部不能指定默認的模板參數。
            函數不能采用偏特化的方法來實現類型的遞歸,但是它可以利用函數重載的方法來實現類型的轉換。

            相對于8而言類能使用偏特化的方法來實現類型的遞歸,并且它的仿函數也可以使用重載operator()來實現函數重載方法。但是它的一個問題就是在調用仿函數的時候一定要加上它的實例化參數類型,以及調用它的構造函數。

            函數調用的時候可以采用由參數的類型來反推函數的模板參數,這是仿函數所不能的。所以我們在編程的過程當中一定要注意這些不同技術之間的優點和不足,看看哪些更適合我們。

            在調用非標準函數的時候最好要與調用標準函數區分開來,這樣不致于使用程序產生歧義的錯誤。做法是:在變量或者函數的前面加上全局標識符::。

            posted @ 2007-01-02 23:51 Jerry Cat 閱讀(1888) | 評論 (4)編輯 收藏

            全新計算機書籍轉讓

            聯系人:趙小姐, 聯系電話:
            白天: 025-83909202(小靈通)
            晚上: 025-83408282
            Email:xiaomeng_zhao#yahoo.com.cn
            (注意將上面Email的將#換成@噢!)

            轉讓如下(南京以外的城市可能要郵購了):

            最新消息 - 新增3本超值特價書
            --------------------------
            1. 人郵出版的"Visual C++ 網絡開發技術"

            2. 清華出版的"Windows 2000/XP中文版注冊表使用開發與案例"

            3. 中國水利水電出版社的"ATM網絡技術"

            --------------------------

            英文書名: Sams Teach Yourself C# in 21 Days
            中文書名: 21天學通C#
            作者: Bradley L. Jones
            授權出版社: Sams
            被授權出版社: 人民郵電出版社
            成色: 全新
            原價: 60元
            折扣率: 同為讀書人, 您看打幾折合適吧
            現價: 您看著給吧:)


            英文書名: Programming Microsoft Windows with Microsoft Visual Basic .Net
            中文書名: Microsoft Windows程序設計 - Visual Basic .NET語言描述
            作者: Charles Petzold (由 章立民 翻譯)
            授權出版社: Microsoft
            被授權出版社: 華中科技大學出版社
            成色: 全新
            原價: 118元
            折扣率: 同為讀書人, 您看打幾折合適吧
            現價: 您看著給吧:)


            書名:? 精通Visual Basic .NET中文版
            作者: 劉炳文教授(譚浩強工作室)
            出版社: 機械工業出版社
            成色: 全新
            原價: 66元
            折扣率: 同為讀書人, 您看打幾折合適吧
            現價: 您看著給吧:)

            書名: PowerBuilder應用與開發
            出版社: 機械工業出版社
            成色: 全新
            原價: 49元
            折扣率: 同為讀書人, 您看打幾折合適吧
            現價: 您看著給吧:)



            書名: 中文Visual Basic高級編程
            出版社: 清華大學出版社
            成色: 幾乎全新
            原價: 29.8
            折扣率: 同為讀書人, 您看打幾折合適吧
            現價: 您看著給吧:)

            書名: Visual Basic實用技術指南
            出版社: 人民郵電出版社
            成色: 9成新
            原價: 38元
            折扣率: 同為讀書人, 您看打幾折合適吧
            現價: 您看著給吧:)

            posted @ 2006-12-12 01:43 Jerry Cat 閱讀(1123) | 評論 (1)編輯 收藏

            長期不變的FTP賬號

            極品匿名ftp
            ftp://219.157.126.10

            影視FTP(太原理工大學終極網域FTP)
            ftp://finalmov:finalmov@202.207.240.119

            ftp://sangel.xicp.net

            滎經電信(綜合,速度快)

            ftp://yjdx:yjdx@202.98.141.74

            極品匿名ftp(電影娛樂類,內容豐富)

            ftp://219.157.126.10

            綜合FTP
            ftp://www.mx3x.com :< wind_code_1 >

            高速ftp,不要密碼,東西暴多!!!
            ftp://221.224.20.206

            武漢安全網ftp
            ftp://315safe:315safe@ftp.315safe.com

            KingSExftp?? 帶寬:網通100m
            ftp://bbs.winzheng.com:loveyou@218.56.97.189
            影視連續劇音樂FTP:

            QUOTE:
            ftp://221.224.20.206

            ftp://202.198.35.153

            超好的音樂FTP:
            ftp://219.153.2.114/

            非匿名?? 石獅影視的臨時帳號:
            ftp://石獅影視免費下載:bbs.yssn.com石獅影視@ftp1.ssdvd.com:21

            藍魂主力FTP:
            ftp.bluesma.com:3131
            帳號:藍魂主力服務器
            密碼:藍魂論壇精彩無限
            http://www.bluesma.com[/url ]

            匿名電影ftp
            202.102.234.88

            蘭蔭補檔區主力1號
            ftp://ly_budang:reqisthebest123@ftp1.lanyin.net/

            天香APE音樂
            ftp://rt_admin:3166admin@hclhcl7.vicp.net:3000

            煙雨江南影視FTP
            ftp://煙雨江南:bbs.tc.cn@ftp.wolf2000.com/

            ftp站點(dvd)
            ftp://dvd:dvd@210.34.14.166:2121

            ftp://ydy.com.high:qSDMPJASo7XTBz@ftp.tzssyxx.com/

            ftp://shiyushen.vicp.net
            用戶名:down
            密碼:fox**fox**down
            學校教育的FTP:

            QUOTE:
            匿名ftp(化學數學軟件等)
            ftp://210.34.15.126/pub/

            香港中文大學(使用Flashfxp的請將“使用被動模式”前面的勾取消就可以登錄了。)
            ftp://ftp.cs.cuhk.edu.hk

            太原理工?
            ftp://202.207.240.119

            ftp://fszc1@57333.com
            ftp://fpdayl1@57333.com

            交大FTP
            ftp://cate:73610@202.117.28.20/

            三明高等專科學校
            ftp://smu:smc@218.5.241.10/

            ftp://210.27.144.7/mpeg/

            湖北國土資源職業學院下載

            ftp://ftp.hbgt.com.cn/

            浙江大學遠程教學ftp
            ftp://teach.zj.edu.cn
            賬號: ftpuser
            密碼: xt38kma7

            浙江廣播電視大學平陽分校FTP

            ftp://61.153.14.194/

            這個論壇實在是好呀,特把自己收藏的教育網內的ftp拿出來給大家分享!
            1.??
            ftp://scau:scau@ftp1.scau.edu.cn:8021 ;游戲+軟件 (“使用被動模式”!)

            2.?? ftp://scau:scau@ftp2.scau.edu.cn:8021 ;電視劇

            3.?? ftp://scau:scau@ftp3.scau.edu.cn:8021 ;電影

            一個學習的ftp,目錄為:sf圖書、Software、study,單線程,速度一般。
            ftp://sf:xjtumba@swds.3322.org
            一個高校的ftp,資料下載帳號 ,使用“被動模式”。
            ftp://61.153.29.19
            同濟大學的電子書庫FTP
            ftp://202.120.165.151
            成都理工大學
            ftp://ftp.cdut.edu.cn

            教授論壇FTP

            ftp://202.113.31.180/


            一高中的FTP里面有好多東東,速度200k
            ftp://teacher:lg2005@ftp.lxyg.net ?

            香港中文大學Ftp
            ftp://ftp.cuhk.hk

            FTP,(學習,游戲,電影)
            ftp://211.86.94.57
            ftp://211.86.49.66
            ftp://edutc1.hyit.edu.cn
            這3個指向同一目錄。

            ftp://210.27.234.1
            ftp://202.117.47.49
            這兩也個指向同一目錄

            甘肅電大FTP
            ftp://61.178.59.209/
            內容:常用工具軟件,電大數字圖書館,直播課堂資料,課件和四六級資料等
            可以多線程下載,速度極快。
            軟件工具和教程類:

            QUOTE:
            超多軟件下載一應俱全
            ftp://ftp.hbgt.com.cn

            一個軟件FTP
            ftp://61.132.90.12/

            華軍專用FTP
            地址:
            ftp://soft.pcsoft.com.cn
            用戶名:soft
            密碼:softsoft

            很多軟件,包括很多網管需要的軟件(由TuTu2008提供)
            ftp://61.152.101.129
            用戶名16288
            密碼都 16288

            有效FTP包括LINUX和破解
            ftp://fikusabe:928604@668y.com

            發個超多軟件的FTP,不用用戶名的,不信的看圖,看了的,就給頂下!
            ftp://221.224.20.206

            龍聯論壇
            ftp://down:bbs.51vip.net@downbbs.51vip.cn:29

            ftp://218.6.43.31

            軟件和ASP源碼FTP 剛發現的

            ftp://downaspskynet:downaspskynet @< wind_code_2 >

            播放軟件、插件、工具下載(非視頻下載)

            ftp://hdtvsoft:hdtvsoft@ftp.hd-tv.cn

            學習教材! (lsq12345:使用迅雷依然能下。)

            http://bbs.mumayi.net/viewthread ... &extra=page%3D1

            小魔術視頻表演、教程

            ftp://fbitchs1@57333.com

            電子圖書書庫 FTP下載3.12G(連接慢)

            ftp://manao.xicp.net

            雜志FTP(電子書)

            ftp://tlfadv:deiejhjhhjt@tlfs90.3322.org:9988

            深圳信息網FTP
            ftp://cd:media@media.szwebs.net

            驅動之家共享FTP

            ftp://driversd:BBSDrivers1724@218.28.45.166/

            常州市科技信息中心FTP

            ftp://218.93.19.35/

            --------------------------------

            大量精彩FTP站點:

            GNUCHINAFTP服務器 ftp.gnuchina.org
            提供各種Linux*作系統鏡像和應用軟件供下載,不限制人數,但限制線程。

            硅谷動力FTP服務器 ftp.esoftware.com.cn/
            由ENET舉辦的FTP站點,提供各種共享軟件、教程、代碼下載,允許匿名訪問和多線程下載。

            中國下載FTP服務器1號 ftp.download.com.cn/
            提供各種共享軟件、免費軟件供下載,允許匿名訪問,人數限制2650。

            中經網自由軟件FTP服務器freesoft.cei.gov.cn/
            由中國經濟信息網舉辦,除提供了大量的Linux軟件外,還設有眾多的FTP站點鏡像目錄,允許匿
            名訪問。

            中國下載FTP服務器2號ftp1.download.com.cn/
            提供各種共享軟件、免費軟件供下載,允許匿名訪問,人數限制1650,同時提供http網頁導
            航。

            黃金眼FTP服務器202.205.10.22/
            提供大量的游戲供下載,分為策略、動作、格斗、即時、角色、冒險、模擬、體育等目錄,允許
            匿名訪問。

            EastDoor亦多下載中心FTP服務器202.113.29.120/
            提供Linux、Windows、Solaris、ISO文件等軟件和電影供下載,允許匿名訪問,每個IP最大線程
            數3,人數限制100。

            TurboLinuxFTP服務器 ftp.turbolinux.com.cn/
            提供TurboLinux*作系統和各種Linux應用軟件供下載,允許匿名訪問。

            藍皮鼠臨時FTP服務器cn-ftp.dhs.org/
            提供各種軟件、資料文檔下載,允許匿名訪問,單個IP線程最高為1。

            深圳熱線FTP服務器 ftp.szonline.net/
            提供各種軟件、游戲、編程資料下載,允許匿名訪問,人數限制60。

            中國經濟信息網FTP服務器 ftp.cei.gov.cn/
            提供經濟類軟件和研究資料下載,允許匿名訪問。

            中國工程技術FTP服務器 ftp.cetin.net.cn/
            提供各種應用軟件、編程軟件供下載,允許匿名訪問。

            166.111.174.33(電影,軟件)
            166.111.184.48(電影,游戲,日劇)
            166.111.215.143(東西很少,但是有機器貓1-49集下載)
            166.111.215.175(東西不多,但是有不少大軟件)
            166.111.106.144(東西很雜,連上后進PUB目錄,然后看內容提要吧)
            166.111.107.91(音樂,軟件)
            166.111.136.248(好象都是一些3D的東東)
            166.111.141.38(軟件)
            166.111.142.6(不少大的軟件,音樂,CD就是放軟件的目錄)
            166.111.147.51(音樂,特別是國外音樂很多,可惜限了速100K)
            166.111.136.2(不少大軟件和ISO的東東)
            166.111.14.199(軟件,電影,MP3,還有一些BOOK)
            166.111.25.5(軟件,電影,連續劇,走遍美國等……)
            166.111.2.114(軟件)
            166.111.26.145(正在學習英文的朋友可以來這里,不少書喔)
            166.111.33.64(東西很少,軟件,電影)
            166.111.36.151(不少好的軟件喔..)
            166.111.37.6(軟件)
            166.111.37.82(軟件)
            166.111.41.149(音樂)
            166.111.41.172(音樂和一些電影的小片段)
            166.111.49.106(書刊)
            166.111.60.198(軟件)
            166.111.53.55(書刊,教程)
            166.111.53.84(很多ISO的好東東)
            166.111.55.29(軟件)
            166.111.61.200(電影)
            166.111.61.232(軍事)
            166.111.62.19(不少ISO的大東東,其中包括有MSDN)
            166.111.62.245(音樂)
            166.111.70.51(軟件)

            posted @ 2006-12-11 00:57 Jerry Cat 閱讀(1580) | 評論 (0)編輯 收藏

            利用MFC的Csocket類實現網絡通信

            Mail

              近年來,利用Internet進行網際間通訊,在WWW瀏 覽、FTP、Gopher這些常規服務,以及在網絡電話、多媒體會議等這些對實時性要求嚴格 的應用中成為研究的熱點,而且已經是必需的了。Windows環境下進行通訊程序設計的最基本方法是應用Windows Sockets實現進程間的通訊,為此微軟提供了大量基于Windows Sockets的通訊API,如WinSockAPI、WinInetAPI和ISAPI,并一直致力于開發更快、 更容易的通訊API,將其和MFC集成在一起以使通訊編程越來越容易。本實例重點介紹使用MFC的CSocket類編寫網絡通訊程序的方法,并通過使用CSocket類實現了網絡聊天程序。程序編譯運行后的界面效果如圖一所示:

              一、實現方法

              微軟的MFC把復雜的WinSock API函數封裝到類里,這使得編寫網絡應用程序更容易。CAsyncSocket類逐個封裝了WinSock API,為高級網絡程序員 提供了更加有力而靈活的方法。這個類基于程序員了解網絡通訊的假設,目的是為了在MFC中使用WinSock,程序員有責任處理諸如阻塞、字節順序和在Unicode與MBCS 間轉換字符的任務。為了給程序員提供更方便的接口以自動處理這些任務,MFC給出 了CSocket類,這個類是由CAsyncSocket類繼承下來的,它提供了比CAsyncSocket更高層的WinSock API接口。Csocket類和CsocketFile類可以與Carchive類一起合作來管理發送和接收的數據,這使管理數據收發更加便利。CSocket對象提供阻塞模式,這對于Carchive的同步操作是至關重要的。阻塞函數(如Receive()、Send()、ReceiveFrom()、SendTo() 和Accept())直到操作完成后才返回控制權,因此如果需要低層控制和高效率,就使用CasyncSock類;如果需要方便,則可使用Csocket類。

              一些網絡應用程序(如網絡電話、多媒體會議工具)對實時性要求非常強,要求能夠直接應用WinSock發送和接收數據。為了充分利用MFC 的優勢,首選方案應當是MFC中的CAsyncSocket類或CSocket類,這兩個類完全封裝了WinSock API,并提供更多的便利。本實例介紹應用這兩個類的編程模型,并引出相關的成員函數與一些概念的解釋。

              CSocket類是由CAsyncSocket繼承而來的,事實上,在MFC中CAsyncSocket 逐個封裝了WinSock API,每個CAsyncSocket對象代表一個Windows Socket對象,使用CAsyncSocket 類要求程序員對網絡編程較為熟悉。相比起來,CSocket類是CAsyncSocket的派生類, 繼承了它封裝的WinSock API。

              一個CSocket對象代表了一個比CAsyncSocket對象更高層次的Windows Socket的抽象,CSocket類與CSocketFile類和CArchive類一起工作來發送和接收數據,因此使用它更加容易使用。CSocket對象提供阻塞模式,因為阻塞功 能對于CArchive的同步操作是至關重要的。在這里有必要對阻塞的概念作一解釋: 一個socket可以處于"阻塞模式"或"非阻塞模式",當一個套接字處于阻塞模式(即同步操作)時,它的阻塞函數直到操作完成才會返回控制權,之所以稱為阻塞是因為此套接字的阻塞函數在完成操作返回之前什么也不能做。如果一個socket處于非阻塞模式(即異步操作),則會被調用函數立即返回。在CAsyncSocket類中可以用GetLastError 成員函數查詢最后的錯誤,如果錯誤是WSAEWOULDBLOCK則說明有阻塞,而CSocket絕不會返回WSAEWOULDBLOCK,因為它自己管理阻塞。微軟建議盡量使用非阻塞模式,通過網絡事件的發生而通知應用程序進行相應的處理。但在CSocket類中,為了利用CArchive 處理通訊中的許多問題和簡化編程,它的一些成員函數總是具有阻塞性質的,這是因為CArchive類需要同步的操作。

              在Win32環境下,如果要使用具有阻塞性質的套接字,應該放在獨立的工作線程中處理,利用多線程的方法使阻塞不至于干擾其他線程,也不會把CPU時間浪費在阻塞上。多線程的方法既可以使程序員享受CSocket帶 來的簡化編程的便利,也不會影響用戶界面對用戶的反應。

              CAsyncSocket類編程模型

              在一個MFC應用程序中,要想輕松處理多個網 絡協議,而又不犧牲靈活性時,可以考慮使用CAsyncSocket類,它的效率比CSocket 類要高。CAsyncSocket類針對字節流型套接字的編程模型簡述如下:

              1、構造一個CAsyncSocket對象,并用這個 對象的Create成員函數產生一個Socket句柄。可以按如下兩種方法構造:


            CAsyncSocket sock; //使用默認參數產生一個字節流套接字
            Sock.Create();

              或在指定端口號產生一個數據報套接字


            CAsyncSocket*pSocket=newCAsyncSocket;
            intnPort=27;
            pSocket->Create(nPort,SOCK-DGRAM);

              第一種方法在棧上產生一個CAsyncSocket對象, 而第二種方法在堆上產生CAsyncSocket對象;第一種方法中Create()成員函數用缺省參數產生一個字節流套接字,第二種方法中用Create()成員函數在指定的端口產生一個數字報套接字。Create()函數的原型為:


            BOOL Create( UINT nSocketPort = 0, int nSocketType = SOCK_STREAM,
            LPCTSTR lpszSocketAddress = NULL );

              該函數的參數有:

              1)端口,UINT類型。注意:如果是服務方,則使 用一個眾所周知的端口供服務方連接;如果是客戶方,典型做法是接受默認參數,使 套接字可以自主選擇一個可用端口;

              2)socket 類型,可以是SOCK-STREAM(默認值,字節流)或SOCK-DGRAM(數據報);

              3)socket的地址,例如" ftp.gliet.edu.cn "或"202.193.64.33"。

              2、如是客戶方程序,用CAsyncSocket∷Connect()成員函數連接到服務方;如是服務方程序,用CAsyncSocket∷Listen()成員函數開始 監聽,一旦收到連接請求,則調用CAsyncSocket∷Accept()成員函數開始接收。注意:CAsyncSocket ∷Accept()成員函數要用一個新的并且是空的CAsyncSocket對象作為它的參數,這里所說 的"空的"指的是這個新對象還沒有調用Create()成員函數。

              3、調用其他的CAsyncSocket類的Receive()、ReceiveFrom()、Send()和SendTo()等成員函數進行數據通信。

              4、通訊結束后,銷毀CAsyncSocket對象。如果是在棧上產生的CAsyncSocket對象,則對象超出定義的范圍時自動被析構;如果是在堆上產生,也就是用了new這個操作符,則必須使用delete操作符銷毀CAsyncSocket 對象。

              CSocket類編程模型

              使用CSocket對象涉及CArchive和CSocketFile 類對象。以下介紹的針對字節流型套接字的操作步驟中,只有第3步對于客戶方和服務方操作是不同的,其他步驟都相同。

              1、構造一個CSocket對象。

              2、使用這個對象的Create()成員函數產生一個socket對象。在客戶方程序中,除非需要數據報套接字,Create()函數一般情況下應該使用默認參數。而對于服務方程序,必須在調用Create時指定一個端口。需要注意的是,Carchive類對象不能與數據報(UDP)套接字一起工作,因此對于數據報套接字,CAsyncSocket和CSocket 的使用方法是一樣的。

              3、如果是客戶方套接字,則調用CAsyncSocket ∷Connect()函數與服務方套接字連接;如果是服務方套接字,則調用CAsyncSocket∷Listen()開始監聽來自客戶方的連接請求,收到連接請求后,調用CAsyncSocket∷Accept()函數接受請求,建立連接。請注意Accept()成員函數需要一個新的并且為空的CSocket對象作為它的參數,解釋同上。

              4、產生一個CSocketFile對象,并把它與CSocket 對象關聯起來。

              5、為接收和發送數據各產生一個CArchive 對象,把它們與CSocketFile對象關聯起來。切記CArchive是不能和數據報套接字一起工作的。

              6、使用CArchive對象的Read()、Write()等函數在客戶與服務方傳送數據。

              7、通訊完畢后,銷毀CArchive、CSocketFile和CSocket對象。

              二、編程步驟

              1、 啟動Visual C++6.0,生成一個基于對話框架的應用程序,將該程序命名為"Test";

              2、 按照圖一所示的效果圖設置對話框的界面;

              3、 使用Class Wizard為應用程序的按鈕添加鼠標單擊消息響應函數;

              4、 使用Class Wizard在應用程序中定義新類CNewSocket,其基類選擇為CSocket;

              5、 添加代碼,編譯運行程序。

            三、程序代碼

            ////////////////////////////////////////////////// NewSocket.h : header file
            #if !defined(AFX_NEWSOCKET_H__8CE2ED73_1D56_11D3_9928_00A0C98F3E85__INCLUDED_)
            #define AFX_NEWSOCKET_H__8CE2ED73_1D56_11D3_9928_00A0C98F3E85__INCLUDED_
            #if _MSC_VER >= 1000
            #pragma once
            #endif // _MSC_VER >= 1000
            class CTestDlg;
            #include <afxsock.h>

            class CNewSocket : public CSocket
            {
            // Attributes
            public:

            // Operations
            public:
            CNewSocket();
            virtual ~CNewSocket();

            // Overrides
            public:
            int m_Status;
            void GetDlg(CTestDlg *dlg);
            CTestDlg *m_dlg;
            // ClassWizard generated virtual function overrides
            //{{AFX_VIRTUAL(CNewSocket)
            public:
            virtual void OnAccept(int nErrorCode);
            virtual void OnReceive(int nErrorCode);
            virtual void OnClose(int nErrorCode);
            //}}AFX_VIRTUAL
            // Generated message map functions
            //{{AFX_MSG(CNewSocket)
            // NOTE - the ClassWizard will add and remove member functions here.
            //}}AFX_MSG
            // Implementation
            protected:
            };
            #endif

            //////////////////////////////////////////////////////// NewSocket.cpp : implementation file
            #include "stdafx.h"
            #include "Test.h"
            #include "NewSocket.h"
            #include "TestDlg.h"
            #ifdef _DEBUG
            #define new DEBUG_NEW
            #undef THIS_FILE
            static char THIS_FILE[] = __FILE__;
            #endif

            CNewSocket::CNewSocket()
            {}

            CNewSocket::~CNewSocket()
            {}

            #if 0
            BEGIN_MESSAGE_MAP(CNewSocket, CSocket)
            //{{AFX_MSG_MAP(CNewSocket)
            //}}AFX_MSG_MAP
            END_MESSAGE_MAP()
            #endif // 0

            void CNewSocket::OnAccept(int nErrorCode)
            {
             if (m_dlg->m_ClientSocket==NULL) m_dlg->OnAccept();
             CSocket::OnAccept(nErrorCode);
            }

            void CNewSocket::OnReceive(int nErrorCode)
            {
             m_dlg->OnReceive();
             CSocket::OnReceive(nErrorCode);
            }

            void CNewSocket::GetDlg(CTestDlg *dlg)
            {
             m_dlg=dlg;
            }

            void CNewSocket::OnClose(int nErrorCode)
            {
             m_dlg->OnClose();
             CSocket::OnClose(nErrorCode);
            }

            ///////////////////////////////////////////////////////////////// TestDlg.h : header file
            #if !defined(AFX_TESTDLG_H__EDDDE196_1BF1_11D3_BE77_0000B454AEE4__INCLUDED_)
            #define AFX_TESTDLG_H__EDDDE196_1BF1_11D3_BE77_0000B454AEE4__INCLUDED_
            #if _MSC_VER >= 1000
            #pragma once
            #endif // _MSC_VER >= 1000
            #include "NewSocket.h"

            class CTestDlg : public CDialog
            {
             // Construction
             public:
              void SocketReset();
              void OnClose();
              void OnReceive();
              void OnAccept();
              CSocketFile *m_file;
              CArchive *m_arOut;
              CArchive *m_arIn;
              CNewSocket* m_ServerSocket;
              CNewSocket* m_ClientSocket;
              CTestDlg(CWnd* pParent = NULL); // standard constructor
              // Dialog Data
              //{{AFX_DATA(CTestDlg)
              enum { IDD = IDD_TEST_DIALOG };
              CString m_Info;
              CString m_Output;
              CString m_Input;
              CString m_Connect;
              CString m_IPAddress;
              UINT m_Port;
              int m_Status;
              //}}AFX_DATA
              // ClassWizard generated virtual function overrides
              //{{AFX_VIRTUAL(CTestDlg)
             protected:
              virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
              //}}AFX_VIRTUAL
              // Implementation
             protected:
              HICON m_hIcon;
              // Generated message map functions
              //{{AFX_MSG(CTestDlg)
              virtual BOOL OnInitDialog();
              afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
              afx_msg void OnPaint();
              afx_msg HCURSOR OnQueryDragIcon();
              afx_msg void OnConnect();
              afx_msg void OnDisconnect();
              afx_msg void OnSend();
              afx_msg void OnServerradio();
              afx_msg void OnClientradio();
              afx_msg void OnSendclear();
              afx_msg void OnReceiveclear();
              //}}AFX_MSG
              DECLARE_MESSAGE_MAP()
             };
            #endif

            //////////////////////////////////////////////////////////////// TestDlg.cpp : implementation file
            #include "stdafx.h"
            #include "Test.h"
            #include "TestDlg.h"
            #include <afxsock.h>
            #ifdef _DEBUG
            #define new DEBUG_NEW
            #undef THIS_FILE
            static char THIS_FILE[] = __FILE__;
            #endif

            class CAboutDlg : public CDialog
            {
             public:
              CAboutDlg();
              // Dialog Data
              //{{AFX_DATA(CAboutDlg)
               enum { IDD = IDD_ABOUTBOX };
              //}}AFX_DATA
              // ClassWizard generated virtual function overrides
              //{{AFX_VIRTUAL(CAboutDlg)
             protected:
              virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
              //}}AFX_VIRTUAL
              // Implementation
             protected:
              //{{AFX_MSG(CAboutDlg)
              //}}AFX_MSG
             DECLARE_MESSAGE_MAP()
            };

            CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
            {
             //{{AFX_DATA_INIT(CAboutDlg)
             //}}AFX_DATA_INIT
            }

            void CAboutDlg::DoDataExchange(CDataExchange* pDX)
            {
             CDialog::DoDataExchange(pDX);
             //{{AFX_DATA_MAP(CAboutDlg)
             //}}AFX_DATA_MAP
            }

            BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
             //{{AFX_MSG_MAP(CAboutDlg)
             // No message handlers
             //}}AFX_MSG_MAP
            END_MESSAGE_MAP()

            CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/)
            : CDialog(CTestDlg::IDD, pParent)
            {
             //{{AFX_DATA_INIT(CTestDlg)
              m_Info = _T("");
              m_Output = _T("");
              m_Input = _T("");
              m_Connect = _T("");
              m_IPAddress = _T("");
              m_Port = 0;
              m_Status = -1;
             //}}AFX_DATA_INIT
             // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
             m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
            }

            void CTestDlg::DoDataExchange(CDataExchange* pDX)
            {
             CDialog::DoDataExchange(pDX);
             //{{AFX_DATA_MAP(CTestDlg)
              DDX_Text(pDX, IDC_OUTPUTEDIT, m_Output);
              DDX_Text(pDX, IDC_INPUTEDIT, m_Input);
              DDX_Text(pDX, IDC_CONNECTEDIT, m_Connect);
              DDX_Text(pDX, IDC_IPADDRESS, m_IPAddress);
              DDV_MaxChars(pDX, m_IPAddress, 15);
              DDX_Text(pDX, IDC_PORT, m_Port);
              DDX_Radio(pDX, IDC_SERVERRADIO, m_Status);
             //}}AFX_DATA_MAP
            }

            BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
            //{{AFX_MSG_MAP(CTestDlg)
             ON_WM_SYSCOMMAND()
             ON_WM_PAINT()
             ON_WM_QUERYDRAGICON()
             ON_BN_CLICKED(IDC_CONNECTBUTTON, OnConnect)
             ON_BN_CLICKED(IDC_DISCONNECTBUTTON, OnDisconnect)
             ON_BN_CLICKED(IDC_SENDBUTTON, OnSend)
             ON_BN_CLICKED(IDC_SERVERRADIO, OnServerradio)
             ON_BN_CLICKED(IDC_CLIENTRADIO, OnClientradio)
             ON_BN_CLICKED(IDC_SENDCLEARBUTTON, OnSendclear)
             ON_BN_CLICKED(IDC_RECEIVECLEARBUTTON, OnReceiveclear)
            //}}AFX_MSG_MAP
            END_MESSAGE_MAP()

            BOOL CTestDlg::OnInitDialog()
            {
             CDialog::OnInitDialog();
             // Add "About..." menu item to system menu.
             // IDM_ABOUTBOX must be in the system command range.
             ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
             ASSERT(IDM_ABOUTBOX < 0xF000);
             CMenu* pSysMenu = GetSystemMenu(FALSE);
             if (pSysMenu != NULL)
             {
              CString strAboutMenu;
              strAboutMenu.LoadString(IDS_ABOUTBOX);
              if (!strAboutMenu.IsEmpty())
              {
               pSysMenu->AppendMenu(MF_SEPARATOR);
               pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
              }
             }
             // Set the icon for this dialog. The framework does this automatically
             // when the application's main window is not a dialog
             SetIcon(m_hIcon, TRUE); // Set big icon
             SetIcon(m_hIcon, FALSE); // Set small icon
             m_Status=-1;
             m_ServerSocket=NULL;
             m_ClientSocket=NULL;
             m_arIn=NULL;
             m_arOut=NULL;
             m_file=NULL;
             m_Connect="";
             m_IPAddress="202.207.243.29";
             m_Port=5000;
             GetDlgItem(IDC_IPADDRESS)->EnableWindow(FALSE);
             GetDlgItem(IDC_PORT)->EnableWindow(FALSE);
             UpdateData(FALSE);
             return TRUE; // return TRUE unless you set the focus to a control
            }

            void CTestDlg::OnSysCommand(UINT nID, LPARAM lParam)
            {
             if ((nID & 0xFFF0) == IDM_ABOUTBOX)
             {
              CAboutDlg dlgAbout;
              dlgAbout.DoModal();
             }
             else
             {
              CDialog::OnSysCommand(nID, lParam);
             }
            }

            // If you add a minimize button to your dialog, you will need the code below
            // to draw the icon. For MFC applications using the document/view model,
            // this is automatically done for you by the framework.
            void CTestDlg::OnPaint()
            {
             if (IsIconic())
             {
              CPaintDC dc(this); // device context for painting
              SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
              // Center icon in client rectangle
              int cxIcon = GetSystemMetrics(SM_CXICON);
              int cyIcon = GetSystemMetrics(SM_CYICON);
              CRect rect;
              GetClientRect(&rect);
              int x = (rect.Width() - cxIcon + 1) / 2;
              int y = (rect.Height() - cyIcon + 1) / 2;
              // Draw the icon
              dc.DrawIcon(x, y, m_hIcon);
             }
             else
             {
              CDialog::OnPaint();
             }
            }

            // The system calls this to obtain the cursor to display while the user drags
            // the minimized window.
            HCURSOR CTestDlg::OnQueryDragIcon()
            {
             return (HCURSOR) m_hIcon;
            }

            void CTestDlg::OnConnect()
            {
             CString msg;
             UpdateData(TRUE);
             if (m_Status==0 ) //server
             {
              if ( m_ServerSocket!=NULL)
              {
               m_Connect="Please disconnect!";
               UpdateData(FALSE);
              }
              else
              {
               m_Connect="Waiting for Client...";
               UpdateData(FALSE);
               if(!AfxSocketInit())
               {
                MessageBox("WindowsSocket initial failed!","Send",MB_ICONSTOP);
                return;
               }
               m_ServerSocket=new CNewSocket;
               m_ServerSocket->m_Status=m_Status;
               m_ServerSocket->GetDlg(this);
               if(!m_ServerSocket->Create(m_Port))
                 MessageBox("SendSocket create failed!", "Send",MB_ICONSTOP);
               else
               {
                m_ServerSocket->Listen();
               }
              }
             }
             else
             {
              if (m_Status==1)
              {
               if (m_ClientSocket!=NULL)
               {
                m_Connect="Please disconnect!";
                UpdateData(FALSE);
               }
               else
               {
                m_Connect="Connect to the Server...";
                UpdateData(FALSE);
                if(!AfxSocketInit())
                {
                 MessageBox("WindowsSocket initial failed!","Receive",MB_ICONSTOP);
                 return;
                }  
                m_ClientSocket=new CNewSocket;
                m_ClientSocket->GetDlg(this);
                m_ClientSocket->m_Status=m_Status;
                if(!m_ClientSocket->Create())
                {
                 MessageBox("ReceiveSocket create failed!","Receive",MB_ICONSTOP);
                 return;
                }
                else
                {
                 if (!m_ClientSocket->Connect(m_IPAddress,m_Port))
                 {
                  CString str=m_Connect;
                  SocketReset();
                  m_Connect=str;
                  m_Connect+="Error!";
                  UpdateData(FALSE);
                 }
                 else
                 {
                  m_Connect+="OK!";
                  m_file=new CSocketFile(m_ClientSocket);
                  m_arIn=new CArchive(m_file, CArchive::load);
                  m_arOut=new CArchive(m_file, CArchive::store);
                 }
                 UpdateData(FALSE);
                }
               }
              }
             }
             if (m_Status==-1)
             {
              msg="Please choose the status!";
              AfxMessageBox(msg);
             }
            }

            void CTestDlg::OnSend()
            {
             if (m_arOut)
             {
              if (m_Status==0)
              {
               UpdateData(TRUE);
               *m_arOut<<m_Output;
               m_arOut->Flush();
              }
              else
              {
               UpdateData(TRUE);
               *m_arOut<<m_Output;
               m_arOut->Flush();
              }
             }
             else AfxMessageBox("Not connected!");
            }

            void CTestDlg::OnAccept()
            {
             m_Connect+="OK!";
             UpdateData(FALSE);
             m_ClientSocket=new CNewSocket;
             m_ClientSocket->GetDlg(this);
             m_ServerSocket->Accept(*m_ClientSocket);
             m_ClientSocket->m_Status=m_ServerSocket->m_Status;
             m_file=new CSocketFile(m_ClientSocket);
             m_arIn=new CArchive(m_file, CArchive::load);
             m_arOut=new CArchive(m_file, CArchive::store);
            }

            void CTestDlg::OnReceive()
            {
             *m_arIn>>m_Input;
             UpdateData(FALSE);
            }

            void CTestDlg::OnDisconnect()
            {
             if (m_arOut!=NULL)
             {
              SocketReset();
              m_Connect="Disconnected!";
              UpdateData(FALSE);
             }
            }

            void CTestDlg::OnClose()
            {
             if (m_ClientSocket->m_Status==0) m_Connect="Client ";
             else m_Connect="Server ";
             m_Connect+="has disconnected!";
             UpdateData(FALSE);
            }

            void CTestDlg::SocketReset()
            {
             if (m_arIn!=NULL)
             {
              delete m_arIn;
              m_arIn=NULL;
             }
             if (m_arOut!=NULL)
             {
              delete m_arOut;
              m_arOut=NULL;
             }
             if (m_file!=NULL)
             {
              delete m_file;
              m_file=NULL;
             }
             if (m_ClientSocket!=NULL)
             {
              delete m_ClientSocket;
              m_ClientSocket=NULL;
             }
             if (m_ServerSocket!=NULL)
             {
              delete m_ServerSocket;
              m_ServerSocket=NULL;
             }
             m_Connect="";
             UpdateData(FALSE);
            }

            void CTestDlg::OnServerradio()
            {
             UpdateData(TRUE);
             GetDlgItem(IDC_IPADDRESS)->EnableWindow(FALSE);
             GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
             UpdateData(FALSE);
            }

            void CTestDlg::OnClientradio()
            {
             UpdateData(TRUE);
             GetDlgItem(IDC_IPADDRESS)->EnableWindow(TRUE);
             GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
             UpdateData(FALSE);
            }

            void CTestDlg::OnSendclear()
            {
             m_Output="";
             UpdateData(FALSE);
            }

            void CTestDlg::OnReceiveclear()
            {
             m_Input="";
             UpdateData(FALSE);
            }

              四、小結

              本實例介紹了CAsyncSocket、CSocket類,并通過使用CSocket類實現了網絡聊天程序。讀者朋友還可以通過MFC CArchive 對象進行信息的接發操作,使得網絡傳輸如同使用MFC的文檔連載協議(Serialization protocol),簡捷易用。

            posted @ 2006-12-11 00:43 Jerry Cat 閱讀(1415) | 評論 (3)編輯 收藏

            /********************************************\
            |????歡迎轉載, 但請保留作者姓名和原文鏈接, 祝您進步并共勉!???? |
            \********************************************/


            C++對象模型(14) - 3.6 Pointer to Data Members
            作者: Jerry Cat
            時間: 2006/11/23
            鏈接:?
            http://www.shnenglu.com/jerysun0818/archive/2006/11/23/15593.html


            3.6 Pointer to Data Members:
            ;-----------------------------------------------------------------------

            Consider the following Point3d class declaration. It declares a virtual function, a static data member, and three coordinate values:

            class Point3d {
            public:
            ?? virtual ~Point3d(); //虛表指針的位置"非頭即尾"
            ?? // ...
            protected:
            ?? static Point3d origin;
            ?? float x, y, z;
            };

            What does it mean, then, to take the address of one of the coordinate members? For example, what value should the following yield?

            &3d_point::z;
            It is going to yield the z-coordinate's offset within the class object. Minimally, this has to be the size of the x and y members, since the language requires the members within an access level be set down in the order of declaration.

            3.6 Pointer to Data Members
            Pointers to data members are a somewhat arcane but useful feature of the language, particularly if you need to probe at the underlying member layout of a class. One example of such a probing might be to determine if the vptr is placed at the beginning or end of the class. A second use, presented in Section 3.2, might be to determine the ordering of access sections within the class. As I said, it's an arcane, although potentially useful, language feature.

            Consider the following Point3d class declaration. It declares a virtual function, a static data member, and three coordinate values:

            class Point3d {
            public:
            ?? virtual ~Point3d();
            ?? // ...
            protected:
            ?? static Point3d origin;
            ?? float x, y, z;
            };
            The member layout for each Point3d class object contains the three coordinate values in the order x, y, z and a vptr. (Recall that origin, the static data member, is hoisted outside the individual class object.) The only implementation aspect of the layout is the placement of the vptr. The Standard permits the vptr to be placed anywhere within the object: at the beginning, at the end, or in between either of the three members. In practice, all implementations place it either at the beginning or at the end.

            What does it mean, then, to take the address of one of the coordinate members? For example, what value should the following yield?

            &3d_point::z;
            It is going to yield the z-coordinate's offset within the class object. Minimally, this has to be the size of the x and y members, since the language requires the members within an access level be set down in the order of declaration.

            At the compiler's discretion, however, the vptr may be placed either before, in-between, or after the coordinate members. Again, in practice, the vptr is either placed at the beginning or at the end of the class object. On a 32-bit machine, floats are 4 bytes each, so we would expect the value to be either 8 bytes without an intervening vptr or 12 bytes with it. (The vptr, and pointers in general, use 4 bytes on a 32-bit architecture.)
            ?
            That expectation, however, is off by one—a somewhat traditional error for both C and C++ programmers.

            The physical offset of the three coordinate members within the class layout are, respectively, either 0, 4, and 8 if the vptr is placed at the end or 4, 8, and 12 if the vptr is placed at the start of the class. The value returned from taking the member's address, however, is always bumped up by 1. Thus the actual values are 1, 5, and 9, and so on. Do you see why Bjarne decided to do that?

            The problem is distinguishing between a pointer to no data member and a pointer to the first data member. Consider for example:

            float Point3d::*p1 = 0;
            float Point3d::*p2 = &Point3d::x;

            // oops: how to distinguish?
            if ( p1 == p2 ) {
            ?? cout << " p1 & p2 contain the same value — ";
            ?? cout << " they must address the same member!" << endl;
            }

            To distinguish between p1 and p2, each actual member offset value is bumped up by 1. Hence, both the compiler (and the user) must remember to subtract 1 before actually using the value to address a member.

            Given what we now know about pointers to data members, we find that explaining the difference between

            &Point3d::z;
            and
            &origin.z

            is straightforward. Whereas taking the address of a nonstatic data member yields its offset within the class, taking the address of a data member bound to an actual class object yields the member's actual address in memory. The result of

            &origin.z
            adds the offset of z (minus 1) to the beginning address of origin. origin是個實例化的類Point3d的靜態數據成員. The value returned is of type

            float*
            not
            float Point3d::*

            because it refers to an specific single instance(靜態成員屬于類而非類的各具體實例對象), much the same as taking the address of a static data member.

            Under multiple inheritance, the combination of a second (or subsequent) base class pointer to a member bound to a derived class object is complicated by the offset that needs to be added. For example, if we have

            struct Base1 { int val1; };
            struct Base2 { int val2; };
            struct Derived : Base1, Base2 { ... };

            void func1( int d::*dmp, d *pd )
            {
            ?? // expects a derived pointer to member
            ?? // what if we pass it a base pointer?
            ?? pd->*dmp;
            }
            void func2( d *pd )
            {
            ?? // assigns bmp 1
            ?? int b2::*bmp = &b2::val2;

            ?? // oops: bmp == 1,
            ?? // but in Derived, val2 == 5
            ?? func1( bmp, pd )
            }
            bmp must be adjusted by the size of the intervening Base1 class when passed as the first argument to func1(). Otherwise, the invocation of

            pd->*dmp;
            within func1() will access Base1::val1, not Base2::val2 as the programmer intended. The specific solution in this case is

            // internal transformation by compiler
            func1( bmp + sizeof( Base1 ), pd );
            In general, however, we cannot guarantee that bmp is not 0 and so must guard against it:

            // internal transformation
            // guarding against bmp == 0
            func1( bmp ? bmp + sizeof( Base1 ) : 0, pd );

            二. Efficiency of Pointers to Members:

            The following sequence of tests attempts to gain some measure of the overhead associated with using pointers to members under the various class representations of the 3D point. In the first two cases, there is no inheritance. The first case takes the address of a bound member:

            float *ax = &pA.x;
            for the three coordinate members of points pA and pB. The assignment, addition, and subtraction look as follows:

            *bx = *ax - *bz;
            *by = *ay + *bx;
            *bz = *az + *by;
            The second case takes the address of a pointer to data member:

            float pt3d::*ax = &pt3d::x;
            for the three coordinate members. The assignment, addition, and subtraction use the pointer to data member syntax, binding the values to the objects pA and pB:

            pB.*bx = pA.*ax - pB.*bz;
            pB.*by = pA.*ay + pB.*bx;
            pB.*bz = pA.*az + pB.*by;
            Recall that the direct data member exercise of this function, executed in Section 3.5, ran with an average user time of 0.80 with optimization turned on and 1.42 with optimization turned off for both compilers. The results of running these two tests, coupled with the results of the direct data access, are shown in Table 3.3:

            Table 3.3. Nonstatic Data Member Access
            ???
            ???????????????????? Optimized?????? Non-optimized

            Direct Access??????? 0.80????????????? 1.42
            Pointer to
            ?? Bound Member????? 0.80????????????? 3.04
            ?
            Pointer to
            ?? Data Member
            ????? CC???????????? 0.80????????????? 5.34
            ????? NCC??????????? 4.04????????????? 5.34


            The non-optimized results conform to expectations. That is, the addition of one indirection per member access through the bound pointer more than doubles the execution time. The pointer-to-member access again nearly doubles the execution time. The binding of the pointer to data member to the class object requires the addition of the offset minus 1 to the address of the object. More important, of course, the optimizer is able to bring the performance of all three access strategies into conformance, except the anomalous behavior of the NCC optimizer. (It is interesting to note here that the appalling performance of the NCC executable under optimization reflects a poor optimization of the generated assembly code and not an attribute of the source-level C++ code. An examination of the generated non-optimized assembly for both CC and NCC showed the two outputs to be identical.)

            The next set of tests looks at the impact of inheritance on the performance of pointers to data members. In the first case, the independent Point class is redesigned into a three-level single inheritance hierarchy with one coordinate value as a member of each class:

            class Point { ... }; // float x;
            class Point2d : public Point?? { ... }; // float y;
            class Point3d : public Point2d { ... }; // float z;
            The next representation retains the three-level single inheritance hierarchy but introduces one level of virtual inheritance: the Point2d class is virtually derived from Point. As a result, each access of Point::x is now accessing a virtual base class data member. Then, more out of curiosity than need, the final representation added a second level of virtual inheritance, that of Point3d being virtually derived from Point2d. Table 3.4 shows the results. (Note: The poor performance of the NCC optimizer was consistent across the tests, so I've left it off the listing.)

            Table 3.4. Pointer to Data Member Access
            ???
            ???????????????????? Optimized?? %?? Non-optimized
            ?
            No Inheritance?????? 0.80????????????? 5.34
            SI (3 levels)??????? 0.80????????????? 5.34
            VI (1 level)???????? 1.60????????????? 5.44
            VI (2 level)???????? 2.14????????????? 5.51
            ?
            SI:? Single Inheritance?????? VI:? Virtual Inheritance


            Because inherited data members are stored directly within the class object, the introduction of inheritance does not affect the performance of the code at all. The major impact of introducing virtual inheritance is to impede the effectiveness of the optimizer. Why? In these two implementations, each level of virtual inheritance introduces an additional level of indirection. Under both implementations, each access of Point::x, such as

            pB.*bx
            is translated into
            &pB->__vbcPoint + ( bx - 1 )

            rather than the more direct
            &pB + ( bx - 1 )

            The additional indirection reduced the ability of the optimizer to move all the processing into registers.

            posted @ 2006-11-23 20:23 Jerry Cat 閱讀(1171) | 評論 (0)編輯 收藏
            用內聯匯編調虛函數, 理解VTABLE原理

            虛函數和動態綁定是C++面向對象編程的核心內容之一。要理解C++虛函數的調用本質,就不得不說VPTR和VTABLE。所有擁有虛函數的C++類的大小都比可看到的內容多至少4個字節(如果派生樹中存在多繼承,就可能多于4個字節),這多出來的4個字節就是VPTR,它位于每個實例的最前方。VPTR的內容就是一個unsigned int的地址,指向一個內存區域,而這個被指向的內存區域就是VTABLE,所謂虛函數地址表。每個擁有虛函數的類都擁有一張VTABLE,里面是一個函數指針數組,每4個字節為一個單位,指向虛函數的入口地址。C++在調用虛函數時,首先要通過這個類的實例內容看到VPTR,從而找到VTABLE,然后根據要調用的虛函數,取相應偏移地址的內容,從而把調用轉到這個位置。

            以下是我用VC內聯匯編編寫的一個手工模擬此過程的示例,通過例子中的代碼可以清楚的看出虛函數的調用過程。首先定義一個類,它有一個虛函數:


            class ?Class1
            {
            ?
            int
            ?i;
            public
            :
            ?Class1(
            int ?i)? {? this -> i? = ?i;?}

            ?
            virtual ? void ?Print( int ?a,? int ?b)? {?printf( " i=%d?a=%d?b=%d " ,?i,?a,?b);?}
            }
            ;

            如果取這個類的大小,可以看到結果是8而不是4。
            下面聲明這個類的一個實例,并取得其VTABLE中第一個元素的值:

            ?Class1 * ?pC? = ? new ?Class1( 1 );
            ?
            int ?addr? = ? * ( int * )( * ( int * )pC);

            注意取值的這一行運用了復雜的強制類型轉換。我把它拆開解釋一下。首先是取得對象pC的前四個字節的內容,只要把pC轉換成int*然后直接取值就行了:
            ?*(int*)pC

            下一步是把取得的這個值當作是一個指針,也就是再進行一次強制類型轉換:
            ?(int*)(*(int*)pC)

            最后取這個指針所指內存的內容,也就是VTABLE中第一個函數的地址了:
            ?*(int*)(*(int*)pC)

            取得這個地址以后,下面就用匯編代碼來調用這個地址所指的函數:

            ?__asm
            ?
            {
            ??mov?ecx,?pC;
            ??push?
            3
            ;
            ??push?
            2
            ;
            ??call?addr;
            ?}


            注意調用類的非靜態成員函數時需要先把對應實例的地址放到ECX寄存器中,也就是平常所說的“隱藏參數”了,然后為函數Print壓兩個參數進棧,根據運行的結果可以明顯看出來調用類成員函數時也是從右向左壓棧的,最后用call語句調用函數。不難發現調用類成員函數在參數個數確定時也是由被調用者負責彈棧,看來類成員函數也是可以聲明為參數個數可變的函數了。

            最后,執行程序,得到結果:
            i=1 a=2 b=3?

            posted @ 2006-11-19 04:13 Jerry Cat 閱讀(2843) | 評論 (4)編輯 收藏

            /********************************************\
            |????歡迎轉載, 但請保留作者姓名和原文鏈接, 祝您進步并共勉!???? |
            \********************************************/


            C++對象模型(13) - 3.5 Object Member Efficiency
            作者: Jerry Cat
            時間: 2006/11/17
            鏈接:?
            http://www.shnenglu.com/jerysun0818/archive/2006/11/17/15311.html


            3.5 Object Member Efficiency
            ;-----------------------------------------------------------------------

            An obvious observation is that without the optimizer turned on, it is extremely difficult to guess at the performance characteristics of a program, since the code is potentially hostage to the "quirk(s) of code generation…unique to a particular compiler." Before one begins source level "optimizations" to speed up a program, one should always do actual performance measurements rather than relying on speculation and common sense.不要想當然, 要試驗之.

            In the next sequence of tests, I introduced first a three-level single inheritance representation of the Point abstraction and then a virtual inheritance representation of the Point abstraction. I tested both direct and inline access (multiple inheritance did not fit naturally into the model, so I decided to forego it.) The general hierarchy is

            class Point1d {...};?????????????????????????? // maintains x
            class Point2d : public Point1d {...};???? // maintains y
            class Point3d : public Point2d {...};???? // maintains z

            The one-level virtual inheritance derived Point2d virtually from Point1d. The two-level virtual inheritance additionally derived Point3d virtually from Point2d. Table 3.2 lists the results of running the tests for both compilers. (Again, I break out the times for the two compilers only when their performances differ significantly from each other's.)

            Table 3.2. Data Access under Inheritance Models

            ???????????????????????????????????? Optimized?????? Non-optimized
            Single Inheritance
            ???? Direct Access?????????? 0.80???????????????? 1.42
            ???? Inline Methods
            ???? CC???????????????????????????0.80??????????????? 2.55
            ???? NCC????????????????????????0.80??????????????? 3.10
            ?
            Virtual Inheritance — 1-Level
            ???? Direct Access??????????? 1.60????????????? 1.94
            ???? Inline Methods
            ???? CC????????????????????????????1.60????????????? 2.75
            ???? NCC?????????????????????????1.60????????????? 3.30
            ?
            Virtual Inheritance — 2-Level
            ???? Direct Access
            ???? CC????????????????????????????2.25????????????? 2.74
            ??? ?NCC?????????????????????????3.04????????????? 3.68
            ?
            ??? Inline Methods
            ???? CC????????????????????????????2.25????????????? 3.22
            ???? NCC?????????????????????????2.50????????????? 3.81
            ?
            Single inheritance should not affect the test performance, since the members are stored contiguously within the derived class object and their offsets are known at compile time. The results, as expected, were exactly the same as those of the independent abstract data type. (The same should be true under multiple inheritance, but I didn't confirm that.)

            Again, it is worth noting that with the optimizer off, performance, which common sense says should be the same (direct member access versus inline access), is in practice slower in the case of inline functions. The lesson again is that the programmer concerned with efficiency must actually measure the performance of his or her program and not leave the measurement of the program to speculation and assumption. It is also worth noting that optimizers don't always work. I've more than once had compilations fail with an optimizer turned on that compiled fine "normally."別想當然, 實驗之! 編譯時盡可能打開優化開關.

            The virtual inheritance performance is disappointing in that neither compiler recognized that the access of the inherited data member pt1d::_x is through a nonpolymorphic class object and that therefore indirect runtime access is unnecessary. Both compilers generate indirect access of pt1d::_x (and pt1d::y in the case of two levels of virtual inheritance), even though its location within the two Point3d objects is fixed at compile time. The indirection significantly inhibited the optimizer's ability to move all the operations within registers. The indirection did not affect the non-optimized executables significantly.
            虛繼承導致性能大降, 即使打開優化開關也沒太大起色.

            posted @ 2006-11-17 18:11 Jerry Cat 閱讀(725) | 評論 (0)編輯 收藏

            /********************************************\
            |????歡迎轉載, 但請保留作者姓名和原文鏈接, 祝您進步并共勉!???? |
            \********************************************/


            C++對象模型(12) - 3.4 Inheritance and the Data Member
            作者: Jerry Cat
            時間: 2006/11/16
            鏈接:?
            http://www.shnenglu.com/jerysun0818/archive/2006/11/16/15269.html


            3.4 Inheritance and the Data Member
            ;-----------------------------------------------------------------------

            Under the C++ inheritance model, a derived class object is represented as the concatenation of its members with those of its base class(es). The actual ordering of the derived and base class parts is left unspecified by the Standard. In theory, a compiler is free to place either the base or the derived part first in the derived class object. In practice, the base class members always appear first, except in the case of a virtual base class. (In general, the handling of a virtual base class is an exception to all generalities, even, of course, this one.)

            class Concrete1 {
            public:
            ?? // ...
            protected:
            ?? int val;
            ?? char bit1;
            };
            class Concrete2 : public Concrete1 {
            public:
            ?? // ...
            protected:
            ?? char bit2;
            };

            class Concrete3 : public Concrete2 {
            public:
            ?? // ...
            protected:
            ?? char bit3;
            };
            From a design standpoint, this representation may make more sense. From an implementation standpoint, however, we may be distressed to find that a Concrete3 class object now has a size of 16 bytes—double its previous size.

            What's going on? Recall that the issue is the integrity of the base class subobject within the derived class. Let's walk through the layout of the inheritance hierarchy to see what is going on.

            The Concrete1 class contains the two members—val and bit1—that together take up 5 bytes. The size of a Concrete1 class object, however, is 8 bytes: the 5 bytes of actual size plus 3 bytes of padding to align the object on a machine word boundary. That's as true in C as it is in C++; generally, alignment constraints are determined by the underlying processor.

            粗心的程序員可要倒霉咯!
            Nothing necessarily to complain about so far. It's the layout of the derived class that typically drives the unwary programmer into fits of either perplexity or angry indignation. Concrete2 adds a single nonstatic data member, bit2, of type char. Our unwary programmer expects it to be packed into the base Concrete1 representation, taking up one of the bytes otherwise wasted as alignment padding. This layout strategy makes the Concrete2 class object also of size 8 bytes, with 2 bytes of padding.

            The layout of the Concrete2 class, however, instead preserves the 3 bytes of padding within the Concrete1 base class subobject. The bit2 member is set down after that, followed by an additional 3 bytes of padding. The size of a Concrete2 class object is 12 bytes, not 8, with 6 bytes wasted for padding. The same layout algorithm results in a Concrete3 class object's being 16 bytes, 9 of which are wasted on padding.

            Why? Let's declare the following set of pointers:

            Concrete2 *pc2;
            Concrete1 *pc1_1, *pc2_2;

            Both pc1_1 and pc2_2 can address objects of either three classes. The following assignment

            *pc1_1 = *pc2_2;
            should perform a default memberwise copy of the Concrete1 portion of the object addressed. If pc1_1 addresses a Concrete2 or Concrete3 object, that should not be of consequence to the assignment of its Concrete1 subobject.

            However, if the language were to pack the derived class members Concrete2::bit2 or Concrete3::bit3 into the Concrete1 subobject, these language semantics could not be preserved. An assignment such as

            pc1_1 = pc2;

            // oops: derived class subobject is overridden
            // its bit2 member now has an undefined value
            *pc1_1 = *pc2_2;
            would overwrite the values of the packed inherited members. It would be an enormous effort on the user's part to debug this, to say the least.

            二. Adding Polymorphism:
            If we want to operate on a point independent of whether it is a Point2d or Point3d instance, we need to provide a virtual function interface within our hierarchy. Let's see how things change when we do that:

            class Point2d {
            public:
            ?? Point2d( float x = 0.0, float y = 0.0 )
            ????? : _x( x ), _y( y ) {};

            ?? // access functions for x & y same as above
            ?? // invariant across type: not made virtual

            ?? // add placeholders for z — do nothing ...
            ?? virtual float z(){ return 0.0 };
            ?? virtual void z( float ) {}
            ?? // turn type explicit operations virtual
            ?? virtual void
            ?? operator+=( const Point2d& rhs ) {
            ?????? _x += rhs.x(); _y += rhs.y(); }

            ?? // ... more members
            protected:
            ?? float _x, _y;
            };
            It makes sense to introduce a virtual interface into our design only if we intend to manipulate two- and three-dimensional points polymorphically, that is, to write code such as

            where p1 and p2 may be either two- or three-dimensional points. This is not something that any of our previous designs supported. This flexibility, of course, is at the heart of OO programming. Support for this flexibility, however, does introduce a number of space and access-time overheads for our Point2d class:

            (1). Introduction of a virtual table associated with Point2d to hold the address of each virtual function it declares. The size of this table in general is the number of virtual functions declared plus an additional one or two slots to support runtime type identification.

            (2). Introduction of the vptr within each class object. The vptr provides the runtime link for an object to efficiently find its associated virtual table.

            (3). Augmentation of the constructor to initialize the object's vptr to the virtual table of the class. Depending on the aggressiveness of the compiler's optimization, this may mean resetting the vptr within the derived and each base class constructor. (This is discussed in more detail in Chapter 5.)

            (4). Augmentation of the destructor to reset the vptr to the associated virtual table of the class. (It is likely to have been set to address the virtual table of the derived class within the destructor of the derived class. Remember, the order of destructor calls is in reverse: derived class and then base class.) An aggressive optimizing compiler can suppress a great many of these assignments.

            Here is our new Point3d derivation:

            class Point3d : public Point2d {
            public:
            ?? Point3d( float x = 0.0, float y = 0.0, float z = 0.0 )
            ????? : Point2d( x, y ), _z( z ) {};
            ?? float z() { return _z; }
            ?? void z( float newZ ) { _z = newZ; }

            ?? void operator+=( const Point2d& rhs ) {
            ????? Point2d::operator+=( rhs );
            ????? _z += rhs.z();
            ?? }
            ?? // ... more members
            protected:
            ?? float _z;
            };

            Although the syntax of the class's declaration has not changed, everything about it is now different: The two z() member functions and the operator+=() operator are virtual instances. Each Point3d class object contains an additional vptr member object (the instance inherited from Point2d). There is also a Point3d virtual table. The invocation of each member function made virtual is also more complex (this is covered in Chapter 4).

            Placing the vptr at the start of the class is more efficient in supporting some virtual function invocations through pointers to class members under multiple inheritance (see Section 4.4). Otherwise, not only must the offset to the start of the class be made available at runtime, but also the offset to the location of the vptr of that class must be made available. The trade-off, however, is a loss in C language interoperability.

            三. Multiple Inheritance:
            Single inheritance provides a form of "natural" polymorphism regarding the conversion between base and derived types within the inheritance hierarchy. Look at Figures 3.1(b), 3.2(a), or 3.3, where you can see that the base and derived class objects both begin at the same address. They differ in that the derived object extends the length of its nonstatic data members. The assignment, such as

            Point3d p3d;
            Point2d *p = &p3d;
            of the derived class object to a pointer or reference to the base class (regardless of the depth of the inheritance hierarchy) requires no compiler intervention or modification of the address. Instead, it happens "naturally," and in that sense, it provides optimal runtime efficiency.

            From Figure 3.2(b), note that placing the vptr at the beginning of the class object breaks the natural polymorphism of single inheritance in the special case of a base class without virtual functions and a derived class with them. The conversion of the derived object to the base in this case requires the intervention of the compiler in order to adjust the address being assigned by the size of the vptr. Under both multiple and virtual inheritances, the need for compiler intervention is considerably more pronounced.

            Multiple inheritance is neither as well behaved nor as easily modeled as single inheritance. The complexity of multiple inheritance lies in the "unnatural" relationship of the derived class with its second and subsequent base class subobjects. Consider, for example, the following multiply derived class, Vertex2d:

            class Point2d {
            public:
            ?? // ...
            protected:
            ?? float _x, _y;
            };

            class Vertex {
            public:
            ?? // ...
            protected:
            ?? Vertex *next;
            };

            class Vertex2d :
            ?? public Point2d, public Vertex {
            public:
            ?? //...
            protected:
            ?? float mumble;
            };
            The problem of multiple inheritance primarily affects conversions between the derived and second or subsequent base class objects, either directly

            extern void mumble( const Vertex& );
            Vertex3d v;
            ...
            // conversion of a Vertex3d to Vertex is ``unnatural''
            mumble( v );
            or through support for the virtual function mechanism. The problems with supporting virtual function invocation are discussed in Section 4.2.

            The assignment of the address of a multiply derived object to a pointer of its leftmost (that is, first) base class is the same as that for single inheritance, since both point to the same beginning address. The cost is simply the assignment of that address (Figure 3.4 shows the multiple inheritance layout). The assignment of the address of a second or subsequent base class, however, requires that that address be modified by the addition (or subtraction in the case of a downcast) of the size of the intervening base class subobject(s).

            What about access of a data member of a second or subsequent base class? Is there an additional cost? No. The member's location is fixed at compile time. Hence its access is a simple offset the same as under single inheritance regardless of whether it is a pointer, reference, or object through which the member is being accessed.

            四. Virtual Inheritance:
            A semantic side effect of multiple inheritance is the need to support a form of shared subobject inheritance. The classic example of this is the original iostream library implementation:

            //pre-standard iostream implementation
            class ios { ... };
            class istream : public ios { ... };
            class ostream : public ios { ... };
            class iostream :
            ?? public istream, public ostream { ... };
            Both the istream and ostream classes contain an ios subobject. In the layout of iostream, however, we need only a single ios subobject. The language level solution is the introduction of virtual inheritance:

            class ios { ... };
            class istream : public virtual ios { ... };
            class ostream : public virtual ios { ... };
            class iostream :
            ?? public istream, public ostream { ... };

            The general implementation solution is as follows. A class containing one or more virtual base class subobjects, such as istream, is divided into two regions: an invariant region and a shared region. Data within the invariant region remains at a fixed offset from the start of the object regardless of subsequent derivations. So members within the invariant region can be accessed directly. The shared region represents the virtual base class subobjects. The location of data within the shared region fluctuates with each derivation. So members within the shared region need to be accessed indirectly. What has varied among implementations is the method of indirect access. The following example illustrates the three predominant strategies. Here is the data portion of a virtual Vertex3d inheritance hierarchy:

            class Point2d {
            public:
            ?? ...
            protected:
            ?? float _x, _y;
            };

            class Vertex : public virtual Point2d {
            public:
            ?? ...
            protected:
            ?? Vertex *next;
            };

            class Point3d : public virtual Point2d {
            public:
            ?? ...
            protected:
            ?? float _z;
            };
            class Vertex3d :
            ?? public Point3d, public Vertex {
            public:
            ?? ...
            protected:
            ?? float mumble;
            };
            The general layout strategy is to first lay down the invariant region of the derived class and then build up the shared region.

            However, one problem remains: How is the implementation to gain access to the shared region of the class? In the original cfront implementation, a pointer to each virtual base class is inserted within each derived class object. Access of the inherited virtual base class members is achieved indirectly through the associated pointer. For example, if we have the following Point3d operator:

            void
            Point3d::
            operator+=( const Point3d &rhs )
            {
            ?? _x += rhs._x;
            ?? _y += rhs._y;
            ?? _z += rhs._z;
            };
            under the cfront strategy, this is transformed internally into

            // Pseudo C++ Code
            __vbcPoint2d->_x += rhs.__vbcPoint2d->_x;
            __vbcPoint2d->_y += rhs.__vbcPoint2d->_y;
            _z += rhs._z;

            A conversion between the derived and base class instances, such as

            Vertex *pv = pv3d;
            under the cfront implementation model becomes

            // Pseudo C++ code
            Vertex *pv = pv3d ? pv3d->__vbcPoint2d : 0;

            3.4 Inheritance and the Data Member
            Under the C++ inheritance model, a derived class object is represented as the concatenation of its members with those of its base class(es). The actual ordering of the derived and base class parts is left unspecified by the Standard. In theory, a compiler is free to place either the base or the derived part first in the derived class object. In practice, the base class members always appear first, except in the case of a virtual base class. (In general, the handling of a virtual base class is an exception to all generalities, even, of course, this one.)

            Given this inheritance model, one can ask: What is the difference in providing two abstract data types for the representation of two- and three-dimensional points, such as

            // supporting abstract data types
            class Point2d {
            public:
            ?? // constructor(s)
            ?? // operations
            ?? // access functions
            private:
            ?? float x, y;
            };

            class Point3d {
            public:
            ?? // constructor(s)
            ?? // operations
            ?? // access functions
            private:
            ?? float x, y, z;
            };
            and providing a two- or three-level hierarchy in which each additional dimension is a class derived from the lower dimension? In the following subsections, the effects of single inheritance without the support of virtual functions, single inheritance with virtual functions, multiple inheritance, and virtual inheritance are examined. Figure 3.1(a) pictures the layout of Point2d and Point3d objects. (In the absence of virtual functions, they are equivalent to C struct declarations.)

            Figure 3.1(a). Data Layout: Independent Structs


            Inheritance without Polymorphism
            Imagine that the programmer wishes to share an implementation but continue to use type-specific instances of either the two- or three-dimensional point. One design strategy is to derive Point3d from our Point2d class, with Point 3d inheriting all the operations and maintenance of the x- and y-coordinates. The effect is to localize and share data and the operations upon that data among two or more related abstractions. In general, concrete inheritance adds no space or access-time overhead to the representation.

            class Point2d {
            public:
            ?? Point2d( float x = 0.0, float y = 0.0 )
            ????? : _x( x ), _y( y ) {};

            ?? float x() { return _x; }
            ?? float y() { return _y; }

            ?? void x( float newX ) { _x = newX; }
            ?? void y( float newY ) { _y = newY; }

            ?? void operator+=( const Point2d& rhs ) {
            ????? _x += rhs.x();
            ????? _y += rhs.y();
            ?? }
            ????? // ... more members
            ?? protected:
            ????? float _x, _y;
            ?? };

            ?? // inheritance from concrete class
            ?? class Point3d : public Point2d {
            ?? public:
            ????? Point3d( float x = 0.0, float y = 0.0, float z = 0.0 )
            ???????? : Point2d( x, y ), _z( z ) {};

            ????? float z() { return _z; }
            ????? void z( float newZ ) { _z = newZ; }

            ????? void operator+=( const Point3d& rhs ) {
            ???????? Point2d::operator+=( rhs );
            ???????? _z += rhs.z();
            ????? }
            ????? // ... more members
            ?? protected:
            ????? float _z;
            ?? };
            The benefit of this design strategy is the localization of the code to manage the x- and y-coordinates. In addition, the design clearly indicates the tight coupling of the two abstractions. The declaration and use of both Point2d and Point3d class objects do not change from when the two classes were independent, so clients of these abstractions need not be aware of whether the objects are independent class types or related through inheritance. Figure 3.1(b) shows the layout of the Point2d and Point3d inheritance layout without the declaration of a virtual interface.

            Figure 3.1(b). Data Layout: Single Inheritance without Virtual Functions


            What are the possible pitfalls of transforming two independent classes into a type/subtype relationship through inheritance? A naive design might, in fact, double the number of function calls to perform the same operations. That is, say the constructor or operator+=() in our example were not made inline (or the compiler could not for some reason support the inlining of the member functions). The initialization or addition of a Point3d object would be the cost of the partial Point2d and Point3d instances. In general, choosing candidate functions for inlining is an important, if unglamorous, aspect of class design. Confirming that they are in fact inlined is necessary before final release of the implementation.

            A second possible pitfall in factoring a class into a two-level or deeper hierarchy is a possible bloating of the space necessary to represent the abstraction as a class hierarchy. The issue is the language guarantee of the integrity of the base class subobject within the derived class. It's slightly subtle. A walk-through of an example might best explain it. Let's begin with a concrete class:

            class Concrete {
            public:
            ?? // ...
            private:
            ?? int val;
            ?? char c1;
            ?? char c2;
            ?? char c3;
            };
            On a 32-bit machine, the size of each Concrete class object is going to be 8 bytes, broken down as follows:

            4 bytes for val

            1 byte each for c1, c2, and c3

            1 byte for the alignment of the class on a word boundary

            Say, after some analysis, we decide that a more logical representation splits Concrete into a three-level inheritance hierarchy as follows:

            class Concrete1 {
            public:
            ?? // ...
            protected:
            ?? int val;
            ?? char bit1;
            };
            class Concrete2 : public Concrete1 {
            public:
            ?? // ...
            protected:
            ?? char bit2;
            };

            class Concrete3 : public Concrete2 {
            public:
            ?? // ...
            protected:
            ?? char bit3;
            };
            From a design standpoint, this representation may make more sense. From an implementation standpoint, however, we may be distressed to find that a Concrete3 class object now has a size of 16 bytes—double its previous size.

            What's going on? Recall that the issue is the integrity of the base class subobject within the derived class. Let's walk through the layout of the inheritance hierarchy to see what is going on.

            The Concrete1 class contains the two members—val and bit1—that together take up 5 bytes. The size of a Concrete1 class object, however, is 8 bytes: the 5 bytes of actual size plus 3 bytes of padding to align the object on a machine word boundary. That's as true in C as it is in C++; generally, alignment constraints are determined by the underlying processor.

            Nothing necessarily to complain about so far. It's the layout of the derived class that typically drives the unwary programmer into fits of either perplexity or angry indignation. Concrete2 adds a single nonstatic data member, bit2, of type char. Our unwary programmer expects it to be packed into the base Concrete1 representation, taking up one of the bytes otherwise wasted as alignment padding. This layout strategy makes the Concrete2 class object also of size 8 bytes, with 2 bytes of padding.

            The layout of the Concrete2 class, however, instead preserves the 3 bytes of padding within the Concrete1 base class subobject. The bit2 member is set down after that, followed by an additional 3 bytes of padding. The size of a Concrete2 class object is 12 bytes, not 8, with 6 bytes wasted for padding. The same layout algorithm results in a Concrete3 class object's being 16 bytes, 9 of which are wasted on padding.

            "That's stupid," is the unwary programmer's judgment, which more than one has chosen to share with me over e-mail, on the phone, and in per-son. Do you see why the language behaves as it does?

            Let's declare the following set of pointers:

            Concrete2 *pc2;
            Concrete1 *pc1_1, *pc2_2;
            Both pc1_1 and pc2_2 can address objects of either three classes. The following assignment

            *pc1_1 = *pc2_2;
            should perform a default memberwise copy of the Concrete1 portion of the object addressed. If pc1_1 addresses a Concrete2 or Concrete3 object, that should not be of consequence to the assignment of its Concrete1 subobject.

            However, if the language were to pack the derived class members Concrete2::bit2 or Concrete3::bit3 into the Concrete1 subobject, these language semantics could not be preserved. An assignment such as

            pc1_1 = pc2;

            // oops: derived class subobject is overridden
            // its bit2 member now has an undefined value
            *pc1_1 = *pc2_2;
            would overwrite the values of the packed inherited members. It would be an enormous effort on the user's part to debug this, to say the least.

            Adding Polymorphism
            If we want to operate on a point independent of whether it is a Point2d or Point3d instance, we need to provide a virtual function interface within our hierarchy. Let's see how things change when we do that:

            class Point2d {
            public:
            ?? Point2d( float x = 0.0, float y = 0.0 )
            ????? : _x( x ), _y( y ) {};

            ?? // access functions for x & y same as above
            ?? // invariant across type: not made virtual

            ?? // add placeholders for z — do nothing ...
            ?? virtual float z(){ return 0.0 };
            ?? virtual void z( float ) {}
            ?? // turn type explicit operations virtual
            ?? virtual void
            ?? operator+=( const Point2d& rhs ) {
            ?????? _x += rhs.x(); _y += rhs.y(); }

            ?? // ... more members
            protected:
            ?? float _x, _y;
            };
            It makes sense to introduce a virtual interface into our design only if we intend to manipulate two- and three-dimensional points polymorphically, that is, to write code such as

            void foo( Point2d &p1, Point2d &p2 ) {
            ?? // ...
            ?? p1 += p2;
            ?? // ...
            }
            where p1 and p2 may be either two- or three-dimensional points. This is not something that any of our previous designs supported. This flexibility, of course, is at the heart of OO programming. Support for this flexibility, however, does introduce a number of space and access-time overheads for our Point2d class:

            Introduction of a virtual table associated with Point2d to hold the address of each virtual function it declares. The size of this table in general is the number of virtual functions declared plus an additional one or two slots to support runtime type identification.

            Introduction of the vptr within each class object. The vptr provides the runtime link for an object to efficiently find its associated virtual table.

            Augmentation of the constructor to initialize the object's vptr to the virtual table of the class. Depending on the aggressiveness of the compiler's optimization, this may mean resetting the vptr within the derived and each base class constructor. (This is discussed in more detail in Chapter 5.)

            Augmentation of the destructor to reset the vptr to the associated virtual table of the class. (It is likely to have been set to address the virtual table of the derived class within the destructor of the derived class. Remember, the order of destructor calls is in reverse: derived class and then base class.) An aggressive optimizing compiler can suppress a great many of these assignments.

            The impact of these overheads depends on the number and lifetime of the Point2d objects being manipulated and the benefits gained in programming the objects polymorphically. If an application knows its use of point objects is limited to either (but not both) two- or three-dimensional points, the overheads of this design may become unacceptable. [1]

            [1] I am not aware of any production system actually making use of a polymorphic Point hierarchy.

            Here is our new Point3d derivation:

            class Point3d : public Point2d {
            public:
            ?? Point3d( float x = 0.0, float y = 0.0, float z = 0.0 )
            ????? : Point2d( x, y ), _z( z ) {};
            ?? float z() { return _z; }
            ?? void z( float newZ ) { _z = newZ; }

            ?? void operator+=( const Point2d& rhs ) {
            ????? Point2d::operator+=( rhs );
            ????? _z += rhs.z();
            ?? }
            ?? // ... more members
            protected:
            ?? float _z;
            };
            Although the syntax of the class's declaration has not changed, everything about it is now different: The two z() member functions and the operator+=() operator are virtual instances. Each Point3d class object contains an additional vptr member object (the instance inherited from Point2d). There is also a Point3d virtual table. The invocation of each member function made virtual is also more complex (this is covered in Chapter 4).

            One current topic of debate within the C++ compiler community concerns where best to locate the vptr within the class object. In the original cfront implementation, it was placed at the end of the class object in order to support the following inheritance pattern, shown in Figure 3.2(a):

            Figure 3.2(a). Vptr Placement and End of Class


            struct no_virts {
            ?? int d1, d2;
            };
            class has_virts: public no_virts {
            public:
            ?? virtual void foo();
            ?? // ...
            private:
            ?? int d3;
            };

            no_virts *p = new has_virts;
            Placing the vptr at the end of the class object preserves the object layout of the base class C struct, thus permitting its use within C code. This inheritance idiom is believed by many to have been more common when C++ was first introduced than currently.

            Subsequent to Release 2.0, with its addition of support for multiple inheritance and abstract base classes, and the general rise in popularity of the OO paradigm, some implementations began placing the vptr at the start of the class object. (For example, Martin O'Riordan, who led Microsoft's original C++ compiler effort, persuasively argues for this implementation model.) See Figure 3.2(b) for an illustration.

            Figure 3.2(b). Vptr Placement at Front of Class


            Placing the vptr at the start of the class is more efficient in supporting some virtual function invocations through pointers to class members under multiple inheritance (see Section 4.4). Otherwise, not only must the offset to the start of the class be made available at runtime, but also the offset to the location of the vptr of that class must be made available. The trade-off, however, is a loss in C language interoperability. How significant a loss? What percentage of programs derive a polymorphic class from a C-lan-guage struct? There are currently no empirical numbers to support either position.

            Figure 3.3 shows the Point2d and Point3d inheritance layout with the addition of virtual functions. (Note: The figure shows the vptr placement at the end of the base class.)

            Figure 3.3. Data Layout: Single Inheritance with Virtual Inheritance


            Multiple Inheritance
            Single inheritance provides a form of "natural" polymorphism regarding the conversion between base and derived types within the inheritance hierarchy. Look at Figures 3.1(b), 3.2(a), or 3.3, where you can see that the base and derived class objects both begin at the same address. They differ in that the derived object extends the length of its nonstatic data members. The assignment, such as

            Point3d p3d;
            Point2d *p = &p3d;
            of the derived class object to a pointer or reference to the base class (regardless of the depth of the inheritance hierarchy) requires no compiler intervention or modification of the address. Instead, it happens "naturally," and in that sense, it provides optimal runtime efficiency.

            From Figure 3.2(b), note that placing the vptr at the beginning of the class object breaks the natural polymorphism of single inheritance in the special case of a base class without virtual functions and a derived class with them. The conversion of the derived object to the base in this case requires the intervention of the compiler in order to adjust the address being assigned by the size of the vptr. Under both multiple and virtual inheritances, the need for compiler intervention is considerably more pronounced.

            Multiple inheritance is neither as well behaved nor as easily modeled as single inheritance. The complexity of multiple inheritance lies in the "unnatural" relationship of the derived class with its second and subsequent base class subobjects. Consider, for example, the following multiply derived class, Vertex3d:

            class Point2d {
            public:
            ?? // ...
            protected:
            ?? float _x, _y;
            };

            class Vertex {
            public:
            ?? // ...
            protected:
            ?? Vertex *next;
            };

            class Vertex2d :
            ?? public Point2d, public Vertex {
            public:
            ?? //...
            protected:
            ?? float mumble;
            };
            The problem of multiple inheritance primarily affects conversions between the derived and second or subsequent base class objects, either directly

            extern void mumble( const Vertex& );
            Vertex3d v;
            ...
            // conversion of a Vertex3d to Vertex is ``unnatural''
            mumble( v );
            or through support for the virtual function mechanism. The problems with supporting virtual function invocation are discussed in Section 4.2.

            The assignment of the address of a multiply derived object to a pointer of its leftmost (that is, first) base class is the same as that for single inheritance, since both point to the same beginning address. The cost is simply the assignment of that address (Figure 3.4 shows the multiple inheritance layout). The assignment of the address of a second or subsequent base class, however, requires that that address be modified by the addition (or subtraction in the case of a downcast) of the size of the intervening base class subobject(s). For example, with

            Figure 3.4. Data Layout: Multiple Inheritance


            Vertex3d v3d;
            Vertex? *pv;
            Point2d *pp;
            Point3d *p3d;
            the assignment

            pv = &v3d;
            requires a conversion of the form

            // Pseudo C++ Code
            pv = (Vertex*)(((char*)&v3d) + sizeof( Point3d ));
            whereas the assignments

            pp? = &v3d;
            p3d = &v3d;
            both simply require a copying of the address. With

            Vertex3d *p3d;
            Vertex?? *pv;
            the assignment

            pv = p3d;
            cannot simply be converted into

            // Pseudo C++ Code
            pv = (Vertex*)((char*)p3d) + sizeof( Point3d );
            since, if p3d were set to 0, pv would end up with the value sizeof(Point3d). So, for pointers, the internal conversion requires a conditional test:

            // Pseudo C++ Code
            pv = p3d
            ?? ? (Vertex*)((char*)p3d) + sizeof( Point3d )
            ?? : 0;
            Conversion of a reference need not defend itself against a possible 0 value, since the reference cannot refer to no object.

            The Standard does not require a specific ordering of the Point3d and Vertex base classes of Vertex3d. The original cfront implementation always placed them in the order of declaration. A Vertex3d object under cfront, therefore, consisted of the Point3d subobject (which itself consisted of a Point2d subobject), followed by the Vertex subobject and finally by the Vertex3d part. In practice, this is still how all implementations lay out the multiple base classes (with the exception of virtual inheritance).

            An optimization under some compilers, however, such as the MetaWare compiler, switch the order of multiple base classes if the second (or subsequent) base class declares a virtual function and the first does not. This shuffling of the base class order saves the generation of an additional vptr within the derived class object. There is no universal agreement among implementations about the importance of this optimization, and use of this optimization is not (at least currently) widespread.

            What about access of a data member of a second or subsequent base class? Is there an additional cost? No. The member's location is fixed at compile time. Hence its access is a simple offset the same as under single inheritance regardless of whether it is a pointer, reference, or object through which the member is being accessed.

            Virtual Inheritance
            A semantic side effect of multiple inheritance is the need to support a form of shared subobject inheritance. The classic example of this is the original iostream library implementation:

            //pre-standard iostream implementation
            class ios { ... };
            class istream : public ios { ... };
            class ostream : public ios { ... };
            class iostream :
            ?? public istream, public ostream { ... };
            Both the istream and ostream classes contain an ios subobject. In the layout of iostream, however, we need only a single ios subobject. The language level solution is the introduction of virtual inheritance:

            class ios { ... };
            class istream : public virtual ios { ... };
            class ostream : public virtual ios { ... };
            class iostream :
            ?? public istream, public ostream { ... };
            As complicated as the semantics of virtual inheritance may seem, its support within the compiler has proven even more complicated. In our iostream example, the implementational challenge is to find a reasonably efficient method of collapsing the two instances of an ios subobject maintained by the istream and ostream classes into a single instance maintained by the iostream class, while still preserving the polymorphic assignment between pointers (and references) of base and derived class objects.

            The general implementation solution is as follows. A class containing one or more virtual base class subobjects, such as istream, is divided into two regions: an invariant region and a shared region. Data within the invariant region remains at a fixed offset from the start of the object regardless of subsequent derivations. So members within the invariant region can be accessed directly. The shared region represents the virtual base class subobjects. The location of data within the shared region fluctuates with each derivation. So members within the shared region need to be accessed indirectly. What has varied among implementations is the method of indirect access. The following example illustrates the three predominant strategies. Here is the data portion of a virtual Vertex3d inheritance hierarchy: [2]

            [2] This hierarchy is suggested by [POKOR94], an excellent 3D Graphics textbook using C++.

            class Point2d {
            public:
            ?? ...
            protected:
            ?? float _x, _y;
            };

            class Vertex : public virtual Point2d {
            public:
            ?? ...
            protected:
            ?? Vertex *next;
            };

            class Point3d : public virtual Point2d {
            public:
            ?? ...
            protected:
            ?? float _z;
            };
            class Vertex3d :
            ?? public Point3d, public Vertex {
            public:
            ?? ...
            protected:
            ?? float mumble;
            };
            The general layout strategy is to first lay down the invariant region of the derived class and then build up the shared region.

            However, one problem remains: How is the implementation to gain access to the shared region of the class? In the original cfront implementation, a pointer to each virtual base class is inserted within each derived class object. Access of the inherited virtual base class members is achieved indirectly through the associated pointer. For example, if we have the following Point3d operator:

            void
            Point3d::
            operator+=( const Point3d &rhs )
            {
            ?? _x += rhs._x;
            ?? _y += rhs._y;
            ?? _z += rhs._z;
            };
            under the cfront strategy, this is transformed internally into

            // Pseudo C++ Code
            __vbcPoint2d->_x += rhs.__vbcPoint2d->_x;
            __vbcPoint2d->_y += rhs.__vbcPoint2d->_y;
            _z += rhs._z;
            A conversion between the derived and base class instances, such as

            Vertex *pv = pv3d;
            under the cfront implementation model becomes

            // Pseudo C++ code
            Vertex *pv = pv3d ? pv3d->__vbcPoint2d : 0;

            There are two primary weaknesses with this implementation model:

            (1). An object of the class carries an additional pointer for each virtual base class. Ideally, we want a constant overhead for the class object that is independent of the number of virtual base classes within its inheritance hierarchy. Think of how you might solve this.

            (2). As the virtual inheritance chain lengthens, the level of indirection increases to that depth. This means that three levels of virtual derivation requires indirection through three virtual base class pointers. Ideally, we want a constant access time regardless of the depth of the virtual derivation.

            MetaWare and other compilers still using cfront's original implementation model solve the second problem by promoting (by copying) all nested virtual base class pointers into the derived class object. This solves the constant access time problem, although at the expense of duplicating the nested virtual base class pointers. MetaWare provides a compile-time switch to allow the programmer to choose whether to generate the duplicate pointers. Figure 3.5(a) illustrates the pointer-to-base-class implementation model.

            There are two general solutions to the first problem. Microsoft's compiler introduced the virtual base class table. Each class object with one or more virtual base classes has a pointer to the virtual base class table inserted within it. The actual virtual base class pointers, of course, are placed within the table. Although this solution has been around for many years, I am not aware of any other compiler implementation that employs it. (It may be that Microsoft's patenting of their virtual function implementation effectively prohibits its use.)

            The second solution, and the one preferred by Bjarne (at least while I was working on the Foundation project with him), is to place not the address but the offset of the virtual base class within the virtual function table. (Figure 3.5(b) on page 100 shows the base class offset implementation model.) I implemented this in the Foundation research project, interweaving the virtual base class and virtual function entries. In the recent Sun compiler, the virtual function table is indexed by both positive and negative indices. The positive indices, as previously, index into the set of virtual functions; the negative indices retrieve the virtual base class offsets. Under this strategy, the Point3d operator is translated into the following general form (leaving off casts for readability and not showing the more efficient precalculation of the addresses):

            // Pseudo C++ Code
            (this + __vptr__Point3d[-1])->_x += (&rhs + rhs.__vptr__Point3d[-1])->_x;
            (this + __vptr__Point3d[-1])->_y += (&rhs + rhs.__vptr__Point3d[-1])->_y;
            _z += rhs._z;

            Although the actual access of the inherited member is more expensive under this strategy, the cost of that access is localized to a use of the member. A conversion between the derived and base class instances, such as

            Vertex *pv = pv3d;
            under this implementation model becomes

            // Pseudo C++ code
            Vertex *pv = pv3d
            ?? ? pv3d + pv3d->__vptr__Point3d[-1])
            ?? : 0;

            Each of these are implementation models; they are not required by the Standard. Each solves the problem of providing access to a shared subobject whose location is likely to fluctuate with each derivation. Because of the overhead and complexity of virtual base class support, each implementation is somewhat different and likely to continue to evolve over time.

            Access of an inherited virtual base class member through a nonpolymorphic class object, such as

            Point3d origin;
            ...
            origin._x;
            can be optimized by an implementation into a direct member access, much as a virtual function call through an object can be resolved at compile time. The object's type cannot change between one program access and the next, so the problem of the fluctuating virtual base class subobject in this case does not hold.

            In general, the most efficient use of a virtual base class is that of an abstract virtual base class with no associated data members.抽象基才是王道!

            posted @ 2006-11-16 23:48 Jerry Cat 閱讀(1144) | 評論 (0)編輯 收藏

            ********************************************\
            |????歡迎轉載, 但請保留作者姓名和原文鏈接, 祝您進步并共勉!???? |
            \********************************************/


            C++對象模型(11) - 3.3 Access of a Data Member
            作者: Jerry Cat
            時間: 2006/11/15
            鏈接:?
            http://www.shnenglu.com/jerysun0818/archive/2006/11/15/15193.html


            3.3 Access of a Data Member

            1. Static Data Members:

            Static data members are literally lifted out of their class, as we saw in Section 1.1 and treated as if each were declared as a global variable (but with visibility limited to the scope of the class).但其可視范圍只在類內.

            Each member's access permission and class association is maintained without incurring any space or runtime overhead either in the individual class objects or in the static data member itself.

            A single instance of each class static data member is stored within the data segment of the program. Each reference to the static member is internally translated to be a direct reference of that single extern instance. For example,

            // origin.chunkSize == 250;
            Point3d::chunkSize == 250;

            // pt->chunkSize == 250;
            Point3d::chunkSize == 250;

            What if the access of the static data member is through a function call or some other form of expression? For example, if we write

            foobar().chunkSize == 250;

            what happens to the invocation of foobar()? In the pre-Standard language, one didn't know what would happen: It was left unspecified in the ARM whether foobar() had to be evaluated. In cfront, for example, it was simply discarded. Standard C++ explicitly requires that foobar() be evaluated, although no use is made of its result. A probable translation looks as follows:

            // foobar().chunkSize == 250;

            // evaluate expression, discarding result
            (void) foobar();
            Point3d::chunkSize == 250;
            Taking the address of a static data member yields an ordinary pointer of its data type, not a pointer to class member, since the static member is not contained within a class object. For example,

            &Point3d::chunkSize;
            yields an actual memory address of type
            const int*

            2. Nonstatic Data Members:

            Nonstatic data members are stored directly within each class object and cannot be accessed except through an explicit or implicit class object. An implicit class object is present whenever the programmer directly accesses a nonstatic data member within a member function. For example, in the following code:

            Point3d
            Point3d::translate( const Point3d &pt ) {
            ?? x += pt.x;
            ?? y += pt.y;
            ?? z += pt.z;
            }
            the seemingly direct access of x, y, and z is actually carried out through an implicit class object represented by the this pointer. Internally, the function is augmented as follows:

            ? // internal augmentation of member function
            ? Point3d
            ? Point3d::translate( Point3d *const this, const Point3d &pt ) {
            ???? this->x += pt.x;
            ???? this->y += pt.y;
            ???? this->z += pt.z;
            ? }

            Access of a nonstatic data member requires the addition of the beginning address of the class object with the offset location of the data member. For example, given

            origin._y = 0.0;
            the address of

            &origin._y;
            is equivalent to the addition of

            &origin + ( &Point3d::_y - 1 );//注意減1
            (Notice the peculiar "subtract by one" expression applied to the pointer-to-data-member offset value. Offset values yielded by the pointer-to-data-member syntax are always bumped up by one. Doing this permits the compilation system to distinguish between a pointer to data member that is addressing the first member of a class and a pointer to data member that is addressing no member(減一用以讓編譯系統區分兩類數據成員指針: 一種是尋址第一個數據成員; 另一種是不對數據成員尋址). Pointers to data members are discussed in more detail in Section 3.6.)

            編譯時確定, 效率不減.The offset of each nonstatic data member is known at compile time, even if the member belongs to a base class subobject derived through a single or multiple inheritance chain. Access of a nonstatic data member, therefore, is equivalent in performance to that of a C struct member or the member of a nonderived class.

            Virtual inheritance introduces an additional level of indirection in the access of its members through a base class subobject. Thus

            Point3d *pt3d;
            pt3d->_x = 0.0;
            performs equivalently if _x is a member of a struct, class, single inheritance hierarchy, or multiple inheritance hierarchy, but it performs somewhat slower if it is a member of a virtual base class. In the next sections, I examine the effect of inheritance on member layout. Before I turn to that, however, recall the question at the beginning of this section: When, if ever, is the access of the coordinate data members, such as

            origin.x = 0.0;
            pt->x = 0.0; //當面臨虛基類時&pt->x是不確定的, 而&origin.x則是在編譯時確定的
            ever significantly different when accessed through the object origin or the pointer pt? The answer is the access is significantly different when the Point3d class is a derived class containing a virtual base class within its inheritance hierarchy and the member being accessed, such as x, is an inherited member of that virtual base class. In this case, we cannot say with any certainty which class type pt addresses (and therefore we cannot know at compile time the actual offset location of the member), so the resolution of the access must be delayed until runtime through an additional indirection. This is not the case with the object origin. Its type is that of a Point3d class, and the offset location of even inherited virtual base class members are fixed at compile time. An aggressive compiler can therefore resolve the access of x through origin statically.

            posted @ 2006-11-15 23:37 Jerry Cat 閱讀(1204) | 評論 (0)編輯 收藏

            ********************************************\
            |????歡迎轉載, 但請保留作者姓名和原文鏈接, 祝您進步并共勉!???? |
            \********************************************/


            C++對象模型(10) - 3.2 Data Member Layout

            作者: Jerry Cat
            時間: 2006/11/15
            鏈接:?
            http://www.shnenglu.com/jerysun0818/archive/2006/11/15/15192.html


            3.2 Data Member Layout

            class Point3d {
            public:
            ?? // ...
            private:
            ?? float x;
            ?? static List<Point3d*> *freeList;
            ?? float y;
            ?? static const int chunkSize = 250;
            ?? float z;
            };
            the nonstatic data members are set down in the order of their declaration(按聲明的順序) within each class object (any intervening static data members, such as freeList and chunkSize, are ignored). In our example, then, each Point3d object consists of three float members in order: x, y, z. The static data members are stored in the program's data segment independent of individual class objects.

            The Standard requires within an access section (the private, public, or protected section of a class declaration) only that the members be set down such that "later members have higher addresses within a class object" (Section 9.2 of the Standard). That is, the members are not required to be set down contiguously.(可以不連續但必須從低到高)

            What might intervene between the declared members? Alignment constraints on the type of a succeeding member may require padding. This is true both of C and C++, and in this case, the member layout of the two languages is in current practice the same.(對齊)

            虛表指針在哪兒? Traditionally, it has been placed after all the explicitly declared members of the class. More recently, it has been placed at the beginning of the class object. The Standard, by phrasing the layout requirement as it does, allows the compiler the freedom to insert these internally generated members anywhere, even between those explicitly declared by the programmer.

            In practice, multiple access sections are concatenated together into one contiguous block in the order of declaration.編譯器幫你同類項合并 No overhead is incurred by the access section specifier or the number of access levels. For example, declaring eight members in one access section or eight separate access sections in practice results in the same-sized objects.

            posted @ 2006-11-15 23:34 Jerry Cat 閱讀(976) | 評論 (0)編輯 收藏
            僅列出標題  下一頁

            <2006年5月>
            30123456
            78910111213
            14151617181920
            21222324252627
            28293031123
            45678910

            常用鏈接

            留言簿(7)

            隨筆檔案

            最新隨筆

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            日本精品一区二区久久久| 久久精品亚洲日本波多野结衣 | 亚洲欧美日韩精品久久亚洲区| 亚洲国产精品成人久久蜜臀| 亚洲日本va中文字幕久久| 青青青国产精品国产精品久久久久| 国产综合精品久久亚洲| 人妻精品久久久久中文字幕一冢本| 亚洲狠狠综合久久| 狠狠色婷婷久久一区二区 | 久久伊人亚洲AV无码网站| 久久精品国产亚洲AV高清热| 欧洲国产伦久久久久久久| 欧美一区二区三区久久综合| 性高朝久久久久久久久久| 久久精品国产亚洲av影院| 久久天天躁狠狠躁夜夜躁2014| 久久免费美女视频| 久久久久亚洲AV无码麻豆| 性做久久久久久久久老女人| a级毛片无码兔费真人久久| 漂亮人妻被黑人久久精品| 97视频久久久| 一本久道久久综合狠狠躁AV| 99久久国产主播综合精品| 久久99精品久久久久久久久久| 99久久综合国产精品免费| 亚洲精品第一综合99久久| 国内精品伊人久久久久影院对白| 久久久久久亚洲精品成人| 99精品久久久久久久婷婷| 精品久久亚洲中文无码| 久久久久免费精品国产| 久久久国产视频| 合区精品久久久中文字幕一区| 欧美久久综合性欧美| 国产亚洲婷婷香蕉久久精品 | 久久综合精品国产一区二区三区| 91亚洲国产成人久久精品网址| 狠狠色丁香久久婷婷综| 国产91色综合久久免费分享|