• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            積木

            No sub title

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

            常用鏈接

            留言簿(1)

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

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            #

            原文出自:http://www.wuzesheng.com/?p=840

            C++中內(nèi)存的動(dòng)態(tài)分配與管理永遠(yuǎn)是一個(gè)讓C++開發(fā)者頭痛的問題,本文通過對(duì)C++中內(nèi)存的動(dòng)態(tài)分配釋放的基本原理的介紹,讓讀者朋友能對(duì)C++中的內(nèi)存的動(dòng)態(tài)分配與釋放有較為深入的理解,從而更好駕馭C++程序。

            1. 函數(shù)(Function)
            (1) operator new function

            1
            2
            
            void * ::operator new(size_t);	                //Global
            void * class-name::operator new(size_t); 	//Class


            上面是C++中operator new function的原型,一個(gè)是全局類型的,一個(gè)的類成員類型的。全局類型的operator new函數(shù)在下面兩種情況下被調(diào)用:一種是在分配C++內(nèi)建(built-in)類型的動(dòng)態(tài)內(nèi)存時(shí),一種是在分配用戶沒有自己定義operator new成員函數(shù)的用戶自定義類型的動(dòng)態(tài)內(nèi)存時(shí)。 如果用戶在自己定義的類型中,定義了operator new函數(shù),那么用戶在用new申請(qǐng)?jiān)擃愋偷膭?dòng)態(tài)內(nèi)存時(shí), 便會(huì)調(diào)用該類型的成員函數(shù)operator new, 而不是全局的operator new。

            另外,我們注意到,上面的原型中函數(shù)的返回值為void *類型, 第一個(gè)參數(shù)為size_t類型,這個(gè)是C++編譯器要求的,如果要自己重載operator new函數(shù),返回值必須為void* 類型,第一個(gè)參數(shù)必須為size_t類型,否則,編譯器會(huì)返回如下錯(cuò)誤信息:

            1
            
            error: ‘operator new’ takes type ‘size_t’ (‘unsigned int’) as first parameter

            這里需要注意的一點(diǎn)是,我們可以利用operator new function可以重載的特點(diǎn),可以通過參數(shù)傳入一些額外的信息,來調(diào)試程序,檢測(cè)內(nèi)存泄露等。比如,我們可以像下面這樣重載,傳入調(diào)用處的行號(hào),函數(shù)名,這樣就可以跟蹤內(nèi)存的分配使用情況:

            1
            2
            3
            4
            5
            
            void * operator new(size_t unSize, int nLine, const char * pFunc)
            {
                prinft("Line: %d, Func: %s, allocate %u byte(s)\n", nLine, pFunc, unSize);
                return malloc(unSize);
            }

            (2) operator delete function

            1
            2
            
            void operator delete( void * );
            void operator delete( void *, size_t );

            上面是operator delete function的原型。operator delete function也有全局的和類成員的兩種。這里需要注意,一個(gè)類只能有一個(gè)operator delete function做為其成員函數(shù),而且必須為上面兩種中的其中一種,沒有其它的形式。如果一個(gè)類實(shí)現(xiàn)了自己的operator delete function成員函數(shù),那么在釋放該類型的內(nèi)存時(shí),編譯器便會(huì)調(diào)用成員operator delete function, 而不是全局的。

            上面的兩種原型,第一種,在調(diào)用的時(shí)候,編譯器會(huì)把要釋放的內(nèi)存的首地址傳入,第二種,在調(diào)用的時(shí)候,編譯器會(huì)把要釋放的內(nèi)存的首地址和大小都傳入。因此,可以利用這一特性,如果我們?cè)诨愔袑?shí)現(xiàn)第二種形式的operator delete function的成員函數(shù),那么便可以用之來釋放子類類型的內(nèi)存(具體參考最后面的例子)。

            2. 運(yùn)算符(Operator)
            (1) new operator

            1
            2
            
            [::] new [placement] new-type-name [new-initializer]
            [::] new [placement] ( type-name ) [new-initializer]

            注:上面的’[]‘表示在其中的部分是optional(可選的)
            上面是new operator的原型。在C++中,動(dòng)態(tài)內(nèi)存的分配,通常都是調(diào)用new operator來完成的,利用new operator來分配動(dòng)態(tài)內(nèi)存,編譯器要做下面兩項(xiàng)工作:

            • a. 調(diào)用operator new function分配內(nèi)存(allocate the memory)
            • b. 調(diào)用構(gòu)造函數(shù)(call the constructor)來進(jìn)行初始化

            下面來說一說new operator的原型中各部分到底是干什么的:
            placement: 如果你重載了operator new function, placement可以用來傳遞額外的參數(shù)
            type-name: 指定要分配的內(nèi)存的類型,可以是內(nèi)建(built-in)類型,也可以是用戶自定義類型
            new-initializer: 指定對(duì)分配后內(nèi)存的初始化的參數(shù),也就的構(gòu)造函數(shù)的參數(shù) 。這里需要注意一點(diǎn),在分配一個(gè)對(duì)象的數(shù)組類型的內(nèi)存時(shí),不能夠指定初始化參數(shù);換言之,要想分配一個(gè)對(duì)象的數(shù)組類型的內(nèi)存,該對(duì)象必須有缺省構(gòu)造函數(shù)

            (2) delete opeartor

            1
            2
            
            [::] delete cast-expression
            [::] delete [ ] cast-expression

            上面是delete operator的原型,第一種用來釋放普通的對(duì)象(包括內(nèi)建類型)類型的內(nèi)存,第二種用來釋放對(duì)象的數(shù)組類型的內(nèi)存。在C++中,用new operator分配的動(dòng)態(tài)內(nèi)存,必須調(diào)用delete operator來釋放,通常用delete operator釋放內(nèi)存編譯器要做下面兩項(xiàng)工作:

            • a. 調(diào)用對(duì)象析構(gòu)函數(shù)來析構(gòu)對(duì)象
            • b. 調(diào)用operator delete function來釋放內(nèi)存(deallocate the memory)


            3. 關(guān)于new/delete使用過程中一些需要注意的點(diǎn)
            (1)如何區(qū)別operator new/delete function 與 new/delete operator ?
            通過上面的講述,不難看出,我們分配/釋放動(dòng)態(tài)內(nèi)存,調(diào)用的是new/delete operator, 而在調(diào)用new/delete的過程中,編譯器會(huì)自動(dòng)調(diào)用operator new/delete function來完成實(shí)際的內(nèi)存分配/釋放的工作

            (2) 用delete operator去釋放一塊不是由new operator釋放的內(nèi)存,結(jié)果是不可預(yù)料的,因此,切記,operator new與operator delete一定要配對(duì)使用,這是寫好程序的基礎(chǔ)

            (3) new operator調(diào)用失敗會(huì)拋出std::bad_alloc異常,前提是你沒有自己重載對(duì)應(yīng)的operator new function;delete operator失敗,常見的原因是多次delete同一塊內(nèi)存

            (4) 如果一塊內(nèi)存被delete后,再對(duì)它解引用(Dereference),結(jié)果也是不可預(yù)測(cè)的,很可能導(dǎo)致程序崩潰

            (5) delete一個(gè)空(NULL)指針是安全的,沒有任何害處的

            (6) 類成員類型的operator new/delete函數(shù)必須為靜態(tài)(static)函數(shù),因此它們不能為虛函數(shù)(virtual function),也遵守public, protected, private的訪問權(quán)限控制

            4. 關(guān)于上面所講的內(nèi)容的一些例子:
            程序:

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30
            31
            32
            33
            34
            35
            36
            37
            38
            39
            40
            41
            42
            43
            44
            45
            46
            47
            48
            49
            50
            51
            52
            53
            54
            55
            56
            57
            58
            59
            60
            61
            62
            63
            64
            65
            66
            67
            68
            69
            70
            71
            72
            73
            74
            75
            76
            77
            78
            79
            80
            81
            82
            83
            84
            
            #include <stdio .h>
            #include <stdlib .h>
             
            void * operator new(size_t unSize)
            {
                printf("operator new called\n");
                return malloc(unSize);
            }
             
            void * operator new(size_t unSize, int nLine, const char * pFunc)
            {
                printf("operator new called, line: %d, func: %s\n",
                        nLine, pFunc);
                return malloc(unSize);
            }
             
            void operator delete(void * pMem)
            {
                printf("delete1\n");
                free(pMem);
            }
             
            class A
            {
            public:
             
                A(int a = 0) :
                    _a(a)
                {   
                    printf("constructor called\n");
                }   
                {
                    printf("~A()\n");
                }
             
                static void operator delete(void * pMem, size_t unSize)
                {
                    printf("delete2: %u\n", unSize);
                    free(pMem);
                }
             
            private:
             
                int _a;
            };
             
            class B: public A
            {
            public:
             
                ~B()
                {
                    printf("~B()\n");
                }
             
                int _b;
             
                int _bb;
            };
             
            int main()
            {
                A * pA = new A(10);
                printf("#######\n");
             
                A * pB = new (__LINE__, __func__) B();
                printf("#######\n");
             
                A * szA = new A[10];
                printf("#######\n");
             
                delete pA; 
                printf("#######\n");
             
                delete pB; 
                printf("#######\n");
             
                delete [] szA;
                printf("#######\n");
             
                char * pC = NULL;
                delete pC; 
            }
            </stdlib></stdio>

            運(yùn)行結(jié)果:

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30
            31
            32
            33
            34
            35
            36
            37
            38
            39
            
            wuzesheng@wuzesheng-ubuntu:~/Program$ ./a.out 
            operator new called
            constructor called
            #######
            operator new called, line: 68, func: main
            constructor called
            #######
            operator new called
            constructor called
            constructor called
            constructor called
            constructor called
            constructor called
            constructor called
            constructor called
            constructor called
            constructor called
            constructor called
            #######
            ~A()
            delete2: 8
            #######
            ~B()
            ~A()
            delete2: 16
            #######
            ~A()
            ~A()
            ~A()
            ~A()
            ~A()
            ~A()
            ~A()
            ~A()
            ~A()
            ~A()
            delete1
            #######
            delete1

            上面的程序很簡(jiǎn)單,我在這里不做過多的解釋,感興趣的朋友可以自己分析一下。

            通過我上面的講解,相信大多數(shù)朋友應(yīng)該對(duì)C++中內(nèi)存的動(dòng)態(tài)分配與釋放有了較為深入的理解。后續(xù)我還有可能寫一些關(guān)于C++中內(nèi)存管理的文章,只有把本文所講的內(nèi)容與后續(xù)的內(nèi)存管理的一些常見的方法結(jié)合起來,我們才寫出更加健壯的C++程序。歡迎讀者朋友留言一起交流!

            posted @ 2012-07-16 12:37 Jacc.Kim 閱讀(296) | 評(píng)論 (0)編輯 收藏

            原文出自:http://visionsky.blog.51cto.com/733317/151760

            聯(lián)合(union)在C/C++里面見得并不多,但是在一些對(duì)內(nèi)存要求特別嚴(yán)格的地方,聯(lián)合又是頻繁出現(xiàn),那么究竟什么是聯(lián)合、怎么去用、有什么需要注意的地方呢?就這些問題,我試著做一些簡(jiǎn)單的回答,里面肯定還有不當(dāng)?shù)牡胤剑瑲g迎指出!
            1、什么是聯(lián)合?
               “聯(lián)合”是一種特殊的類,也是一種構(gòu)造類型的數(shù)據(jù)結(jié)構(gòu)。在一個(gè)“聯(lián)合”內(nèi)可以定義多種不同的數(shù)據(jù)類型, 一個(gè)被說明為該“聯(lián)合”類型的變量中,允許裝入該“聯(lián)合”所定義的任何一種數(shù)據(jù),這些數(shù)據(jù)共享同一段內(nèi)存,已達(dá)到節(jié)省空間的目的(還有一個(gè)節(jié)省空間的類型:位域)。 這是一個(gè)非常特殊的地方,也是聯(lián)合的特征。另外,同struct一樣,聯(lián)合默認(rèn)訪問權(quán)限也是公有的,并且,也具有成員函數(shù)。
            2、聯(lián)合與結(jié)構(gòu)的區(qū)別?
               “聯(lián)合”與“結(jié)構(gòu)”有一些相似之處。但兩者有本質(zhì)上的不同。在結(jié)構(gòu)中各成員有各自的內(nèi)存空間, 一個(gè)結(jié)構(gòu)變量的總長度是各成員長度之和(空結(jié)構(gòu)除外,同時(shí)不考慮邊界調(diào)整)。而在“聯(lián)合”中,各成員共享一段內(nèi)存空間, 一個(gè)聯(lián)合變量的長度等于各成員中最長的長度。應(yīng)該說明的是, 這里所謂的共享不是指把多個(gè)成員同時(shí)裝入一個(gè)聯(lián)合變量?jī)?nèi), 而是指該聯(lián)合變量可被賦予任一成員值,但每次只能賦一種值, 賦入新值則沖去舊值。
               下面舉一個(gè)例了來加對(duì)深聯(lián)合的理解。
                例4:
            #include <stdio.h>
            void main()
            {
                    union number
                    {                   /*定義一個(gè)聯(lián)合*/
                            int i;
                            struct
                            {             /*在聯(lián)合中定義一個(gè)結(jié)構(gòu)*/
                                    char first;
                                    char second;
                            }half;
                    }num;
                    num.i=0x4241;         /*聯(lián)合成員賦值*/
                    printf("%c%c\n", num.half.first, num.half.second);
                    num.half.first='a';   /*聯(lián)合中結(jié)構(gòu)成員賦值*/
                    num.half.second='b';
                    printf("%x\n", num.i);
                    getchar();
            }
                輸出結(jié)果為:
                 AB
                 6261
                從上例結(jié)果可以看出: 當(dāng)給i賦值后, 其低八位也就是first和second的值; 當(dāng)給first和second賦字符后, 這兩個(gè)字符的ASCII碼也將作為i 的低八位和高八位。
            3、如何定義?
               例如:
                union test
                {
                  test() { }
                  int office;
                  char teacher[5];
                };
                定義了一個(gè)名為test的聯(lián)合類型,它含有兩個(gè)成員,一個(gè)為整型,成員名office;另一個(gè)為字符數(shù)組,數(shù)組名為teacher。聯(lián)合定義之后,即可進(jìn)行聯(lián)合變量說明,被說明為test類型的變量,可以存放整型量office或存放字符數(shù)組teacher。
            4、如何說明?
               聯(lián)合變量的說明有三種形式:先定義再說明、定義同時(shí)說明和直接說明。
               以test類型為例,說明如下:
                1) union test
                   {
                     int office;
                     char teacher[5];
                   };
                   union test a,b;    /*說明a,b為test類型*/
                2) union test
                   {
                     int office;
                     char teacher[5];
                   } a,b;
                3) union
                   {
                     int office;
                     char teacher[5];
                   } a,b;
                   經(jīng)說明后的a,b變量均為test類型。a,b變量的長度應(yīng)等于test的成員中最長的長度,即等于teacher數(shù)組的長度,共5個(gè)字節(jié)。a,b變量如賦予整型值時(shí),只使用了4個(gè)字節(jié),而賦予字符數(shù)組時(shí),可用5個(gè)字節(jié)。
            5、如何使用?
               對(duì)聯(lián)合變量的賦值,使用都只能是對(duì)變量的成員進(jìn)行。聯(lián)合變量的成員表示為:     
            聯(lián)合變量名.成員名 
            例如,a被說明為test類型的變量之后,可使用a.class、a.office 
            不允許只用聯(lián)合變量名作賦值或其它操作,也不允許對(duì)聯(lián)合變量作初始化賦值,賦值只能在程序中進(jìn)行。
            還要再強(qiáng)調(diào)說明的是,一個(gè)聯(lián)合變量,每次只能賦予一個(gè)成員值。換句話說,一個(gè)聯(lián)合變量的值就是聯(lián)合變員的某一個(gè)成員值。
            6、匿名聯(lián)合
               匿名聯(lián)合僅僅通知編譯器它的成員變量共同享一個(gè)地址,而變量本身是直接引用的,不使用通常的點(diǎn)號(hào)運(yùn)算符語法.例如:
                 #i nclude <iostream>
                 void main()
                 {
                     union{
                            int test;
                            char c;
                           };         
                    test=5;
                    c=′a′;
                    std::cout<<i<<" "<<c;
                 }
                正如所見到的,聯(lián)合成分象聲明的普通局部變量那樣被引用,事實(shí)上對(duì)于程序而言,這也正是使用這些變量的方式.另外,盡管被定義在一個(gè)聯(lián)合聲明中,他們與同一個(gè)程序快那的任何其他局部變量具有相同的作用域級(jí)別.這意味這匿名聯(lián)合內(nèi)的成員的名稱不能與同一個(gè)作用域內(nèi)的其他一直標(biāo)志符沖突.
                對(duì)匿名聯(lián)合還存在如下限制:
                因?yàn)槟涿?lián)合不使用點(diǎn)運(yùn)算符,所以包含在匿名聯(lián)合內(nèi)的元素必須是數(shù)據(jù),不允許有成員函數(shù),也不能包含私有或受保護(hù)的成員。還有,全局匿名聯(lián)合必須是靜態(tài)(static)的,否則就必須放在匿名名字空間中。
            7、幾點(diǎn)需要討論的地方:
               1、聯(lián)合里面那些東西不能存放?
                  我們知道,聯(lián)合里面的東西共享內(nèi)存,所以靜態(tài)、引用都不能用,因?yàn)樗麄儾豢赡芄蚕韮?nèi)存。
               2、類可以放入聯(lián)合嗎?
                  我們先看一個(gè)例子:
                  class Test
                  {
                  public:
                Test():data(0) { }
                  private:
                      int data;
                  };
                 typedef union _test
                 {
            Test test;  
                 }UI;  
                 編譯通不過,為什么呢?
                 因?yàn)槁?lián)合里不允許存放帶有構(gòu)造函數(shù)、析夠函數(shù)、復(fù)制拷貝操作符等的類,因?yàn)樗麄児蚕韮?nèi)存,編譯器無法保證這些對(duì)象不被破壞,也無法保證離開時(shí)調(diào)用析夠函數(shù)。
                3、又是匿名惹的禍??
                   我們先看下一段代碼:
            class test
            {
                    public:
                         test(const char* p);
                         test(int in);
                         const operator char*() const {return
            data.ch;}
                         operator long() const {return data.l;}
                    private:
                 enum type {Int, String };
                        union
                 {
            const char* ch;
            int i;
                  }datatype;
                  type stype;
                  test(test&);
                  test& operator=(const test&);
                    };
                   test::test(const char *p):stype
            (String),datatype.ch(p)     { }
                   test::test(int in):stype(Int),datatype.l(i)     {
            }
                 看出什么問題了嗎?呵呵,編譯通不過。為什么呢?難道datatype.ch(p)和datatype.l(i)有問題嗎?
                 哈哈,問題在哪呢?讓我們來看看構(gòu)造test對(duì)象時(shí)發(fā)生了什么,當(dāng)創(chuàng)建test對(duì)象時(shí),自然要調(diào)用其相應(yīng)的構(gòu)造函數(shù),在構(gòu)造函數(shù)中當(dāng)然要調(diào)用其成員的構(gòu)造函數(shù),所以其要去調(diào)用datatype成員的構(gòu)造函數(shù),但是他沒有構(gòu)造函數(shù)可調(diào)用,所以出
            錯(cuò)。
                 注意了,這里可并不是匿名聯(lián)合!因?yàn)樗竺婢o跟了個(gè)data!
                4、如何有效的防止訪問出錯(cuò)?
                   使用聯(lián)合可以節(jié)省內(nèi)存空間,但是也有一定的風(fēng)險(xiǎn):通過一個(gè)不適當(dāng)?shù)臄?shù)據(jù)成員獲取當(dāng)前對(duì)象的值!例如上面的ch、i交錯(cuò)訪問。
                   為了防止這樣的錯(cuò)誤,我們必須定義一個(gè)額外的對(duì)象,來跟蹤當(dāng)前被存儲(chǔ)在聯(lián)合中的值得類型,我們稱這個(gè)額外的對(duì)象為:union的判別式。
                   一個(gè)比較好的經(jīng)驗(yàn)是,在處理作為類成員的union對(duì)象時(shí),為所有union數(shù)據(jù)類型提供一組訪問函數(shù)。
            posted @ 2012-06-27 10:35 Jacc.Kim 閱讀(223) | 評(píng)論 (0)編輯 收藏

            原文出自:http://www.cnblogs.com/JCSU/articles/1051826.html

            程序員們經(jīng)常編寫內(nèi)存管理程序,往往提心吊膽。如果不想觸雷,唯一的解決辦法就是發(fā)現(xiàn)所有潛伏的地雷并且排除它們,躲是躲不了的。本文的內(nèi)容比一般教科書的要深入得多,讀者需細(xì)心閱讀,做到真正地通曉內(nèi)存管理。

                 內(nèi)存分配方式 

               (1)從靜態(tài)存儲(chǔ)區(qū)域分配。內(nèi)存在程序編譯的時(shí)候就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。例如全局變量,static變量。
               (2)在棧上創(chuàng)建。在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。
               (3) 從堆上分配,亦稱動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用malloc或new申請(qǐng)任意多少的內(nèi)存,程序員自己負(fù)責(zé)在何時(shí)用free或delete釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期由我們決定,使用非常靈活,但問題也最多。
             
                 常見的內(nèi)存錯(cuò)誤及其對(duì)策 

                 發(fā)生內(nèi)存錯(cuò)誤是件非常麻煩的事情。編譯器不能自動(dòng)發(fā)現(xiàn)這些錯(cuò)誤,通常是在程序運(yùn)行時(shí)才能捕捉到。而這些錯(cuò)誤大多沒有明顯的癥狀,時(shí)隱時(shí)現(xiàn),增加了改錯(cuò)的難度。有時(shí)用戶怒氣沖沖地把你找來,程序卻沒有發(fā)生任何問題,你一走,錯(cuò)誤又發(fā)作了。 常見的內(nèi)存錯(cuò)誤及其對(duì)策如下: 

                 * 內(nèi)存分配未成功,卻使用了它

            編程新手常犯這種錯(cuò)誤,因?yàn)樗麄儧]有意識(shí)到內(nèi)存分配會(huì)不成功。常用解決辦法是,在使用內(nèi)存之前檢查指針是否為NULL。如果指針p是函數(shù)的參數(shù),那么在函數(shù)的入口處用assert(p!=NULL)進(jìn)行檢查。如果是用malloc或new來申請(qǐng)內(nèi)存,應(yīng)該用if(p==NULL) 或if(p!=NULL)進(jìn)行防錯(cuò)處理。

            * 內(nèi)存分配雖然成功,但是尚未初始化就引用它

            犯這種錯(cuò)誤主要有兩個(gè)起因:一是沒有初始化的觀念;二是誤以為內(nèi)存的缺省初值全為零,導(dǎo)致引用初值錯(cuò)誤(例如數(shù)組)。內(nèi)存的缺省初值究竟是什么并沒有統(tǒng)一的標(biāo)準(zhǔn),盡管有些時(shí)候?yàn)榱阒担覀儗幙尚牌錈o不可信其有。所以無論用何種方式創(chuàng)建數(shù)組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。

            * 內(nèi)存分配成功并且已經(jīng)初始化,但操作越過了內(nèi)存的邊界

            例如在使用數(shù)組時(shí)經(jīng)常發(fā)生下標(biāo)“多1”或者“少1”的操作。特別是在for循環(huán)語句中,循環(huán)次數(shù)很容易搞錯(cuò),導(dǎo)致數(shù)組操作越界。

            * 忘記了釋放內(nèi)存,造成內(nèi)存泄露

            含有這種錯(cuò)誤的函數(shù)每被調(diào)用一次就丟失一塊內(nèi)存。剛開始時(shí)系統(tǒng)的內(nèi)存充足,你看不到錯(cuò)誤。終有一次程序突然死掉,系統(tǒng)出現(xiàn)提示:內(nèi)存耗盡。

            動(dòng)態(tài)內(nèi)存的申請(qǐng)與釋放必須配對(duì),程序中malloc與free的使用次數(shù)一定要相同,否則肯定有錯(cuò)誤(new/delete同理)。

            * 釋放了內(nèi)存卻繼續(xù)使用它

            有三種情況:

            (1)程序中的對(duì)象調(diào)用關(guān)系過于復(fù)雜,實(shí)在難以搞清楚某個(gè)對(duì)象究竟是否已經(jīng)釋放了內(nèi)存,此時(shí)應(yīng)該重新設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu),從根本上解決對(duì)象管理的混亂局面。

            (2)函數(shù)的return語句寫錯(cuò)了,注意不要返回指向“棧內(nèi)存”的“指針”或者“引用”,因?yàn)樵搩?nèi)存在函數(shù)體結(jié)束時(shí)被自動(dòng)銷毀。

            (3)使用free或delete釋放了內(nèi)存后,沒有將指針設(shè)置為NULL。導(dǎo)致產(chǎn)生“野指針”。

            【規(guī)則1】用malloc或new申請(qǐng)內(nèi)存之后,應(yīng)該立即檢查指針值是否為NULL。防止使用指針值為NULL的內(nèi)存。

            【規(guī)則2】不要忘記為數(shù)組和動(dòng)態(tài)內(nèi)存賦初值。防止將未被初始化的內(nèi)存作為右值使用。

            【規(guī)則3】避免數(shù)組或指針的下標(biāo)越界,特別要當(dāng)心發(fā)生“多1”或者“少1”操作。

            【規(guī)則4】動(dòng)態(tài)內(nèi)存的申請(qǐng)與釋放必須配對(duì),防止內(nèi)存泄漏。

            【規(guī)則5】用free或delete釋放了內(nèi)存之后,立即將指針設(shè)置為NULL,防止產(chǎn)生“野指針”。

            指針與數(shù)組的對(duì)比

            C++/C程序中,指針和數(shù)組在不少地方可以相互替換著用,讓人產(chǎn)生一種錯(cuò)覺,以為兩者是等價(jià)的。

            數(shù)組要么在靜態(tài)存儲(chǔ)區(qū)被創(chuàng)建(如全局?jǐn)?shù)組),要么在棧上被創(chuàng)建。數(shù)組名對(duì)應(yīng)著(而不是指向)一塊內(nèi)存,其地址與容量在生命期內(nèi)保持不變,只有數(shù)組的內(nèi)容可以改變。

            指針可以隨時(shí)指向任意類型的內(nèi)存塊,它的特征是“可變”,所以我們常用指針來操作動(dòng)態(tài)內(nèi)存。指針遠(yuǎn)比數(shù)組靈活,但也更危險(xiǎn)。

            下面以字符串為例比較指針與數(shù)組的特性

            1 修改內(nèi)容

            下例中,字符數(shù)組a的容量是6個(gè)字符,其內(nèi)容為hello。a的內(nèi)容可以改變,如a[0]= 'x'。指針p指向常量字符串"world"(位于靜態(tài)存儲(chǔ)區(qū),內(nèi)容為world),常量字符串的內(nèi)容是不可以被修改的。從語法上看,編譯器并不覺得語句 p[0]= 'x'有什么不妥,但是該語句企圖修改常量字符串的內(nèi)容而導(dǎo)致運(yùn)行錯(cuò)誤。

            #include<iostream.h>

            void main()
            {
                
            char a[] = "hello";
                a[
            0= 'x';
                cout 
            << a << endl;
                
            char *= "world"// 注意p指向常量字符串
                p[0= 'x'// 編譯器不能發(fā)現(xiàn)該錯(cuò)誤
                cout << p << endl;
            }

            2 內(nèi)容復(fù)制與比較

            不能對(duì)數(shù)組名進(jìn)行直接復(fù)制與比較。下例中,若想把數(shù)組a的內(nèi)容復(fù)制給數(shù)組b,不能用語句 b = a ,否則將產(chǎn)生編譯錯(cuò)誤。應(yīng)該用標(biāo)準(zhǔn)庫函數(shù)strcpy進(jìn)行復(fù)制。同理,比較b和a的內(nèi)容是否相同,不能用if(b==a) 來判斷,應(yīng)該用標(biāo)準(zhǔn)庫函數(shù)strcmp進(jìn)行比較。

            語句p = a 并不能把a(bǔ)的內(nèi)容復(fù)制指針p,而是把a(bǔ)的地址賦給了p。要想復(fù)制a的內(nèi)容,可以先用庫函數(shù)malloc為p申請(qǐng)一塊容量為strlen(a)+1個(gè)字符的內(nèi)存,再用strcpy進(jìn)行字符串復(fù)制。同理,語句if(p==a) 比較的不是內(nèi)容而是地址,應(yīng)該用庫函數(shù)strcmp來比較。

            // 數(shù)組…
            char a[] = "hello";
            char b[10];
            strcpy(b, a); 
            // 不能用 b = a;
            if(strcmp(b, a) == 0// 不能用 if (b == a)

            // 指針…
            int len = strlen(a);
            char *= (char *)malloc(sizeof(char)*(len+1));
            strcpy(p,a); 
            // 不要用 p = a;
            if(strcmp(p, a) == 0// 不要用 if (p == a)

             3 計(jì)算內(nèi)存容量

            用運(yùn)算符sizeof可以計(jì)算出數(shù)組的容量(字節(jié)數(shù))。下例(a)中,sizeof(a)的值是12(注意別忘了' ')。指針p指向a,但是 sizeof(p)的值卻是4。這是因?yàn)閟izeof(p)得到的是一個(gè)指針變量的字節(jié)數(shù),相當(dāng)于sizeof(char*),而不是p所指的內(nèi)存容量。 C++/C語言沒有辦法知道指針?biāo)傅膬?nèi)存容量,除非在申請(qǐng)內(nèi)存時(shí)記住它。注意當(dāng)數(shù)組作為函數(shù)的參數(shù)進(jìn)行傳遞時(shí),該數(shù)組自動(dòng)退化為同類型的指針。下例(b)中,不論數(shù)組a的容量是多少,sizeof(a)始終等于sizeof(char *)。

            示例(a)
            char a[] = "hello world";
            char *= a;
            cout
            << sizeof(a) << endl; // 12字節(jié)
            cout<< sizeof(p) << endl; // 4字節(jié)

            示例(b)
            void Func(char a[100])
            {
             cout
            << sizeof(a) << endl; // 4字節(jié)而不是100字節(jié)
            }


             

            posted @ 2012-06-25 10:25 Jacc.Kim 閱讀(273) | 評(píng)論 (0)編輯 收藏

            3D游戲開發(fā)中,該變換時(shí)常發(fā)生。下面記錄下,以備隨時(shí)查詢用。

            說明:
            1)
            已經(jīng)向量v就不用說,可以是3維空間中的任意向量。(不論位置在哪都沒關(guān)系,因?yàn)橄蛄渴桥c位置無關(guān)的)
            2)
            繞已知向量n旋轉(zhuǎn)。此處的n向量,最好要規(guī)范化成單位向量。為什么需要此要求呢?其實(shí)很簡(jiǎn)單,如果不規(guī)范化成單位向量,那么變換后的結(jié)果向量(假如稱為v'),其模就不會(huì)等于原向量v的模。因此就會(huì)出錯(cuò)。(當(dāng)然,如果非要用非規(guī)范化的單位向量參與計(jì)算的話,我們也是可以通過將v'進(jìn)行一些處理,而得到最終想要的向量。)

            下面,確認(rèn)一下,變換矩陣以及變化公式所需的參數(shù)信息:
            v(vx     ,      vy     ,  vz)                                :
            為已經(jīng)向量
            n(nx    ,    ny     ,  nz)                                :
            v向量繞n旋轉(zhuǎn)的基向量
            R(n     ,    angle)                                         : 為旋轉(zhuǎn)所需的變換矩陣。(注釋:angle就是角度,本來可以直接用那些希臘字母,結(jié)果發(fā)現(xiàn)用了,在此,我就輸入不了中文與英文字符了。蛋疼)

            v'(vx'   ,    vy'     , vz')                                : 為變換后最終要得到的結(jié)果向量

            根據(jù)3D變換基礎(chǔ)知識(shí),可得如下公式:
            v' = vR(n, angle)
            其中,v向量已知。R(n, angle)根據(jù)推導(dǎo),可得如下式子:
                                 [ p' ]    [ nx2(1 - cos(angle)) + cos(angle)                            nxny(1 - cos(angle)) + nzsin(angle)                       nxnz(1 - cos(angle)) - nysin(angle) ]
            R(n, angle) =  | q' | = | nxny(1 - cos(angle)) - nzsin(angle)                        ny2(1 - cos(angle)) + cos(angle)                           nynz(1 - cos(angle)) + nxsin(angle) |
                                 [ r' ]     [ nxnz(1 - cos(angle)) + nysin(angle)                       nynz(1 - cos(angle)) - nxsin(angle)                        nz2(1 - cos(angle)) + cos(angle)    ]

            所以,可得最終的v'向量。具體如下:

            v' = vR = (vx, vy, vz)R;

            vx'   = vx * (nx2(1 - cos(angle)) + cos(angle));
            vx' += vy * (nxny(1 - cos(angle)) - nzsin(angle));
            vx' += vz * (nxnz(1 - cos(angle)) + nysin(angle));

            vy'   = vx * (nxny(1 - cos(angle)) + nzsin(angle));
            vy' += vy * (ny2(1 - cos(angle)) + cos(angle));
            vy' += vz * (nynz(1 - cos(angle)) - nxsin(angle));

            vz'   = vx * (nxnz(1 - cos(angle)) - nysin(angle));
            vz' += vy * (nynz(1 - cos(angle)) + nxsin(angle));
            vz' += vz * (nz2(1 - cos(angle)) + cos(angle));

            -----------------------------------------------------------------
            有了上面的知識(shí),現(xiàn)在就可以很容易理解。如下一段旋轉(zhuǎn)攝像機(jī)方向的代碼了:(注:代碼出自徐明亮作者所著《opengl游戲編程》一書中的內(nèi)容)

            /**  旋轉(zhuǎn)攝像機(jī)方向  */
            void Camera::rotateView(float angle, float x, float y, float z)
            {
                Vector3 newView;

                
            /** 計(jì)算方向向量 */
                Vector3 view 
            = m_View - m_Position;        

                
            /** 計(jì)算 sin 和cos值 */
                
            float cosTheta = (float)cos(angle);
                
            float sinTheta = (float)sin(angle);

                
            /** 計(jì)算旋轉(zhuǎn)向量的x值 */
                newView.x  
            = (cosTheta + (1 - cosTheta) * x * x)        * view.x;
                newView.x 
            += ((1 - cosTheta) * x * y - z * sinTheta)    * view.y;
                newView.x 
            += ((1 - cosTheta) * x * z + y * sinTheta)    * view.z;

                
            /** 計(jì)算旋轉(zhuǎn)向量的y值 */
                newView.y  
            = ((1 - cosTheta) * x * y + z * sinTheta)    * view.x;
                newView.y 
            += (cosTheta + (1 - cosTheta) * y * y)        * view.y;
                newView.y 
            += ((1 - cosTheta) * y * z - x * sinTheta)    * view.z;

                
            /** 計(jì)算旋轉(zhuǎn)向量的z值 */
                newView.z  
            = ((1 - cosTheta) * x * z - y * sinTheta)    * view.x;
                newView.z 
            += ((1 - cosTheta) * y * z + x * sinTheta)    * view.y;
                newView.z 
            += (cosTheta + (1 - cosTheta) * z * z)        * view.z;

                
            /** 更新攝像機(jī)的方向 */
                m_View 
            = m_Position + newView;
            }



            posted @ 2012-06-12 11:29 Jacc.Kim 閱讀(1743) | 評(píng)論 (0)編輯 收藏

                 摘要: 下面這個(gè)地址,介紹了幾個(gè)c++中的關(guān)鍵字。還不錯(cuò),就收下了。http://www.shnenglu.com/leetaolion/archive/2011/09/13/46586.html C++關(guān)鍵字:mutable、volatile、explicit以及__based      mutable關(guān)鍵字     關(guān)鍵字mu...  閱讀全文
            posted @ 2012-05-29 13:59 Jacc.Kim 閱讀(243) | 評(píng)論 (0)編輯 收藏

            好久沒有寫博客了。這里記錄一點(diǎn)關(guān)于ccLayer的觸摸處理的記錄,怕將來忘記。
            要使用ccLayer,必要要處理它的觸摸響應(yīng)。否則就少了必要的交互。
            關(guān)于ccLayer的觸摸有兩種類型。
            1) 單點(diǎn)觸摸
            2) 多點(diǎn)觸摸
            下面分別總結(jié)一下:
            1) 單點(diǎn)觸摸
            要使用單點(diǎn)觸摸,必須要重寫以下幾個(gè)接口:
             virtual void onEnter();//必須
             virtual void onExit();//必須
             virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);//必須
             virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);//可選,但一般情況下要
             virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);//可選,但一般情況下要
             virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);//可選
            a) onEnter();接口。在此接口中,需要調(diào)用父類的該接口,并且最重要的,要為當(dāng)前對(duì)象注冊(cè)一個(gè)觸摸委托(即:代理)。參考如下代碼:
             CCLayer::onEnter();
             CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);
            b) onExit();接口。在此接口中,需要將在onEnter()中注冊(cè)的當(dāng)前對(duì)象的觸摸委托給移除掉。然后調(diào)用父類的onExit();。參考代碼如下:
             CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
             CCLayer::onExit();
            c) ccTouchBegan();該接口,細(xì)心的人可能會(huì)發(fā)現(xiàn),就它是返回bool的。它的返回值決定著,后續(xù)的ccTouchMoved();ccTouchEnded();ccTouchCancelled();是否觸發(fā)。只有返回true時(shí)才觸發(fā)。
            關(guān)于ccTouchBegan();ccTouchMoved();ccTouchEnded();ccTouchCancelled();的作用,在此我就不多說了。看名字我想就應(yīng)該能清楚。

            2) 多點(diǎn)觸摸
            要使用多點(diǎn)觸摸。則只需要實(shí)現(xiàn)重寫如下幾個(gè)接口,即可:
             virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
             virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);
             virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);
            它們的作用我也不多說。下面簡(jiǎn)要介紹下CCSet*對(duì)象。它存儲(chǔ)著所有的觸摸信息。即:遍歷它,可以處理所有的觸摸點(diǎn)響應(yīng)。其他的用法同單點(diǎn)觸摸一樣。
            注意:用多點(diǎn)觸摸時(shí),不需要注冊(cè)上面第 1) 點(diǎn)中的a)與b)小點(diǎn)的信息。(如果我沒記錯(cuò)的話,處理了,可能會(huì)讓程序蹦掉。)
            posted @ 2012-04-11 12:08 Jacc.Kim 閱讀(5684) | 評(píng)論 (0)編輯 收藏

                 摘要: 原文轉(zhuǎn)自:http://www.shnenglu.com/tx7do/archive/2006/04/24/6146.html TinyXml學(xué)習(xí)筆記張弛 <zhangchi@china.com>一、      TinyXml 的特點(diǎn)TinyXml 是一個(gè)基于 DOM ...  閱讀全文
            posted @ 2012-02-14 17:40 Jacc.Kim 閱讀(358) | 評(píng)論 (0)編輯 收藏

            CCDirector::sharedDirector()->getWinSize();
            CCSprite::setPosition(...);
            CCSprite::getPosition();

            以上這些都是以 點(diǎn) 為單位的(不是像素)。
            猜測(cè):所有在cocos2d中有關(guān)x與y有關(guān)的,應(yīng)該都是以點(diǎn)為單位。
            posted @ 2012-02-01 09:48 Jacc.Kim 閱讀(366) | 評(píng)論 (0)編輯 收藏

            1) 場(chǎng)景創(chuàng)建出來后,如果有autorelease。則在該場(chǎng)景被替換掉時(shí),先前場(chǎng)景將被釋放。(這點(diǎn)在cc的管理器中有注釋說明)
            2) 場(chǎng)景創(chuàng)建出來后,如果沒有autorelease。則在被添加進(jìn)管理器后,請(qǐng)一定要release一次。否則將會(huì)有內(nèi)存泄漏
            posted @ 2012-01-18 10:31 Jacc.Kim 閱讀(456) | 評(píng)論 (0)編輯 收藏

            1) CCMenuItem*對(duì)象在創(chuàng)建出來后并添加到CCMenu*對(duì)象后,只要CCMenu對(duì)象有釋放,CCMenuItem*對(duì)象也將被釋放。即:CCMenuItem*對(duì)象不需要再將釋放。。否則可能會(huì)蹦。
            2) CCMenu*對(duì)象被addChild到父對(duì)象后,最后只要remove出來就可以。remove出來后,CCMenu*對(duì)象就被釋放掉。(原因:因?yàn)镃CMenu*對(duì)象被創(chuàng)建出來時(shí),它是autorelease()的)

             ccColor3B color;
             color.r = 0;
             color.g = 0;
             color.b = 255;
             m_pPlayMenuItem = CCMenuItemFont::itemFromString("Play", this, menu_selector(CWelcomeScene::OnPlayGameMenuItemClicked));
             ((CCMenuItemFont*)m_pPlayMenuItem)->setFontSizeObj(12);
             ((CCMenuItemFont*)m_pPlayMenuItem)->setColor(color);
             
             color.r = 255;
             color.g = 0;
             color.b = 0;
             m_pExitMenuItem = CCMenuItemFont::itemFromString("Exit", this, menu_selector(CWelcomeScene::OnExitMenuItemClicked));
             ((CCMenuItemFont*)m_pExitMenuItem)->setFontSizeObj(12);
             ((CCMenuItemFont*)m_pExitMenuItem)->setColor(color);

             if (NULL == m_pMenu)
             {
              m_pMenu = CCMenu::menuWithItems(m_pPlayMenuItem, m_pExitMenuItem, NULL);
              this->addChild(m_pMenu, 1);
              m_pMenu->alignItemsVerticallyWithPadding(0.0f);
             }


             m_pPlayMenuItem = CCMenuItemImage::itemFromNormalImage(g_pcszStartNormalMI, g_pcszStartSelectedMI, this, menu_selector(CWelcomeScene::OnPlayGameMenuItemClicked));
             m_pExitMenuItem = CCMenuItemImage::itemFromNormalImage(g_pcszCloseNormalMI, g_pcszCloseSelectedMI, this, menu_selector(CWelcomeScene::OnExitMenuItemClicked)); 
             m_pPlayMenuItem->setScale(0.4f);

             m_pMenu = CCMenu::menuWithItems(m_pPlayMenuItem, m_pExitMenuItem, NULL);
             this->addChild(m_pMenu, 1);

             float fX = GetCurrentWinSize(true).width / 2.0f - 10.0f;
             float fY = -(GetCurrentWinSize(false).height / 2.0f) + 5.0f;
             m_pExitMenuItem->setAnchorPoint(ccp(1.0, 0.0f));
             m_pExitMenuItem->setPosition(ccp(fX, fY));

             fX -= m_pExitMenuItem->getContentSize().width + 2.0f;
             fY -= 2.0f;
             m_pPlayMenuItem->setAnchorPoint(ccp(1.0f, 0.0f));
             m_pPlayMenuItem->setPosition(ccp(fX, fY));



            posted @ 2012-01-18 10:25 Jacc.Kim 閱讀(786) | 評(píng)論 (1)編輯 收藏

            僅列出標(biāo)題
            共14頁: First 5 6 7 8 9 10 11 12 13 Last 
            久久久久波多野结衣高潮| 国产精品久久久久…| 九九久久精品无码专区| 久久久国产一区二区三区| 伊色综合久久之综合久久| 伊人久久综合精品无码AV专区 | 伊人久久大香线蕉av不卡| 欧美激情精品久久久久久久| 99久久夜色精品国产网站| 久久香蕉国产线看观看猫咪?v| 国产高清美女一级a毛片久久w| 中文字幕乱码人妻无码久久| 久久婷婷是五月综合色狠狠| 久久精品国产AV一区二区三区| 久久久久亚洲av成人网人人软件| 亚洲一区精品伊人久久伊人| 久久噜噜电影你懂的| 亚洲综合伊人久久综合| 亚洲色大成网站WWW久久九九| 一本色道久久综合狠狠躁| 久久综合久久自在自线精品自| 久久久久人妻精品一区三寸蜜桃| 久久精品国产一区二区电影| 欧美精品丝袜久久久中文字幕| 午夜精品久久久久| 午夜精品久久久久久久久| 久久久精品久久久久特色影视| 久久久网中文字幕| 无码国内精品久久综合88| 久久播电影网| 伊人久久亚洲综合影院| aaa级精品久久久国产片| jizzjizz国产精品久久| 麻豆精品久久久久久久99蜜桃| 熟妇人妻久久中文字幕| 97热久久免费频精品99| 久久97久久97精品免视看秋霞| 久久人人爽人人爽人人片AV不 | 国产精品久久新婚兰兰| 久久香蕉超碰97国产精品| 国产精品一区二区久久精品无码|