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

            concentrate on c/c++ related technology

            plan,refactor,daily-build, self-discipline,

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

            常用鏈接

            留言簿(9)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            #

            在編譯工程的時候,遇到了一些惡心的問題:
            A.h
            #ifndef A_H
            #define A_H
            class A
            {
               public:
                struct B
               {};
            };
            #endif
            在B.h里面進行這樣的調用
            B.h
            #ifndef B_H
            #define B_H
            #include "A.h"
            class BB
            {
                private:
                    A::B* ab;
            };
            #endif
            這里出現B.h里面不能找到A的問題了,我一直在想,如果將頭文件包含進去了,那么編譯器應該就要知道了吧,但是事實上都沒有按照那樣的做法去完成.
            后來查找了一下,是因為出現了調用環. A.H調用了B.H,而B.h由調用了C.h,而C.h又調用了A.h就這樣形成了調用環的關系,結果A.h里面的東西就出現了問題,連簡單的類都不能被識別了,這樣的問題出現是由于濫用包含頭文件的原因,在頭文件里面盡量要少包含東西,多前置聲明,只將信息暴露給實現文件,讓實現文件知道多一點,而讓頭文件暴露得信息少一點.
            另外還值得說明的幾點是:1)盡量多使用防止重復包含的做法,比如使用
            #ifndef XXX_H
            #define XXX_H
            #endif
            這樣的話,就不會造成包含次數過多的情況.
            2)保證防止重復包含的條件編譯標志唯一性,因為按照其說明的要求,一旦聲明了某個標志,那么就不會再編譯了那個頭文件,如果 存在相同的文件編譯標志,那么就說明某個文件一定是漏過編譯了.這樣顯然會出錯的,之前遇到一個類似上面的問題,包含進去頭文件了,卻找不到頭文件里面對應的聲明,后來找了一下,才發現這個文件的條件編譯標志跟另外一個文件的條件編譯標志是一樣的,因而跳過了編譯當前文件.
            頭文件一定要把握好了,突然想起來一個建議:在寫好代碼以后,可以用doxygen來察看工程里面的文件組織格式和類結構格式,這樣如果存在不妥的話,一定得要重新設計和規劃了,呵呵,不寫了,回家了.
            posted @ 2008-07-14 22:03 jolley 閱讀(477) | 評論 (0)編輯 收藏

            選擇static library工程.
            lib.h
            #ifndef LIB_H
            #define LIB_H
            void Print();
            #endif

            lib.cpp
            #include "lib.h"
            #include <iostream>
            using std::cout;
            using std::endl;

            void Print()
            {
             cout << "Hello,World" << endl;
            }

            可能會在路徑上面存在問題,特別是在輸出的時候,可以修改其中的生成事件,方便拷貝lib文件到制定目錄.

            在調用的時候,要求進行一些類似的修改:

            #include "lib.h"
            #pragma comment(lib,"lib.lib")

            int _tmain(int argc, _TCHAR* argv[])
            {
             Print();
             return 0;
            }
            不過這里生成或者調用的方式都要相同,比如MT,MD,或者是MTD,等.
            感覺很簡單的樣子,但是在生成和調用的時候還存在這么多潛規則,讓我深惡痛絕.
            今天在調用的時候,發現了一個問題,可能要對基本運行時檢查,以及緩沖區安全檢查,之前的問題都沒有紀錄下來,后來編譯通過了,也不能重現問題了,現在將這些網絡上面的資源添加進來.
            http://topic.csdn.net/u/20070816/19/9d4a23d6-e2f9-4a7b-8cd6-fcbbdb465bce.html
            http://topic.csdn.net/t/20050719/11/4153745.html
            http://forums.msdn.microsoft.com/zh-CN/vcgeneral/thread/644ea195-5a86-4beb-9e19-b38713d67074/
            http://bytes.com/forum/thread616336.html





            posted @ 2008-07-04 11:59 jolley 閱讀(1366) | 評論 (0)編輯 收藏

            該文為轉載.原文來自http://baike.baidu.com/view/161302.htm
            1. 虛函數的定義
            虛函數用來表現基類和派生類的成員函數之間的一種關系.
            虛函數的定義在基類中進行,在需要定義為虛函數的成員函數的聲明前冠以關鍵字 virtual.
            基類中的某個成員函數被聲明為虛函數后,此虛函數就可以在一個或多個派生類中被重新定義.
            在派生類中重新定義時,其函數原型,包括返回類型,函數名,參數個數,參數類型及參數的先后順序,都必須與基類中的原型完全相同.
            虛函數是重載的一種表現形式,是一種動態的重載方式.
            2. 為什么使用虛函數
            #include
            class CBase{
            public:
            void who( )
            {cout<<"this is the base class!\n";}
            };
            class CDerive1 : public CBase{
            public:
            void who( )
            {cout<<"this is the derive1 class!\n";}
            };
            class CDerive2 : public CBase{
            public:
            void who( )
            {cout< who( );
            p = &obj2;
            p -> who( );
            p = &obj3;
            p -> who( );
            obj2.who( );
            obj3.who( );
            return 1;
            }
            運行結果:
            this is the base class!
            this is the base class!
            this is the base class!
            this is the derive1 class!
            this is the derive2 class!
            通過對象指針進行的普通成員函數調用,僅僅與指針的類型有關,而與此刻正指向什么對象無關.要想實現當指針指向不同對象時執行不同的操作,就必須將基類相應中的成員函數定義為虛函數.
            3. 虛函數與重載函數的關系
            一般的重載函數,函數的返回類型及所帶的參數必須至少有一樣不完全相同,只需函數名相同即可.
            基類中定義的虛函數在派生類中重新定義時,其函數原型,包括返回類型,函數名,參數個數,參數類型及參數的先后順序,都必須與基類中的原型完全相同.
            重載虛函數時,若與基類中的函數原型出現不同,系統將根據不同情況分別處理:
            (1)僅僅返回類型不同,其余相同,系統會當作出錯處理;
            (2)函數原型不同,僅僅函數名相同,系統會認為是一般的函數重載,將丟失虛特性.
            3.3.4 虛基類
            #include
            class x{
            protected:
            int a;
            public:
            void f ( ) ;
            };
            class x1 : public x {
            public:
            x1( ){cout<};
            class x2 : public x {
            public:
            x2( ){ cout<};
            class y : public x1, public x2{
            public:
            y( ){ cout<};
            main( )
            {
            y obj; //error
            obj . f ( ) ; //error
            return ;
            }
            二義性錯誤
            非虛基類的類層次
            虛基類的類層次
            當在多條繼承路徑上有一個公共的基類,在這些路徑中的某幾條匯合處,這個公共的基類就會產生多個實例(或多個副本),若只想保存這個基類的一個實例,可以將這個公共基類說明為虛基類.
            class x1 : virtual public x
            {
            // … …
            };
            class x2 : virtual public x
            {
            // … …
            };
            虛基類的初始化
            虛基類的初始化與一般多繼承的初始化在語法上是一樣的,但構造函數的調用次序不同.
            派生類構造函數的調用次序有三個原則:
            (1) 虛基類的構造函數在非虛基類之前調用;
            (2) 若同一層次中包含多個虛基類,這些虛基類的構造函數按它們說明的次序調用;
            (3) 若虛基類由非虛基類派生而來,則仍先調用基類構造函數,再調用派生類的構造函數.


            純虛函數
            僅僅用來為要從基類中派生的函數占據一個位置。
            純虛函數在基類中沒有定義,它們被初始化為0。
            任何用純虛函數派生的類,都要自己提供該函數的具體實現。

            定義純虛函數
            virtual void myMethod(void) = 0;
            posted @ 2008-06-30 13:46 jolley 閱讀(225) | 評論 (0)編輯 收藏

            #include <iostream>
            using namespace std;

            class SomeObject
            {
            public:
             void SetValue(int value)
             {
              this->value = value;
             }
             int GetValue()const
             {
              return this->value;
             }
            private:
             int value;
            };
            int main()
            {
             SomeObject* so = new SomeObject;
             if (NULL == so)
             {
              return 1;
             }
             so->SetValue(1000);
             SomeObject *cp = so;
            cout << cp->GetValue() << endl;//1)
             delete so;
             so = NULL;
             cout << cp->GetValue() << endl;//2)
             return 0;
            }
            2)處出錯,因為刪除掉了,雖然本意是刪除某個數值,但是他們是共用一個地址的,在刪除的時候也對原來占用的指針刪除掉了.低級錯誤,不可忽視.

            posted @ 2008-06-11 14:37 jolley 閱讀(78) | 評論 (0)編輯 收藏

            游戲中經常會出現崩潰地址的情況,這讓我很惱火,在網絡上面看到了一些處理方法,主要有這幾種: 1)map file,2) crashFinder,3)BoundsChecker,4)dump文件,主要是這些信息,現在主要說明第四種方法的處理.
            這種方法主要是用一個MiniDumpWriteDump函數將信息寫入到dmp文件里面去,以及系統的宏__try,和__except.在__except里面就進行異常的處理-寫MiniDumpWriteDump函數.
            今天在寫好了一個小demo之后,想將他移植到游戲工程里面去的時候,發現一個很詭異的做法:
            error C2712: Cannot use __try in functions that require object unwinding
            感覺很驚訝,
            后來在網絡上面查找了一些資料,主要涉及到幾個點:命令參數(GX,SEH,EHSc),
            GX-允許異常處理.SEH-結構化異常處理,EHSc-
            http://msdn.microsoft.com/en-us/library/1deeycx5(VS.80).aspx

            詳細的情況請查閱MSDN.
            因為這樣C++和SEH的東西(__try/__except)不能混淆起來,所以將他在另外一個函數里面給出來.
            http://www.codeguru.com/forum/archive/index.php/t-199810.html
            http://www.codeproject.com/KB/cpp/exception.aspx
            http://msdn.microsoft.com/en-us/library/xwtb73ad(VS.80).aspx

            posted @ 2008-06-11 10:56 jolley 閱讀(5572) | 評論 (0)編輯 收藏

            兩個超惡心的錯誤,現在網絡上面針對這些錯誤都有很多種做法:
            http://blog.csai.cn/user1/16781/archives/2006/6412.html
            http://www.qqgb.com/Program/VC/VCJQ/Program_165466.html
            http://topic.csdn.net/u/20071105/11/673EBD2D-CD29-419D-8486-433D6C1A28FA.html
            http://www.ureader.com/msg/145357.aspx
            http://topic.csdn.net/u/20071229/21/490eb437-9693-4666-8a97-f236c81b0036.html
            等等這些,都提出了很多,但是總結起來vc2005里面主要要修改的地方還是有一些的.
            項目屬性頁->常規->配置類型,->MFC的使用,->字符集(Unicode/Multi-byte)
            項目屬性頁->調試->工作目錄.
            項目屬性頁->C/C++->優化.
            項目屬性頁->預處理器->預處理器定義
            項目屬性頁->C/C++->代碼生成->運行時庫
            項目屬性頁->C/C++->預編譯頭文件->創建/使用預編譯頭文件
            項目屬性頁->連接器->常規->啟用增量連接(這個主要是影響map文件信息的聲稱)
            項目屬性頁->連接器->常規->附加庫目錄
            項目屬性頁->連接器->清單文件->生成清單
            項目屬性頁->連接器->系統->子系統
            項目屬性頁->連接器->高級->入口點
            項目屬性頁->連接器->命令行(添加附加的庫).
            當然還有清單工具等等.
            基本上一些工程的配置都在上面進行,以前總是在弄弄,但是沒發覺什么,今天弄煩了,索性將他全部列出來.
            winMain相關錯誤除了前面的解決辦法之外,還要注意一些模板的設置,比如,win32 console和win32 app的主函數就是不一樣的,而mfc的主函數就更加不一樣了,所以不能將一些工程模板的主函數混淆起來.
            之前我出現的一個錯誤就是在win32 console里面用win32 app的主函數.
            而對于msvcr80d.dll的錯誤一般是要處理:項目屬性頁->C/C++->代碼生成->運行時庫將mtd改為md或者其它的,在上面的url里面也有相應的說明,只希望這兩個錯誤以后不能再犯了.呵呵
            PRJ0019: 這個錯誤以前在編譯ut的時候就出現過,ut2004,后來一直都沒有重視它,現在又出現了,就不能不重視了.
            這個錯誤的報告是"生成后事件目錄錯誤".具體的解決辦法是工程的目錄太深,需要將之清除.
            項目屬性頁->生成事件->生成后事件.出現這個錯誤的時候,命令行上面有一個很長串的目錄,需要將這個清除掉后再重新編譯即可.
            posted @ 2008-06-10 11:10 jolley 閱讀(6943) | 評論 (2)編輯 收藏

            void royLog(const char*string, ...)
            {
             FILE* fp = NULL;
             // 打開文件,如果沒有的話,那么就創建該文件
             fp = fopen("log.txt","a+");
             // 進行有效性判斷
             if (NULL == fp)
             {
              printf("opening file fails! ");
              return;
             }

             va_list list;

             va_start(list,string);

             // 申請內存
             int len = strlen(string);
             char* temp = (char*)malloc(sizeof(char)*(len));
             // 判斷分配內存是否有效
             if (NULL == temp)
             {
              printf("allocation fails!");
              return;
             }
             //初始化內存
             memset(temp,0,len);
             // 執行拷貝
             memcpy(temp,string,len);
             int result = vfprintf(fp,temp,list);
             // 釋放內存和將指針置為為空
             free(temp);
             temp = NULL;
             
             va_end(list);

             //關閉文件指針
             fclose(fp);
             fp = NULL;

            }
            感覺公司里面的log不怎么好用,直接用OutputDebugString到輸出窗口怎么能跟我的這個log做法相比較呢?呵呵
            呵呵.感覺很羞恥的是上面的log文件存在嚴重問題.
            特別是vfprintf. int vfprintf( FILE *stream, const char *format, va_list argptr );
            而我在上面的log里面是將log信息傳遞給format的,這樣就存在嚴重的問題,不過感覺離奇的是,我在之前的測試中居然沒有報錯,在我將log添加到游戲工程里面以后就卻報錯了.(代碼還是要進行路徑覆蓋的,不能光看功能或者做一些黑盒測試,只看輸入和輸出,而忽視了正確的功能).
            正確的文件log要更加簡單些:
            FILE* fp = NULL;
             // 打開文件,如果沒有的話,那么就創建該文件
             fp = fopen("file.txt","a+");
             // 進行有效性判斷
             if (NULL == fp)
             {
              printf("opening file fails! ");
              return;
             }
             va_list list;
             va_start(list,format);
             vfprintf(fp,format,list);
             va_end(list);
             //關閉文件指針
             fclose(fp);
             fp = NULL; 
            請大家如果發現bug的話,一定要告訴我,我一定請他吃飯,呵呵.

            posted @ 2008-06-03 20:48 jolley 閱讀(278) | 評論 (0)編輯 收藏

            基本變換

            平移變換
            平移矩陣= (1 0 0 0   )
                      (0 1 0 0   )
                      (0 0 1 0   )
                      (tx ty tz 1)
            點p(px,py,pz,1)與平移矩陣相乘以后,就可以得到新點p' = (px+tx,py+ty,pz+tz,1),在計算機實時圖形學上面的平移矩陣好象是錯誤的呀,要轉置以后才對的。
            在書中記錄的平移是:
            (1 0 0 tx)
            (0 1 0 ty)
            (0 0 1 tz)
            (0 0 0 1 )
            另外平移矩陣的逆矩陣應該是:
            (1 0 0 -tx)
            (0 1 0 -ty)
            (0 0 1 -tz)
            (0 0 0  1 )
            這里采用代數行列式的形式計算出來的,具體來說,就是將平移矩陣轉置,獲得轉置矩陣pt(t),那么p-1(t) = pt(-t).
            在D3D里面一般用函數D3DXMatrixTranslation.

            旋轉變換

            繞x軸旋轉
            (1 0 0 0 )
            (0 cosA -sinA 0)
            (0 sinA cosA 0)
            (0 0 0 1)
            假設存在某個點p(px,py,pz,1),那么經過繞x旋轉以后,就可以得到
            (px,pycosA + pzsinA, pzcosA-pysinA,1)
            在D3D里面一般用函數D3DXMatrixRotationX

            繞y軸旋轉
            (cosA 0 sinA 0)
            (0 1 0 0 )
            (-sinA 0 cosA 0)
            (0 0 0 1)
            假設存在某個點p(px,py,pz,1),那么經過繞y旋轉以后,就可以得到
            (pxcosA-pzsinA,py,pxsinA+pzcosA,1)
            在D3D里面一般用函數D3DXMatrixRotationY

            繞z軸旋轉
            (cosA -sinA 0 0)
            (sinA cosA 0 0)
            (0 0 1 0)
            (0 0 0 1)
            假設存在某個點p(px,py,pz,1),那么經過z旋轉以后,就可以得到
            (pxcosA + pysinA, pycosA - pxsinA, pz, 1) 
            在D3D里面一般用函數D3DXMatrixRotationZ

            當然還有一個可以自定義的旋轉軸做法:
            D3DXMatrixRotationAxis

            對繞任意旋轉角度A的3x3旋轉矩陣RL來說,其對角線元素之和是一個與坐標軸無關的常量,稱為跡
            tr(R) = 1 + 2cosA

            縮放變換
            縮放矩陣
            s = (sx 0 0 0)
                (0  sy 0 0)
                (0  0 sz 0)
                (0  0  0  1)
            假設存在某個點p(px,py,pz,1),那么經過縮放變換以后,就可以得到
            (pxsx,pyxy,pzsz,1)
            在齊次坐標系里面,創建一個一致性縮放矩陣的另外一種方式是通過改變(3,3)處的元素實現
            比如
            s = (1 0 0 0)
                (0 1 0 0)
                (0 0 1 1)
                (0 0 0 factor)
            在這里這個factor將起主要作用.
            如果對縮放矩陣的一個或者三個分量置為空,就會產生一個反射矩陣,或者成為鏡像矩陣。如果其中兩個縮放因子是-1,那么將會旋轉180度

            錯切變換
            錯切變換是使圖形產生一個扭變。分為x和y方向的錯切變換。

                圖形沿x方向的錯切矩陣表示為 

                 [1 0 0]
            [x' y' 1] = [x y 1][b 1 0] = [ x+by y 1]
                               [0 0 1]

                此時,圖形的y坐標不變,x坐標隨坐標(x y)和系數b作線性變化。b>0,圖形沿+x方向做錯切;b<0,圖形沿-x方向做錯切;b≠0。

                圖形沿y方向的錯切矩陣表示為     

                 [1 d 0]
            [x' y' 1] = [x y 1][1 1 0] = [ x dx+y 1]
                               [0 0 1] 

               此時,圖形的x坐標不變,y坐標隨坐標(x y)和系數d作線性變化。d>0,圖形沿+y方向做錯切;d<0,圖形沿-y方向做錯切;d≠0。 
            一般在游戲中被用來扭曲整個場景,從而產生某種虛幻效果,或者抖動來產生模糊反射效果。
            Hxy:第一下標是由錯切矩陣改變的坐標,第二個下標表示要進行錯切操作的坐標。

                      (1 0 s 0)
            Hxz(s) =  (0 1 0 0)
                      (0 0 1 0)
                      (0 0 0 1)
            假設存在P點(px,py,pz,1)與矩陣相乘可以得到(px + s pz, py, pz,1)
            任何錯切矩陣的行列式值總為1。

            變換級聯
            矩陣乘法存在不可交換性,將多個矩陣級聯為單個矩陣可以獲得比較好的效率。

            剛體變換
            僅由平移和旋轉兩種級聯所做成的變換稱為剛體變換,這種變換具有長度和角度不變的特性。
            可以將剛體矩陣X寫成一個平移矩陣T(t)和一個旋轉矩陣R的級聯。
                        ( r00 r01 r02 tx)
                        ( r10 r11 r12 ty)
            X = T(t)R = ( r20 r21 r22 tz)
                        ( 0   0   0   1)
            X的逆矩陣 = (T(t)R)的逆矩陣 = R的逆矩陣*T(t)的逆矩陣 = R的逆矩陣 * T(-t)
            首先對矩陣R左上角的3x3矩陣進行轉置,然后改變平移矩陣T的平移值符號,最后將這兩個矩陣進行相乘即可以得到相應的逆矩陣。
            另外一種求X的逆矩陣方法:
                                  (r,0轉置)
            R = ( r,0 r,1 r,2) = (r,1轉置)
                                  (r,2轉置)

            X = ( R     t)
                ( 0轉置 1)

            法線變換
            法線必須通過用變換集合圖形的逆矩陣的轉置矩陣進行變換。
            在實際應用中,如果變換矩陣是正交的,就沒有必要計算該矩陣的逆矩陣
            在這種情況下,正交矩陣的逆矩陣就是轉置矩陣,可以用這個矩陣本身對法線進行變換。

            如果使用一個或者多個一致性縮放矩陣進行變換,就不需要計算相應的逆矩陣,因為這個縮放只影響變換后的法線長度,而不影響其方向,在這種情況下,一般都要在矩陣變換對法線進行變化之后,需要對法線進行歸一化。

            逆矩陣計算
            1) 如果矩陣是單個變換或者一些給定參數的變換,那么很容易通過調換參數和矩陣順序計算出相應的逆矩陣,比如M = T(t)R(s), 那么M的逆矩陣為R(-s)T(-t)
            2) 如果矩陣是正交,那么其逆矩陣就是其轉置,任何的旋轉變換都是正交的。
            3) 在不知道任何特殊信息的情形下,可以采用伴隨矩陣,克萊姆法則,LU分解,或者高斯消去法來計算逆矩陣


            歐拉變換
            主要用來構造一個自定位(如相機)或者任何實體處于特定方向的矩陣。
            首先,需要建立一種默認的觀察方向,通常情況下,頭部沿著y軸方向,面部朝向為z軸負方向。
            當使用歐拉變換的時候,會出現所謂萬向鎖(Gimbal Lock),這種現象通常出現在旋轉變換的時候出現,其中會缺少一個自由度。

            改變head就是使觀察者搖頭說不,改變pitch就是點頭,改變roll就是頭向一邊歪。

            矩陣分解
            主要應用場合:
            1)提取縮放因子
            2)確定特定系統所需要的變換
            3)確定模型是否只經歷過剛體變換
            4)在動畫的關鍵幀之間插值
            5)從旋轉矩陣中消除其中的錯切變換

            四元組
            四元數是一個用來構造強制變換的有力工具,在某些情況下,要比歐拉角和歐拉矩陣更具有優勢,特別是在遇到旋轉和定向的情況時更是如此。
            數學背景
            q = (qv,qw) = iqx + jqy + kqz + qw = qv + qw
            qv = iqx + jqy + kqz = (qx,qy,qz)
            i*i = j*j = k*k = -1, jk = -kj = i, ki = -ik = j, ij = -ji = k
            乘法:
            QR = (iqx + jqy + kqz + qw)(irx + jry + krz + rw)
               = i*(qyrz - qzry + rwqz + qwrx)
                 + j*(qzrx - qxrz + rwqy + qwry)
                 + k*(qxry - qyrx + rwqz + qwrz)
                 + qwrw - qxrx - qyry - qzrz
               = qv * rv + rwqw - qxrx - qyry - qzrz

            加法:
            Q + R = (qv, qw) +(rv,rw) = (qv + rv, qw + rw)

            共厄:
            Q = (qv,qw)共厄 = (-qv,qw)

            范數:
            n(Q) = qx * qx + qy * qy + qz * qz + qw * qw

            同一性:
            i = (0,1)

            逆:
            q的逆 = 1/n(Q) *Q共厄

            共軛法則:

            Q共軛 = Q共軛

            ( Q + R)共軛 = Q共軛 + R共軛

            (QR)共軛 = R共軛Q共軛

            范數法則:
            n(Q共厄) = n(Q)

            n(QR) = n(Q)n(R)

            乘法定律:
              線性關系: P(sQ + tR) = sPQ + tPR
                         (sP +tQ)R = sPR + tQR
              組合關系:P(QR) = (PQ)R

            單位四元組Q=(qv,qw)的模n(Q)為1,基于此,Q可以表示為:
            Q = (sinAuq,cosA) = sinAuq + cosA

            對于一些三維向量uq來說,有||uq|| = 1,因為:
            n(Q) = n(sinAuq,cosA) = sinA*sinA*(uq.uq) + cosA*cosA = 1
            當且僅當uq*uq = ||uq|| *||uq||.

            球面線性插值
            球面線性插值是在給定兩個單位四元組Q和R,以及一個參數t(0<=t<=1)的情況下計算插值四元組,
            該運算的代數形式可以用下面的復合四元組S表示:
            S(Q,R,t) = (RQ的逆)tQ

            slerp表示球面線性插值
            slerp(Q,R,t) = sinA(1-t)/sinA*Q + sinAt/sinA *R
            cosA = qxrx + qyry + qzrz + qwrw
            對于0<=t<=1來說,slerp函數計算是唯一的插值四元組
            這些四元組構成了四維單位球面上從Q(t=0)到R(t=1)之間的最短弧。
            這條弧位于一個圓上,這個圓由Q,R,以及原點組成的平面與四維單位球面相交形成的

            Ai = Bi = Qi exp(-(log(QiQi-1)+log(QiQi+1))/4)

            使用Qi,Ai和Bi對使用平滑三次樣條的四元組進行球面插值
            squad(Qi,Qi+1,Ai,Ai+1,t) = slerp(slerp(Qi,Qi+1,t),slerp(Ai,Ai+1,t),2t(1-t))

            squad函數是由slerp函數重復使用球面插值而構成的,該插值通過初始化方位Qi而不是Ai,Ai主要用來表示初始方位的切線方向。

            從一個向量到另外一個向量的旋轉
            四元組計算規則:
            首先對s和t歸一化,然后,計算出單位旋轉軸u,具體通過式u = (s*t)/||s x t||來計算。
            其次e = s.t = cos2A,||s * t|| = sin2A
            其中2A表示s與t之間的夾角.
            這樣,從s到t旋轉的四元組可以表示為Q =(sinAu,cosA).

            posted @ 2008-05-25 15:58 jolley 閱讀(1872) | 評論 (1)編輯 收藏

             

            編譯器基本技巧:

             

            F10: 單步調試,按步執行程序,一般用來察看程序執行流程,如果程序程序從中斷掉了,就可以用單步調試。

            F9: 設置斷點,程序在執行到設置斷點的地方就會停下。

            F5: 執行調試程序,Debug|Go。

            F11: 進入block內部進行調試。

            Ctrl+F5: 在使用的時候,執行調試程序,Debug|Execute。

            Ctrl+F7: 編譯單個文件,而不編譯所有文件,這樣可以避免編譯一些不必要的文件而增加編譯時間。

            Clean: 清除工程

            Rebuild all : 刪除之前產生的中間生成文件以后,重新編譯整個工程

            Watch 窗口:將變量添加到watch窗口,并且可以查看變量值,但是不能察看函數的返回值,一般要看函數的返回值,應該將其保存在變量,然后進行察看。

            Call Stack: 調用函數棧,在call stack里面可以看到各級函數的調用關系,并且這樣方便定位bug報錯位置。

            Shift+F5: 取消調試過程。

            Ctrl+ B: 設置數據斷點和位置斷點。

             

             

            位置斷點:


            這是我們經常采用的辦法,在我們要調試的代碼行上設置一個斷點,然后按F5或者F10進行調試,這種方法在非循環的代碼block里面是可行的,但是如果在一個循環里面,那么就有點麻煩了,這樣的話,你要多次地按F5,假如循環次數超過1000的話,想想看,不說你按F10來單步調試,就是按F5也要耗掉你不少體力.這里介紹一個方法:
            即斷點的條件判斷法.
            設置你要調試的代碼行.
            ctrl+b打開斷點設置對話框,這樣的話,我們就看到了一個location,我們可以在下面的breakpoints列表里選擇我們所要調試的代碼行,這樣的話,在上面我們就可以在break at這個框里面看到我們剛才選擇的代碼行,接著在下面的condition按鈕中選中,"Enter the expression to be evaluated"這一項中輸入一個表達式,這個表示只有在該表達式成立的情況下,這個斷點才能被啟動.當然如果是輸入一個變量名的話,那么在這個變量被修改的時候,斷點才能被啟動.另外還有兩項:"Enter the number of elements to watch in an array or structure" 以及"Enter the number of times to skip before stopping".在這里都可以設置一些選項,這些對于循環的block非常有用.第一項表明要輸入某個數組或者結構中要觀測的項目數,第二項表示在終止前要跳過的次數,顯然在循環次數很多的情況下是很有用的,在這里我們可以設置他為循環的次數,這樣的話,等所有信息都設置好以后,F5以后,程序就會被中斷,那么我們在"breakpoints"這里就可以看到一個信息"remaining xx"這表示說在循環進行100-xx+1項后就終止了,我們當然還可以采用"Enter the expression to be evaluated"這種方法,直接從值這個方面來獲取信息,而不是從次數上獲取信息.
            for (int i = 100;i >0;i--) {
                            printf("%d",100/(i-5));
                    }
            這就是個例子來的.


            數據斷點:


            就是我們開始說的"Enter the expression to be evaluated"這種方法,即在滿足某個條件值時,斷點才會啟動.
            其實位置斷點和數據斷點是互補的,相對而言,數據斷點更加適合于判斷數據時候被修改這種的情況,而位置斷點一般用在循環中,來說明程序執行的情況.在那個位置出錯了,我們找到這個位置再去定位錯誤.
            另外還有一種方法:callstack里面為某個函數設置斷點,這樣的話,就可以將函數調用從深度層次中返回.
            還有可以在調試狀態下,在某個代碼行A處單擊右鍵菜單中選擇set next statement這個項,這樣的話,下次執行的代碼行就是A,而不是其它代碼行,其實從字面意思就可以了解了.
            另外watch窗口也可以用來監視變量的值變化,很管用的,特別對于一些數組,結構之類的,你可以查看數組/結構里面所有元素的值情況,不過我們用完了就得刪除我們選擇的變量,因為下次使用的時候它們還會出現的.

             

            打印log:

            可以通過輸出函數打印相關信息,將log信息輸出到屏幕,這些方法主要有:

            Prinf, cout, MessageBox, 以及OutputDebugString等。當然也可以將信息記錄在文本里面供執行以后察看,用fopen之類的,將信息寫入文件。

             

            查找崩潰地址:

            可以采用dbghelp,具體信息可以到微軟的官網上查找

             

            也可以使用map 文件,這里有一篇介紹不錯的文章。

            http://www.codeproject.com/KB/debug/mapfile.aspx

             

            使用斷言:

            斷言可以幫助你更加檢查數據的有效性,不過要注意的一點是,不要在斷言里面使用函數,因為在非debug模式下面,比如release模式下面assert是無效的,要跳過去執行的。

             

            使用異常:

            Try/catch/throw, 以及__try/__catch/__throw

             

            其他調試信息和相關編譯器設置信息:

            http://ei.cs.vt.edu/~cs1205/c_debug/intro.html

            http://www.hermetic.ch/cfunlib/debug.htm

            http://www.gamedev.net/reference/articles/article1344.asp

             

            編程習慣建議:

            多使用斷言,assert對數據前驅和后繼進行檢查,使用防御編程,在函數入口,和函數出口處都進行數據檢查。

            在申請內存或者分配空間的時候,要檢查數據是否分配成功,如果沒有分配成功的話,要做相關的處理。

            在聲明變量的時候,最好也要進行初始化,防止沒有經過初始化獲得一些未知的數值。

            未初始化的內存區一般被0xcccccccc填充,已經釋放掉內存的區域一般是0xcdcdcdcd.

            如果你試著訪問一個數據值為0xcdcdcdcd的指針,那么意味著你已經在某個地方將該之珍釋放掉了,如果是0xcccccccc,那么意味著該指針一直沒有被分配空間。
             

            (1)指針消亡了,并不表示它所指的內存會被自動釋放。(摘自林博士的高質量C++編程)

             

            (2)內存被釋放了,并不表示指針會消亡或者成了NULL指針。

            這里可以這樣來理解:
            1)對于棧上的內存區域,那么在該指針離開了該作用域以后,就無效了,但是并不意味著該指針所占據的內存被自動釋放了,相反,出現內存泄漏了。
            2)在釋放內存以后,該指針仍舊未非空的,所以還要將之置空,避免出現野指針。

            posted @ 2008-05-14 15:38 jolley 閱讀(838) | 評論 (0)編輯 收藏

            1) 使用windows 頭文件
            API 頭文件允許32位和64位應用程序,包含了ANSI版本和UNICODE版本的聲明。
            如果安裝更新SDK的話,那么就可能有頭文件的多個版本在機器上面。
            一些函數的使用可能會通過使用條件編譯代碼依賴于某個特定版本的操作系統,為了編譯成功,你得定義比較合適的macro.頭文件使用宏來指示哪個版本系統支持編程元素。
            http://msdn2.microsoft.com/en-us/library/aa383745(VS.85).aspx
            2) 初始化類中的成員模板
            i) 使用非模板的方式
            template <typename Argtype>
            class Option
            {
            public:
            Option( void (*func_name)(Argtype), Argtype Arg1 )
              : MenuFunction<Argtype>( (*func_name)(Argtype), Argtype Arg1 )
             {
             } 
            private:
             MenuFunction<Argtype> function;
            };
            template <typename Argtype>
            Option<Argtype> makeOption(void (*func_name)(Argtype), Argtype Arg1 )
            {
            return Option<Argtype>(func_name, Arg1);
            }

            ii) 使用多態
            class Option
            {
            private:
            class FunctionBase
            {
            public:
            virtual ~FunctionBase() {}
            virtual void call() = 0;
            };

            template <typename Argtype>
            class Function : public FunctionBase
            {
            public:
            Function(void (*func_name)(Argtype), Argtype arg) :
            m_func(func_name, arg)
            {
            }
            void call()
            {
            m_func(); // or whatever
            }
            private:
            MenuFunction<Argtype> m_func;
            };

            public:
            template<typename Argtype> Option( void (*func_name)(Argtype), Argtype Arg1 )
            {
             // of course, this means you need a destructor, copy constructor, and assignment operator
            // function->call() would invoke the function
            function = new Function<Argtype>(func_name, Arg1);

            }  
            FunctionBase * function;
            };
            3 大小寫字符串比較大?。紤]區域性語言的問題)
            #include <iostream>
            #include<algorithm>
            #include<functional>
            #include<boost/bind.hpp>
            #include<string>
            #include<locale>

            struct CaseSensitiveString
            {
              public:
                   bool operator()(const std::string & lhs,const std::string & rhs)
                   {
                          std::string lhs_lower;
                          std::string rhs_lower;
                          std::transform(lhs.begin(),lhs.end(),std::back_inserter(lhs_lower),bind(std::tolower<char>,_1,_loc));
                          std::transform(rhs.begin(),rhs.end(),std::back_inserter(rhs_lower),bind(std::tolower<char>,_1,_loc));
                          return lhs_lower < rhs_lower;
                   }
                  CaseSensitiveString(const std::locale & loc):_loc(loc){}
                 private:
                   std::locale _loc;
            };
            詳細內容見:
            http://learningcppisfun.blogspot.com/2008/04/case-insensitive-string-comparison.html

            4 找不到msctf.h問題
            在用DX自帶的dxut做界面程序的時候,整個程序編制下來就出現了這個錯誤
            fatal error C1083: Cannot open include file: 'msctf.h': No such file or directory
            很詭異的,在dxsdk里面也找不到,想了很久,才發現自己沒有安裝platform sdk.因為win32程序之類的,最好都要安裝這些sdk之類的。具體的信息可以在這里得到
            http://www.gamedev.net/community/forums/topic.asp?topic_id=481358
            5 重載 , 覆蓋,隱藏
            重載與覆蓋有以下的區別:
            重載:同一類,相同函數名,不同函數參數,不一定要有virtual 關鍵字
            覆蓋:子類和父類,相同函數名,  相同函數參數,一定要有virtual 關鍵字
            隱藏:1)如果派生類的函數名與基類的函數名相同,但是參數不同,不論有無virtual關鍵字,基類的函數將被隱藏(與重載區別開來)
                        2)如果派生類的函數名與基類的函數名相同,并且參數相同,但是基類沒有virtual關鍵字,基類的函數將被隱藏(與覆蓋區別開來)
            6 快速加載文件
            在游戲里面,一般對從硬盤或者DVD加載資源要求比較高的,一般采用這樣的方法:

            for(int i = 0; < NumBlocks; i++)
            {
               // VirtualAlloc() creates storage that is page aligned
               // and so is disk sector aligned
               blocks[i] = static_cast<char *>
                  (VirtualAlloc(0, BlockSize, MEM_COMMIT, PAGE_READWRITE));

               ZeroMemory(&overlapped[i], sizeof(OVERLAPPED));
               overlapped[i].hEvent = CreateEvent(0, false, false, 0);
            }

            HANDLE hFile = CreateFile(FileName, GENERIC_READ, 0, 0, OPEN_EXISTING,
               FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING |
               FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, 0);

            int iWriterPos = 0;
            int iReaderPos = 0;
            int iIOPos = 0;
            int iPos = 0;

            do
            {
               while(iWriterPos - iReaderPos != NumBlocks && iIOPos < FileSize)
               {
                  overlapped[iWriterPos & NumBlocksMask].Offset = iIOPos;

                  int iLeft = FileSize - iIOPos;
                  int iBytesToRead = iLeft > BlockSize ? BlockSize: iLeft;

                  const int iMaskedWriterPos = iWriterPos & NumBlocksMask;
                  ReadFile(hFile, blocks[iMaskedWriterPos], iBytesToRead, 0,
                     &overlapped[iMaskedWriterPos]);

                  iWriterPos++;
                  iIOPos += iBytesToRead;
               }

               const int iMaskedReaderPos = iReaderPos & NumBlocksMask;

               WaitForSingleObject(overlapped[iMaskedReaderPos].hEvent, INFINITE);

               int iLeft = FileSize - iPos;
               int iBytesToRead = iLeft > BlockSize ? BlockSize: iLeft;

               memcpy(&g_buffer[iPos], blocks[iMaskedReaderPos], iBytesToRead);

               iReaderPos++;
               iPos += iBytesToRead;

            }
            while(iPos < FileSize);

            CloseHandle(hFile);

            for(int i = 0; i < NumBlocks; i++)
            {
               VirtualFree(blocks[i], BlockSize, MEM_COMMIT);
               CloseHandle(overlapped[i].hEvent);
            }

            char* s vs char s[]
            char s1[] = "abcd";// 定義一個未指定長度的char型數組,并使用字符串"abcd"將之初始化
            char *s2  = "abcd";// 定義一個char型指針,并將其指向字符串"abcd",該字串位于靜態存儲區

            s1[0] = 'm';// 無編譯期、運行期錯誤
            s2[0] = 'm';// 無編譯器錯誤,但運行期試圖修改靜態內存,所以發生運行期錯誤
            char s*只是被賦予了一個指針,char s[]是在棧中重新開辟了空間,可以在程序中寫,而不引起程序崩潰。
            所以相比較而言,使用字符串數組要比字符指針要安全的多,要慎用char*s 和char s[].
            7 can not find MSVCR80.dll
            在安裝了vc2005之后,發現錯誤報告說MSVCR80.dll,以為又要重新安裝vc2005了,但是在網絡上面搜索到另外一個例子說,其實可以不用安裝vc2005,直接改變配置就好了,于是就有這個了:
            http://blogs.msdn.com/seshadripv/archive/2005/10/30/486985.aspx
            http://www.codeguru.com/forum/showthread.php?t=439964
            工程架構:
            新建一個空白的 solution.
            然后在新建的solution上面添加vcproject.
            并且也可以在子空白solution上面添加vcproject.

            1>        ]
            1>正在編譯資源...
            1>正在鏈接...
            1>LINK : warning LNK4075: 忽略“/INCREMENTAL”(由于“/OPT:ICF”規范)
            1>fatal error C1900: Il mismatch between 'P1' version '20060201' and 'P2' version '20050411'
            1>LINK : fatal error LNK1257: 代碼生成失敗
            1>生成日志保存在“file://e:\demo-work\LocalVersionTianJi\_out\DragoonApp\Release\BuildLog.htm”
            1>DragoonApp - 1 個錯誤,5382 個警告
            ========== 全部重新生成: 0 已成功, 1 已失敗, 0 已跳過 ==========

            http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1512436&SiteID=1
            http://forum.codecall.net/c-c/6244-fatal-error-c1900-il-mismatch-between-p1-version-20060201-p2-version-2005-a.html
            http://www.codeguru.com/forum/archive/index.php/t-144030.html

            timeGetTime: 頭文件 mmsystem.h,庫文件 winmm.lib
            獲得當前窗口的句柄。
            HWND hwnd=::GetForegroundWindow();
            hwnd就保存了當前系統的最頂層窗口的句柄
            GetSafehWnd 取你程序所在窗口類的句柄
            GetActiveWindow 取當前活動窗口句柄
            AfxGetMainWnd 取主窗口句柄
            GetForegroundWindow 取前臺窗口句柄
            FindWindow
            EnumWindow
            改變窗口屬性:
            SetWindowLong
            SetClassLong.

            strcpy strncpy memcpy.
            strcpy:按照msdn的話說是:No overflow checking is performed when strings are copied or appended,即沒有嚴格的長度檢查,所以即使是溢出也無法被檢查出來,以及The behavior of strcpy is undefined if the source and destination strings overlap.

            strncpy:雖然加入了size這個來限制,但是這個size小于或者等于字符長度的話,那么該信息是不被加上字符串結束符的.并且仍舊存在跟strcpy一樣的問題, The behavior of strncpy is undefined if the source and destination strings overlap

            memcpy:具體的用法跟strncpy類似,也加入了size的成分在里面,但是卻比strncpy好用得多.

            If the source and destination overlap, this function does not ensure that the original source characters in the overlapping region are copied before being overwritten.

            Use memmove to handle overlapping regions.

            顯然它能夠處理重疊的部分,安全可靠.
            并且:

            The first argument, dest, must be large enough to hold count characters of src; otherwise, a buffer overrun can occur.


            上次遇到的問題是我將一串漢字用strcpy來拷貝到緩沖里面,結果發現出現了亂碼.
            strncpy, strcpy還是建議少用,換用memcpy+memmove(如果存在重疊的情況)吧:)
            未完待續.

            posted @ 2008-05-12 10:01 jolley 閱讀(2753) | 評論 (1)編輯 收藏

            僅列出標題
            共4頁: 1 2 3 4 
            久久久无码精品亚洲日韩京东传媒 | 精品国产乱码久久久久久郑州公司| 伊人久久国产免费观看视频| 一级做a爰片久久毛片免费陪| 狠狠色综合网站久久久久久久高清 | 狠狠色丁香久久婷婷综合五月 | 亚洲国产成人久久一区WWW| 久久香综合精品久久伊人| 国产亚洲综合久久系列| 欧美性猛交xxxx免费看久久久| 伊人久久久AV老熟妇色| 99久久www免费人成精品| 精品久久久久久中文字幕大豆网| 国产精品久久亚洲不卡动漫| 色综合合久久天天给综看| 久久精品国产亚洲AV无码娇色| 日韩久久无码免费毛片软件| 国产国产成人精品久久| 精品久久久久久国产| 久久婷婷人人澡人人| 久久久久久久尹人综合网亚洲| 精品一二三区久久aaa片| 久久亚洲2019中文字幕| 丰满少妇人妻久久久久久4| 国产欧美久久久精品| 久久久av波多野一区二区| 久久久久久久久久久精品尤物| 激情久久久久久久久久| 亚洲国产精品久久| 国产成人综合久久综合| 国产精品视频久久| 久久婷婷五月综合色高清| 久久99精品国产麻豆宅宅| 精品久久亚洲中文无码| 亚洲人成电影网站久久| 午夜精品久久久久久影视777| 久久免费大片| 久久精品免费全国观看国产| 中文字幕无码久久人妻| 久久天天躁狠狠躁夜夜2020一| 亚洲国产天堂久久久久久|