• <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>
            posts - 15,comments - 21,trackbacks - 0
            很久沒有碰C++,下個項目要開始使用C++,所以很多東西需要撿起來重新看看。從今天起記錄一些筆記,方便自己今后查閱。言歸正傳,先從構造函數的初始化列表說起。我把這個知識點細化成3個問題,1.為什么要有初始化列表,它和構造函數中初始化有什么不一樣。2.初始化的順序。3.一些注意的細節。
                  先說第一個問題。我們有兩個東西,是必須在初始化列表里完成的,一個是const修飾的變量,一個是引用。這點我就不細說了,查閱資料的都可以找到。下面我具體說說類成員。
            class Test1
            {
            public:
            Test1()
            {
            cout << "Test1 default constructor" << endl;
            }
            Test1(int i)
            {
            cout << "Test1 value constructor" << endl;
            }
            Test1(const Test1 &obj)
            {
            cout << "Test1 copy constructor" << endl;
            }
            Test1& operator = (const Test1 &obj)
            {
            cout << "Test1 = constructor" << endl;
            return *this;
            }
            ~Test1()
            {
            cout << "Test1 destructor" << endl;
            }
            };
             我定義一個類,然后再定義一個類,其中包含Test1
            class Test2
            {
            public:
                Test2()
                {
                    t1 = Test1(1);
                }

            private:
                Test1 t1;
            }; 
            我們在構造函數中初始化Test1,我們看看運行結果
            Test1 default constructor
            Test1 value constructor
            Test1 = constructor
            Test1 destructor
            Test1 destructor
            我們分析下這個輸出。Test1 default constructor,這說明在進入Test1構造函數之前,已經初始化了t1成員,并且調用的是無參構造函數。Test1 value constructor這個是Test1(1)創建出來的對象。Test1 = constructor,這個表示條用了拷貝構造函數,Test1 destructor這個表示Test1(1)這個臨時對象的析構,Test1 destructor這個表示是t1這個成員對象的析構。從上面的結果來看,構造函數中t1 = Test1(1);其實并不是真正意義是上的初始化,而是一次拷貝賦值。當進入構造函數內部之前,類成員會被默認構造函數初始化。如果說Test1是個很大的對象,這塊會造成性能上的開銷。所以,這點也是使用初始化列表的原因之一。
                  第二我們再來說下順序問題。簡單的原則是初始化列表里的會先于構造函數中,初始化列表里會按照變量聲明的順序。我們具體看看下面的例子。
            class Test3()
            {
            public:
                Test3(
            int x,int y,int z):_z(z),_y(y)
                {
                    _x 
            = x;
                }

            private:
                
            int _x,_y,_z;
            };
            按照上面的說法,賦值的順序是_y,_z,_x。
                  第三個是注意問題,每個成員只能在初始化列表里出現一次。
            class Test3
            {
            public:
                Test3(
            int x,int y,int z):_z(z),_y(y),_z(x)
                {
                    _x 
            = x;
                }

            private:
                
            int _x,_y,_z;
            };
            比如這種就是問題的。_z被初始化了2次。
            posted @ 2012-08-14 10:24 梨樹陽光 閱讀(1464) | 評論 (3)編輯 收藏
                  寫服務器的,通常會涉及到內存池的東西,自己在這方面也看了寫了一些東西,有些體會,寫出來跟大家分享下。
                  內存池基本包含以下幾個東西,第一,初始化。第二,分配內存。第三,回收內存。所謂初始化,就是在服務器啟動的時候,或者第一次需要內存的時候,系統分配很大的一塊內存,方便之后的使用。分配內存,就是從內存池中取出需要的內存給外部使用,當然這里需要考慮的是當內存池中沒有內存可分配時候的處理。回收內存,簡單來說,就是外面對象生命期結束了,將分配出去的內存回收入內存池中。好了簡單概念就說完了,我們先來看一種最簡單的設計方式。
            //為了方便描述,這里附上幾個簡單的鏈表操作宏
            #define INSERT_TO_LIST( head, item, prev, next ) \
            do{ \
            if ( head ) \
            (head)->prev = (item); \
            (item)->next = (head); \
            (head) = (item);          \
            }while(0)
            #define REMOVE_FROM_LIST(head, item, prev, next) \
            do{ \
            if ( (head) == (item) ) \
            { \
            (head) = (item)->next; \
            if ( head ) \
            (head)->prev = NULL; \
            } \
            else \
            { \
            if ( (item)->prev ) \
            (item)->prev->next = (item)->next;          \
            \
            if ( (item)->next ) \
            (item)->next->prev = (item)->prev;          \
            } \
            }while(0)
            struct student
            {
                  char name[32];
                  byte sex;
                  
                  struct student *prev,*next;
            };

            static struct mem_pool
            {
                  //該指針用來記錄空閑節點
                  
            struct student *free;
                  //該變量記錄分配結點個數
                  size_t alloc_cnt;
            }s_mem_pool;

            //分配內存“塊”的函數
            bool mem_pool_resize(size_t size)
            {
                  //該函數創建size個不連續的對象,把他們通過鏈表的方式加入到s_mem_pool.free中
                  for ( size_t i = 0;i < size;++i )
                  {
                        
            struct student *p = (struct student *)malloc(sizeof(struct student));
                        
            if ( !p )
                           return false;
                        
                        p->prev = p->next = NULL;
                        INSERT_TO_LIST(s_mem_pool.free,p,prev,next);

                  }

                  s_mem_pool.alloc_cnt += size;
            }

            #define MEM_INIT_SIZE 512  
            #define MEM_INC_SIZE 256
            //初始化函數
            bool mem_pool_init()
            {
            if ( !mem_pool_resize(MEM_INIT_SIZE) )
                        return false;
            return true;
            }
            struct student *get_data()
            {
            if ( s_mem_pool.free == NULL )
            {
                        if ( !mem_pool_resize(MEM_INC_SIZE) )
                              return NULL;
            }
            struct student *ret = s_mem_pool.free;
            REMOVE_FROM_LIST(s_mem_pool.free,ret,prev,next)
            return ret;
            }
            void free_data(struct student *p)
            {
            if ( !p )
                        return;
            memset(p,0,sizeof(struct student));
            INSERT_TO_LIST(s_mem_pool.free,p,prev,next)
            }
            好了最簡單的內存池的大致框架就是這樣。我們先來看下他的過程。首先,在mem_pool_init()函數中,他先分配512個不連續的student對象。每分配出來一個就把它加入到free鏈表中,初始化完成后內存池大概是這樣的

            接下來就是從內存池中取出一個對象get_data()。函數先去判斷是否有空閑的對象,有則直接分配,否則再向系統獲取一"塊"大的內存。調用一次后的內存池大概是這樣的

            釋放對象,再把對象加入到Free鏈表中。
            以上就是過程的簡單分析,下面我們來看看他的缺點。
            第一,內存不是連續的,容易產生碎片
            第二,一個類型就得寫一個這樣的內存池,很麻煩
            第三,為了構建這個內存池,每個沒對象必須加上一個prev,next指針
            好了,我們來優化一下它。我們重新定義下我們的結構體
            union student
            {
                
            int index;
                
            struct
                {
                    
            char name[32];
                    
            byte sex;
                }s;
            };

            static struct mem_pool
            {
                
            //該下標用來記錄空閑節點
                int free;
                
            //內存池
                union student *mem;
                
            //已分配結點個數
                size_t alloc_cnt;
            }s_mem_pool;

            //分配內存塊的函數
            bool mem_pool_resize(size_t size)
            {
                size_t new_size 
            = s_mem_pool.alloc_cnt+size;
                union student 
            *tmp = (union student *)realloc(s_mem_pool.mem,new_size*sizeof(union student));
                
            if ( !tmp )
                    
            return false;
                    
                memset(tmp
            +s_mem_pool.alloc_cnt,0,size*sizeof(union student));
                size_t i 
            = s_mem_pool.alloc_cnt;
                
            for ( ;i < new_size - 1;++i )
                {
                    tmp[i].index 
            = i + 1;
                }
                
                tmp[i].index 
            = -1;
                s_mem_pool.free 
            = s_mem_pool.alloc_cnt;
                s_mem_pool.mem 
            = tmp;
                s_mem_pool.alloc_cnt 
            = new_size;
                
                
            return true;
            }

            #define MEM_INIT_SIZE    512  
            #define MEM_INC_SIZE    256
            //初始化函數
            bool mem_pool_init()
            {
                
            if ( !mem_pool_resize(MEM_INIT_SIZE) )
                    
            return false;
                    
                
            return true;
            }

            union student 
            *get_data()
            {
                
            if ( s_mem_pool.free == -1 )
                {
                    
            if ( !mem_pool_resize(MEM_INC_SIZE) )
                        
            return NULL;
                }
                
                union student 
            *ret = s_mem_pool.mem+s_mem_pool.free;
                s_mem_pool.free 
            = ret->index;
                
            return ret;
            }

            void free_data(union student *p)
            {
                
            if ( !p )
                    
            return;
                
                p
            ->index = s_mem_pool.free;
                s_mem_pool.free 
            = p - s_mem_pool.mem;
            }
            我們來看看改進了些什么。第一student改成了聯合體,這主要是為了不占用額外的內存,也就是我們上面所說的第三個缺點,第二,我們使用了realloc函數,這樣我們可以使我們分配出來的內存是連續的。我們初始化的時候多了一個for循環,這是為了記錄空閑對象的下標,當我們取出一個對象時,free可以立刻知道下一個空閑對象的位置,釋放的時候,對象先記錄free此時的值,接著再把free賦值成該對象在數組的下標,這樣就完成了回收工作。
            我們繼續分析這段代碼,問題在realloc函數上,如果我們的s_mem_pool.mem已經很大了,在realloc的時候我們都知道,先要把原來的數據做一次拷貝,所以如果數據量很大的情況下做一次拷貝,是會消耗性能的。那這里有沒有好的辦法呢,我們進一步優化
            思路大概是這樣
            初始化

            再次分配的時候,我們只需要重新分配新的內存單元,而不需要拷貝之前的內存單元。

            因此基于此思路,我們修改我們的代碼
            #include <stdio.h>
            #include 
            <stdlib.h>

            struct student
            {
                
            int index;

                
            char name[32];
                
            byte sex;
            };

            static struct mem_pool
            {
                
            //該下標用來記錄空閑節點
                int free;
                
            //內存池
                struct student **mem;
                
            //已分配塊個數
                size_t block_cnt;
            }s_mem_pool;

            #define BLOCK_SIZE        256        //每塊的大小
            //分配內存塊的函數
            bool mem_pool_resize(size_t block_size)
            {
                size_t new_cnt 
            = s_mem_pool.block_cnt + block_size;
                
            struct student **tmp = (struct student **)realloc(s_mem_pool.mem,new_size*sizeof(struct student *));
                
            if ( !tmp )
                    
            return false;
                    
                memset(tmp
            +s_mem_pool.block_cnt,0,size*sizeof(struct student*));
                
            for ( size_t i = s_mem_pool.block_cnt;i < new_cnt;++i )
                {
                    tmp[i] 
            = (struct student *)calloc(BLOCK_SIZE,sizeof(struct student));
                    
            if ( !tmp[i] )
                        
            return false;
                        
                    size_t j 
            = 0;
                    
            for(;j < BLOCK_SIZE - 1;++j )
                    {
                        tmp[i][j].index 
            = i*BLOCK_SIZE+j+1;
                    }
                    
                    
            if ( i != new_cnt-1 )
                        tmp[i][j].index 
            = (i+1)*BLOCK_SIZE;
                    
            else
                        tmp[i][j].index 
            = -1;
                }
                
                s_mem_pool.free 
            = s_mem_pool.alloc_cnt*BLOCK_SIZE;
                s_mem_pool.mem 
            = tmp;
                s_mem_pool.block_cnt 
            = new_cnt;
                
                
            return true;
            }
             
            #define MEM_INC_SIZE    10
            //初始化函數
            bool mem_pool_init()
            {
                
            if ( !mem_pool_resize(MEM_INIT_SIZE) )
                    
            return false;
                    
                
            return true;
            }

            struct student *get_data()
            {
                
            if ( s_mem_pool.free == -1 )
                {
                    
            if ( !mem_pool_resize(MEM_INC_SIZE) )
                        
            return NULL;
                }
                
                
            struct student *ret = s_mem_pool.mem[s_mem_pool.free/BLOCK_SIZE]+s_mem_pool.free%BLOCK_SIZE;
                
            int pos = s_mem_pool.free;
                s_mem_pool.free 
            = ret->index;
                ret
            ->index = pos;
                
            return ret;
            }

            void free_data(struct student *p)
            {
                
            if ( !p )
                    
            return;
                
                
            int pos = p->index;
                p
            ->index = s_mem_pool.free;
                s_mem_pool.free 
            = pos;
            }
            這里不一樣的地方主要在mem_pool_resize函數中,mem變成了2級指針,每次realloc的時候只需要分配指針數組的大小,無須拷貝對象,這樣可以提高效率,但是為了在釋放的時候把對象放回該放的位置,我們這里在結構體里加入了index變量,記錄它的下標。在內存池里,它表示下個空閑對象的下標,在內存池外,它表示在內存池中的下標。總的來說滿足了一個需求,卻又帶來了新的問題,有沒有更好的方法呢,答案是肯定,不過今天先寫到這里,明天繼續。
            posted @ 2012-07-19 11:41 梨樹陽光 閱讀(3518) | 評論 (2)編輯 收藏
                  昨天在看一篇文章的時候,突然想起了這個基礎性的問題,自己一直對它的區別不是很清楚,于是今天上午研究下了,分享下自己的理解。(對它很清楚的同學們可以略過此篇文章)
                  從存儲方式來說,文件在磁盤上的存儲方式都是二進制形式,所以,文本文件其實也應該算二進制文件。那么他們的區別呢,各自的優缺點呢?不急,我慢慢道來。
                  先從他們的區別來說,雖然都是二進制文件,但是二進制代表的意思不一樣。打個比方,一個人,我們可以叫他的大名,可以叫他的小名,但其實都是代表這個人。二進制讀寫是將內存里面的數據直接讀寫入文本中,而文本呢,則是將數據先轉換成了字符串,再寫入到文本中。下面我用個例子來說明。
            我們定義了一個結構體,表示一個學生信息,我們打算把學生的信息分別用二進制和文本的方式寫入到文件中。
            struct Student 
            {
                
            int num;
                
            char name[20];
                
            float score;
            };
            我們定義兩個方法,分別表示內存寫入和文本寫入
            //使用二進制寫入
            void write_to_binary_file()
            {
                
            struct Student stdu;
                stdu.num 
            = 111;
                sprintf_s(stdu.name,
            20,"%s","shine");
                stdu.score 
            = 80.0f;
                fstream binary_file(
            "test1.dat",ios::out|ios::binary|ios::app); //此處省略文件是否打開失敗的判斷
                binary_file.write((char *)&stdu,sizeof(struct Student));//二進制寫入的方式
                binary_file.close();

            //文本格式寫入
            void write_to_text_file()
            {
                
            struct Student stdu;
                stdu.num 
            = 111;
                sprintf_s(stdu.name,
            20,"%s","shine");
                stdu.score 
            = 80.0f;
                FILE 
            *fp = fopen("test2.dat","a+");   //此處省略文件是否打開失敗的判斷
                fprintf(fp,"%d%s%f",stdu.num,stdu.name,stdu.score); //將數據轉換成字符串(字符串的格式可以自己定義)
                fclose(fp);

            //MAIN函數調用前面兩個方法
            int _tmain(int argc, _TCHAR* argv[])
            {
                write_to_binary_file();
                write_to_text_file();
                
                
            return 0;
            }
            我們來看下,文件里面的格式 2進制文件

            文本文件


            2進制文件里面將111編碼成6F,1個字節,這剛好是111的16進制表示,而文本文件中則寫成31,31,31用了3個字節,表示111。73   68   69   6E   65 表示shine,之后2進制文件里是幾個連續的FE,而文本文件中是38   30......文本文件將浮點數80.000000用了38(表示8)   30(表示0)  2E(表示.)   30(表示0)   30(表示0)   30(表示0)   30(表示0)   30(表示0)   30(表示0),二進制文件用了4個字節表示浮點數00   00   A0   42
            通過這里我們可以初見端倪了,二進制將數據在內存中的樣子原封不動的搬到文件中,文本格式則是將每一個數據轉換成字符寫入到文件中,他們在大小上,布局上都有著區別。由此可以看出,2進制文件可以從讀出來直接用,但是文本文件還多一個“翻譯”的過程,因此2進制文件的可移植性好。
            posted @ 2012-07-12 09:59 梨樹陽光 閱讀(13023) | 評論 (5)編輯 收藏

            先看一個簡單的使用例子

            求任意個自然數的平方和:

            int SqSum(int n,)   
            {   
                va_list arg_ptr;   
                
            int sum = 0,_n = n;   
                arg_ptr 
            = va_start(arg_ptr,n);   
                
            while(_n != 0)   
                {   
                    sum 
            += (_n*_n);   
                    _n 
            = va_arg(arg_ptr,int);   
                }   
                va_end(arg_ptr);   
                
            return sum;   
            }
            首先解釋下函數參數入棧情況 在VC等絕大多數C編譯器中,默認情況下,參數進棧的順序是由右向左的,因此,參數進棧以后的內存模型如下圖所示:
            最后一個固定參數的地址位于第一個可變參數之下,并且是連續存儲的。
            | 最后一個可變參數(高內存地址處) | 第N個可變參數 | 第一個可變參數 | 最后一個固定參數 | 第一個固定參數(低內存地址處)
            明白上面那個順序,就知道其實可變參數就是玩弄參數的地址,已達到“不定”的目的 下面我摘自VC中的源碼來解釋
            va_list,va_start,va_arg,va_end宏
            1.其實va_list就是我們平時經常用的char* typedef char * va_list;
            2.va_start該宏的目的就是將指針指向最后一個固定參數的后面,即第一個不定參數的起始地址 #define va_start(ap,v)( ap = (va_list)&v + _INTSIZEOF(v) ) v即表示最后一個固定參數,&v表示v的地址, #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) 該宏其實是一個內存對齊的操作。即表示大于sizeof(n)且為sizeof(int)倍數的最小整數。這句話有點繞,其實舉幾個例子就簡單了。比如1--4,則返回4,5--8則返回8
            3.va_arg 該宏的目的是將ap指針繼續后移,讀取后面的參數,t表示參數類型。該宏首先將ap指針移動到下一個參數的起始地址ap += _INTSIZEOF(t),然后將本參數的值返回 #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
            4.va_end將指針賦空 #define va_end(ap) ap = (va_list)0 有了這個分析我們可以把上例中的代碼重新翻譯下
            int SqSum(int n,)   
            {   
                
            char *arg_ptr;   
                
            int sum = 0,_n = n;   
                arg_ptr 
            = (char *)&+ 4;//本機上sizeof(int) = 4   
                while(_n != 0)   
                {   
                    sum 
            += (_n*_n);   
                    arg_ptr 
            += 4;   
                    _n 
            = *(int *)(arg_ptr-4);   
                }   
                arg_ptr 
            = (void*)0;   
            }  
            這樣我們也可以寫出我們自己的printf了
            posted @ 2012-07-12 09:51 梨樹陽光 閱讀(1054) | 評論 (1)編輯 收藏

            1.制作自己的動態庫和靜態庫

            linux下動態庫以.so結尾,靜態庫以.a結尾,它們都以lib開頭,比如一個庫名為net,那么它的全名應該是libnet.so或者libnet.a

            我們有兩個文件,hello.ctest.c,下面是兩個文件的內容

            //hello.c

            #include <stdio.h>

             

            void my_lib_func()

            {

            printf("Library routine called\r\n");

            }

             

            //test.c

            #include <stdio.h>

             

            int main()

            {

            my_lib_func();

             

            return 1;

            }

             

            test.c調用了hello.c的方法,我們把hello.c封裝成庫文件。無論是靜態庫還是動態庫,都是由.o文件組成,我們先把gcc -c hello.c生成.o文件

            制作靜態庫

            ar crv libmyhello.a hello.o,ar是生成靜態庫的命令,libmyhello.a是我的靜態庫名。下一步就是在我的程序中使用靜態庫

            可以看到已經有了Library routine called的結果,說明調用成功了。

            下面我們刪除libmyhello.a,看看程序是否還是運行正常


            我們發現程序依然運行正常,說明靜態庫已經連接進入我們的程序中

            制作動態庫


            我們看見動態庫libmyhello.so已經生成,下面繼續使用


            找不到庫文件,這個時候我們把so文件拷貝到/usr/lib下面


            運行成功

             

            2.動態庫和靜態庫同時存在的調用規則

            我們可以發現,不論是動態庫還是靜態庫,程序編譯連接的時候都是加的參數-l,那么當他們同時存在的時候,程序會選擇動態庫還是靜態庫呢。我們做個嘗試。


            我們同時存在libmyhello.alibmyhello.so,我們發現運行的時候,出現找不到動態庫的錯誤,由此,我們可以得出結論,同時存在動態庫和靜態庫的時候,gcc會優先選擇動態庫

            posted @ 2012-07-11 15:15 梨樹陽光 閱讀(1493) | 評論 (0)編輯 收藏
            僅列出標題
            共2頁: 1 2 
            日韩精品久久久久久| 婷婷伊人久久大香线蕉AV| 91精品国产高清久久久久久91| 99久久婷婷国产综合亚洲| 久久99精品久久久久久噜噜| 久久久久久久久久久| 久久香蕉国产线看观看99| 欧洲国产伦久久久久久久| 久久久精品国产sm调教网站| 国产综合精品久久亚洲| 日韩精品久久久肉伦网站| 国产精品欧美亚洲韩国日本久久 | 国产成人综合久久精品尤物| 久久久久婷婷| 日本久久久久久中文字幕| 亚洲午夜久久久久久噜噜噜| 国产精品综合久久第一页| 狠狠色丁香久久婷婷综合五月| 99久久国产亚洲综合精品| 精品水蜜桃久久久久久久| 97久久精品午夜一区二区| 日本五月天婷久久网站| 美女久久久久久| 国内精品久久久久久久涩爱| 久久香蕉一级毛片| aaa级精品久久久国产片| 中文字幕无码精品亚洲资源网久久| 久久精品不卡| 久久久久亚洲AV成人网人人网站| 99久久精品这里只有精品| 国内精品久久久久久野外| 久久久久久久久无码精品亚洲日韩| 97精品伊人久久大香线蕉| 亚洲国产精品一区二区三区久久 | 国产精品一区二区久久精品涩爱 | 精品久久久中文字幕人妻| 久久国产亚洲精品| 久久国产AVJUST麻豆| 久久婷婷色综合一区二区| 中文字幕久久精品无码| 亚洲av成人无码久久精品|