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

            2008年8月6日

            如何 學(xué)習(xí)C++

            我們?cè)趯W(xué)習(xí)C++的時(shí)候,有很多人不知怎樣學(xué)習(xí)它、怎樣學(xué)好它,也不知應(yīng)先從那里開(kāi)始學(xué)起,關(guān)于C++的入門其實(shí)很簡(jiǎn)單,你只要一步步按照你手中的那本C++教材來(lái)學(xué)就可以了,也許你把C++學(xué)的很爛,這時(shí)你千萬(wàn)不要認(rèn)為我好像很精通C++了。

            我個(gè)人認(rèn)為要想學(xué)習(xí)C++,最好直接學(xué)習(xí)它,不要先學(xué)習(xí)C語(yǔ)言,然后在學(xué)習(xí)C++,雖然C++是從C語(yǔ)言上發(fā)展過(guò)來(lái)的,但如果你對(duì)C語(yǔ)言了解的越 多,在你寫(xiě)C++程序的時(shí)候,你很難擺脫C的風(fēng)格,既使你是一位很有經(jīng)驗(yàn)的程序員,如果你對(duì)C很了解,在學(xué)習(xí)C++的時(shí)候,盡量使用C++的風(fēng)格,我這樣 并不是說(shuō)C不好,關(guān)鍵我們現(xiàn)在要了解的是C++而不是C。

            現(xiàn)在讓我們深入學(xué)習(xí)C++吧!C++的難學(xué),不僅在它那廣博的語(yǔ)法、語(yǔ)法背后的語(yǔ)義、語(yǔ)義背后的深層思維、深層思維背后的對(duì)像模型;C++的難 學(xué),還在與它提供了四種不同的編程思維模型。當(dāng)我們找來(lái)一本C++教材時(shí),當(dāng)我們翻開(kāi)第一頁(yè)時(shí),這時(shí)我們已進(jìn)入了C++的世界,我們現(xiàn)在開(kāi)始探索,開(kāi)始在 追求新技術(shù)的旅程中!

            想學(xué)好C++,熟練掌握它的語(yǔ)法是不可少的,當(dāng)你掌握了C++的語(yǔ)法時(shí),那么我要恭喜你,你已正正進(jìn)入了C++的世界,要想學(xué)好C++,你只有努 力的學(xué)習(xí),經(jīng)常的思考多多的實(shí)踐,這時(shí)你會(huì)問(wèn)了,我應(yīng)該還要學(xué)習(xí)什么呢? C++的語(yǔ)法我都已掌握了啊!我是不是可以學(xué)習(xí)Windows編程了呢?不要急,你是已掌握了C++的語(yǔ)法,但你能用它寫(xiě)出高效率的程序嗎?你已對(duì)C++ 所有運(yùn)行機(jī)制都了解嗎?是的,單單了解C++語(yǔ)法是不夠的,接下來(lái)你的任務(wù)很多,要學(xué)習(xí)如何高效地使用C++語(yǔ)言。現(xiàn)在我就教你怎樣的學(xué)好它,怎樣的高效 使用它。

            我們還是先從C++的語(yǔ)法開(kāi)始說(shuō)起吧!這里我只做一個(gè)簡(jiǎn)單的概述,當(dāng)我們學(xué)習(xí)C++的時(shí)候,你先要了解它的編程模式,其中包括面向?qū)ο窬幊獭⑼ㄓ? 編程和傳統(tǒng)的過(guò)程化編程。當(dāng)你在學(xué)習(xí)一個(gè)C++語(yǔ)法時(shí),如果你一時(shí)感到很難理解,不妨你先跳過(guò)這一段,繼續(xù)向后學(xué)習(xí),當(dāng)你看完你所學(xué)習(xí)C++的那本教材 時(shí),你在回過(guò)頭來(lái)學(xué)習(xí)C++,你會(huì)發(fā)現(xiàn)其實(shí)它就是那么回事,有很多人在學(xué)習(xí)C++時(shí),剛學(xué)習(xí)到了一半,突然感到好像以前學(xué)習(xí)的語(yǔ)法忘了許多,他們會(huì)把書(shū)又 翻回去,找回那忘掉的語(yǔ)法,如果你在學(xué)習(xí)C++時(shí)也有這樣的情況,你大可不必那么擔(dān)心,你現(xiàn)在的任務(wù)是繼續(xù)你的學(xué)習(xí),不要去管那一時(shí)不記得的語(yǔ)法,如果你 現(xiàn)在去重新學(xué)習(xí)那一時(shí)忘掉的C++,恩,不錯(cuò),這看起來(lái)你好像對(duì)那語(yǔ)法已深深的牢記在心,當(dāng)你的C++在學(xué)習(xí)到這里時(shí),你能保證前面的語(yǔ)法不在遺忘嗎?這 時(shí)的你在學(xué)習(xí)新的C++語(yǔ)法時(shí),但心會(huì)忘掉前面剛剛找回的C++,你說(shuō)這時(shí)你能學(xué)好新的C++語(yǔ)法嗎?你會(huì)一邊學(xué)習(xí)新的,一邊重復(fù)舊的,這樣一來(lái),那就糟 了,這時(shí)的你會(huì)很容易搞亂新舊C++語(yǔ)法,新的記不住,舊的又被新的語(yǔ)法搞亂了,這時(shí)的你不得不從頭再來(lái)(畢竟你是初學(xué)者)。

            對(duì)于初學(xué)者來(lái)說(shuō),C++的廣博語(yǔ)法是件頭疼的事,學(xué)會(huì)了這個(gè)卻忘了那個(gè),就像我上面提到的那樣,這時(shí)的你應(yīng)該繼續(xù)的學(xué)習(xí)C++新知識(shí),等看完你手 中的那本C++教材時(shí),你在來(lái)學(xué)習(xí)忘掉的語(yǔ)法,這時(shí)你會(huì)感覺(jué)好像C++很簡(jiǎn)單,沒(méi)有我們開(kāi)始說(shuō)的那么難學(xué)啊!你會(huì)覺(jué)得我開(kāi)始說(shuō)C++難學(xué)是用來(lái)嚇唬人的。 我說(shuō)C++難學(xué)當(dāng)然不是用來(lái)嚇唬人的,這時(shí)的你對(duì)C++語(yǔ)法已非常熟悉了,這時(shí)你千萬(wàn)不要認(rèn)為對(duì)C++已很精通,就像我開(kāi)頭所說(shuō)的那樣,雖然現(xiàn)在你已擺脫 了初學(xué)著的稱呼,但你也不能算是位精通人士啊!你只掌握了C++的大概,接下來(lái)的你就要深入學(xué)習(xí)拉!

            posted @ 2008-08-06 22:18 lovetiffany 閱讀(3341) | 評(píng)論 (2)編輯 收藏

            2008年3月14日

            C/C++ 程序設(shè)計(jì)員應(yīng)聘常見(jiàn)面試試題深入剖析

            本文的寫(xiě)作目的并不在于提供C/C++程序員求職面試指導(dǎo),而旨在從技術(shù)上分析面試題的內(nèi)涵。文中的大多數(shù)面試題來(lái)自各大論壇,部分試題解答也參考了網(wǎng)友的意見(jiàn)。

            許多面試題看似簡(jiǎn)單,卻需要深厚的基本功才能給出完美的解答。企業(yè)要求面試者寫(xiě)一個(gè)最簡(jiǎn)單的strcpy函數(shù)都可看出面試者在技術(shù)上究竟達(dá)到了怎樣的程 度,我們能真正寫(xiě)好一個(gè)strcpy函數(shù)嗎?我們都覺(jué)得自己能,可是我們寫(xiě)出的strcpy很可能只能拿到10分中的2分。讀者可從本文看到strcpy 函數(shù)從2分到10分解答的例子,看看自己屬于什么樣的層次。此外,還有一些面試題考查面試者敏捷的思維能力。

              分析這些面試題,本身包含很強(qiáng)的趣味性;而作為一名研發(fā)人員,通過(guò)對(duì)這些面試題的深入剖析則可進(jìn)一步增強(qiáng)自身的內(nèi)功。

              2.找錯(cuò)題

              試題1:

            void test1()
            {
             char string[10];
             char* str1 = "0123456789";
             strcpy( string, str1 );
            }
              試題2:

            void test2()
            {
             char string[10], str1[10];
             int i;
             for(i=0; i<10; i++)
             {
              str1 = 'a';
             }
             strcpy( string, str1 );
            }
              試題3:

            void test3(char* str1)
            {
             char string[10];
             if( strlen( str1 ) <= 10 )
             {
              strcpy( string, str1 );
             }
            }
              解答:

              試題1字符串str1需要11個(gè)字節(jié)才能存放下(包括末尾的’\0’),而string只有10個(gè)字節(jié)的空間,strcpy會(huì)導(dǎo)致數(shù)組越界;

            對(duì)試題2,如果面試者指出字符數(shù)組str1不能在數(shù)組內(nèi)結(jié)束可以給3分;如果面試者指出strcpy(string, str1)調(diào)用使得從str1內(nèi)存起復(fù)制到string內(nèi)存起所復(fù)制的字節(jié)數(shù)具有不確定性可以給7分,在此基礎(chǔ)上指出庫(kù)函數(shù)strcpy工作方式的給10 分;

              對(duì)試題3,if(strlen(str1) <= 10)應(yīng)改為if(strlen(str1) < 10),因?yàn)閟trlen的結(jié)果未統(tǒng)計(jì)’\0’所占用的1個(gè)字節(jié)。

              剖析:

              考查對(duì)基本功的掌握:

              (1)字符串以’\0’結(jié)尾;

              (2)對(duì)數(shù)組越界把握的敏感度;

              (3)庫(kù)函數(shù)strcpy的工作方式,如果編寫(xiě)一個(gè)標(biāo)準(zhǔn)strcpy函數(shù)的總分值為10,下面給出幾個(gè)不同得分的答案:

              2分

            void strcpy( char *strDest, char *strSrc )
            {
              while( (*strDest++ = * strSrc++) != ‘\0’ );
            }
              4分

            void strcpy( char *strDest, const char *strSrc )
            //將源字符串加const,表明其為輸入?yún)?shù),加2分
            {
              while( (*strDest++ = * strSrc++) != ‘\0’ );
            }
              7分

            void strcpy(char *strDest, const char *strSrc)
            {
             //對(duì)源地址和目的地址加非0斷言,加3分
             assert( (strDest != NULL) && (strSrc != NULL) );
             while( (*strDest++ = * strSrc++) != ‘\0’ );
            }
              10分

            //為了實(shí)現(xiàn)鏈?zhǔn)讲僮鳎瑢⒛康牡刂贩祷兀?分!

            char * strcpy( char *strDest, const char *strSrc )
            {
             assert( (strDest != NULL) && (strSrc != NULL) );
             char *address = strDest;
             while( (*strDest++ = * strSrc++) != ‘\0’ );
              return address;
            }
              從2分到10分的幾個(gè)答案我們可以清楚的看到,小小的strcpy竟然暗藏著這么多玄機(jī),真不是蓋的!需要多么扎實(shí)的基本功才能寫(xiě)一個(gè)完美的strcpy啊!

              (4)對(duì)strlen的掌握,它沒(méi)有包括字符串末尾的'\0'。

              讀者看了不同分值的strcpy版本,應(yīng)該也可以寫(xiě)出一個(gè)10分的strlen函數(shù)了,完美的版本為: int strlen( const char *str ) //輸入?yún)?shù)const

            {
             assert( strt != NULL ); //斷言字符串地址非0
             int len;
             while( (*str++) != '\0' )
             {
              len++;
             }
             return len;
            }
              試題4:

            void GetMemory( char *p )
            {
             p = (char *) malloc( 100 );
            }

            void Test( void )
            {
             char *str = NULL;
             GetMemory( str );
             strcpy( str, "hello world" );
             printf( str );
            }
              試題5:

            char *GetMemory( void )
            {
             char p[] = "hello world";
             return p;
            }

            void Test( void )
            {
             char *str = NULL;
             str = GetMemory();
             printf( str );
            }
              試題6:

            void GetMemory( char **p, int num )
            {
             *p = (char *) malloc( num );
            }

            void Test( void )
            {
             char *str = NULL;
             GetMemory( &str, 100 );
             strcpy( str, "hello" );
             printf( str );
            }
              試題7:

            void Test( void )
            {
             char *str = (char *) malloc( 100 );
             strcpy( str, "hello" );
             free( str );
             ... //省略的其它語(yǔ)句
            }
              解答:

              試題4傳入中GetMemory( char *p )函數(shù)的形參為字符串指針,在函數(shù)內(nèi)部修改形參并不能真正的改變傳入形參的值,執(zhí)行完

            char *str = NULL;
            GetMemory( str );
              后的str仍然為NULL;

              試題5中

            char p[] = "hello world";
            return p;
              的p[]數(shù)組為函數(shù)內(nèi)的局部自動(dòng)變量,在函數(shù)返回后,內(nèi)存已經(jīng)被釋放。這是許多程序員常犯的錯(cuò)誤,其根源在于不理解變量的生存期。

              試題6的GetMemory避免了試題4的問(wèn)題,傳入GetMemory的參數(shù)為字符串指針的指針,但是在GetMemory中執(zhí)行申請(qǐng)內(nèi)存及賦值語(yǔ)句
            tiffany bracelets

            *p = (char *) malloc( num );
              后未判斷內(nèi)存是否申請(qǐng)成功,應(yīng)加上:

            if ( *p == NULL )
            {
             ...//進(jìn)行申請(qǐng)內(nèi)存失敗處理
            }
              試題7存在與試題6同樣的問(wèn)題,在執(zhí)行

            char *str = (char *) malloc(100);
              后未進(jìn)行內(nèi)存是否申請(qǐng)成功的判斷;另外,在free(str)后未置str為空,導(dǎo)致可能變成一個(gè)“野”指針,應(yīng)加上:

            str = NULL;
              試題6的Test函數(shù)中也未對(duì)malloc的內(nèi)存進(jìn)行釋放。

              剖析:

              試題4~7考查面試者對(duì)內(nèi)存操作的理解程度,基本功扎實(shí)的面試者一般都能正確的回答其中50~60的錯(cuò)誤。但是要完全解答正確,卻也絕非易事。

              對(duì)內(nèi)存操作的考查主要集中在:

              (1)指針的理解;

              (2)變量的生存期及作用范圍;

              (3)良好的動(dòng)態(tài)內(nèi)存申請(qǐng)和釋放習(xí)慣。

              再看看下面的一段程序有什么錯(cuò)誤:

            swap( int* p1,int* p2 )
            {
             int *p;
             *p = *p1;
             *p1 = *p2;
             *p2 = *p;
            }
              在swap函數(shù)中,p是一個(gè)“野”指針,有可能指向系統(tǒng)區(qū),導(dǎo)致程序運(yùn)行的崩潰。在VC++中DEBUG運(yùn)行時(shí)提示錯(cuò)誤“Access Violation”。該程序應(yīng)該改為:

            swap( int* p1,int* p2 )
            {
             int p;
             p = *p1;
             *p1 = *p2;
             *p2 = p;
            }

            posted @ 2008-03-14 10:03 lovetiffany 閱讀(4841) | 評(píng)論 (16)編輯 收藏

            2008年1月11日

            C++ 心得

            我們現(xiàn)在編寫(xiě)一個(gè)程序越來(lái)越容易了。利用一些軟件開(kāi)發(fā)工具,往往只要通過(guò)鼠標(biāo)的拖拖點(diǎn)點(diǎn),計(jì)算機(jī)就會(huì)自動(dòng)幫你生成許多代碼。但在很多時(shí)候,計(jì)算機(jī)的這種能力被濫用了,我們往往只考慮把這個(gè)程序搭起來(lái),而不去考慮程序的性能如何,程序是否足夠的健壯。而此節(jié)課的目的主要是介紹一些編碼的經(jīng)驗(yàn),讓大家編寫(xiě)的程序更加健壯和高性能。


              在C++編程中應(yīng)該盡量使用const和inline來(lái)代替#define,盡量做到能不用#define就不用。#define常見(jiàn)的用途有“定義常量”以及“定義宏”,但其中存在諸多的弊病。

              第一,查錯(cuò)不直觀,不利于調(diào)試。Define的定義是由預(yù)處理程序處理的,作的是完全的文本替換,不做任何的類型檢查。在編譯器處理階段,define定義的東西已經(jīng)被完全替換了,這樣在debug的時(shí)候就看不到任何的相關(guān)信息,即跟蹤時(shí)不能step into宏。例如,把ASPECT_RATIO用define定義成1.653,編譯器就看不到ASPECT_RATIO這個(gè)名字了。如果編譯器報(bào)1.653錯(cuò),那么就無(wú)從知道此1.653來(lái)自于何處。在真正編碼的時(shí)候應(yīng)該使用如下的語(yǔ)句來(lái)定義:

            static const double ASPECT_RATIO = 1.653;


              第二,沒(méi)有任何類型信息,不是type safe。因?yàn)樗俏谋炯?jí)別的替換,這樣不利于程序的維護(hù)。

              第三,define的使用很容易造成污染。比如,如果有兩個(gè)頭文件都定義了ASPECT_RATIO, 而一個(gè)CPP文件又同時(shí)包含了這兩個(gè)頭文件,那么就會(huì)造成沖突。更難查的是另外一種錯(cuò)誤,比如有如下的代碼:

              // in header file def.h
              #define Apple 1
              #define Orange 2
                #define Pineapple 3
               …
              // in some cpp file that includes the def.h
              enum Colors {White, Black, Purple, Orange};


              在.h文件中Orange被定義成水果的一種,而在.cpp文件中Orange又成為了一種顏色,那么編譯器就會(huì)把此處的Orange替換成2,編譯可能仍然可以通過(guò),程序也能夠運(yùn)行,但是這就成了一個(gè)bug,表現(xiàn)出古怪的錯(cuò)誤,且很難查錯(cuò)。再比如定義了一個(gè)求a與b哪個(gè)數(shù)大的宏,#define max(a,b) ((a) > (b) ? (a) : (b))

              int a = 5, b = 0;
              max(++ a, b);
              max(++ a, b + 10);

            190-823 117-202 
              在上面的操作中,max(++ a, b); 語(yǔ)句中a被++了兩次,而max(++ a, b + 10); 語(yǔ)句中a只加了一次,這樣在程序處理中就很有可能成為一個(gè)bug,且此bug也非常的難找。在實(shí)際編碼時(shí)可以使用如下的語(yǔ)句來(lái)做:

              template<class T>
              inline const T&
              max(const T& a, const T& b) { return a > b ? a : b; }


              2、Prefer C++-style casts

              在程序中經(jīng)常會(huì)需要把一種類型轉(zhuǎn)換成另外一種類型,在C++中應(yīng)該使用static_cast、const_cast、dynamic_cast、reinterpret_cast關(guān)鍵字來(lái)做類型轉(zhuǎn)換。因?yàn)檫@有以下好處,一是其本身就是一種注釋,在代碼中看到上面這些關(guān)鍵字就可馬上知道此處是進(jìn)行類型轉(zhuǎn)換。二是C語(yǔ)言中類型轉(zhuǎn)換通常是很難進(jìn)行搜索的,而通過(guò)關(guān)鍵字cast則可以很容易的找到程序中出現(xiàn)類型轉(zhuǎn)換的地方了。

              3、Distinguish between prefix and postfix forms of increment and decrement operators

              通常對(duì)于操作系統(tǒng)或編譯器自身支持的類型,prefix(前綴,如++i)與postfix(后綴,如i++)的效果是一樣的。因?yàn)楝F(xiàn)在的編譯器都很聰明,它會(huì)自動(dòng)做優(yōu)化,這兩者的匯編代碼是一樣的,性能不會(huì)有差別。但有時(shí)候也會(huì)有不同的,如一些重載了操作符的類型。下面是模擬prefix與postfix的操作過(guò)程,可以發(fā)現(xiàn)在postfix操作中會(huì)生成一個(gè)臨時(shí)變量,而這一臨時(shí)變量是會(huì)占用額外的時(shí)間和開(kāi)銷的。

              // prefix form: increment and fetch
              UPInt& UPInt::operator++()
               {
                *this += 1; // increment
               return *this; // fetch
               }
              // postfix form: fetch and increment
               const UPInt UPInt::operator++(int)
               {
                UPInt oldValue = *this; // fetch
               ++(*this); // increment
                return oldValue; // return what was fetched
               }


              一般情況下不需要區(qū)分是先++,還是后++,但是我們?cè)诰帉?xiě)程序的時(shí)候最好能習(xí)慣性的將其寫(xiě)成++i的形式,如在使用STL中的iterator時(shí),prefix與postfix會(huì)有相當(dāng)大的性能差異。請(qǐng)不要小看這些細(xì)節(jié),實(shí)際在編寫(xiě)程序的時(shí)候,若不注意具體細(xì)節(jié),你會(huì)發(fā)現(xiàn)程序的性能會(huì)非常的低。但要注意,雖然在大多數(shù)情況下可以用prefix來(lái)代替postfix,但有一種情況例外,那就是有[]操作符時(shí),比如gzArray [++index] 是不等于 gzArray[index++]的。

            4、Minimizing Compile-time Dependencies

              有些人在編寫(xiě)程序時(shí),往往喜歡將一個(gè).h文件包含到另一個(gè).h文件,而實(shí)踐證明在做大型軟件時(shí)這是一個(gè)非常不好的習(xí)慣,因這樣會(huì)造成很多依賴的問(wèn)題,包含較多的.h文件,別人又使用了這個(gè)class,而在他的那個(gè)工程中可能并不存在這些.h文件,這樣很可能就編譯不能通過(guò)。而且這樣做,還可能造成很難去更新一個(gè)模塊的情況。因?yàn)橐粋€(gè).h文件被很多模塊包含的話,如果修改了此.h文件,在編譯系統(tǒng)的時(shí)候,編譯器會(huì)去尋找哪些模塊依賴于某個(gè)被修改過(guò)的.h文件,那么就導(dǎo)致了所有包含入此.h文件的模塊全都要進(jìn)行重新編譯。在項(xiàng)目比較小的時(shí)候,大家可能還感覺(jué)不到差別,但是如果說(shuō)是在大型的軟件系統(tǒng)里,你可能編譯一遍源碼需要七、八個(gè)小時(shí)。如果你這個(gè).h文件被很多模塊包含的話,就算在.h文件中加了一行注釋,在編譯時(shí)編譯器檢查哪些文件被改動(dòng),那么所有包含入此.h文件的模塊都會(huì)被重新編譯,造成巨大的時(shí)間和精力負(fù)擔(dān)。對(duì)于此問(wèn)題,解決的方法就是讓.h文件自包含,也就是說(shuō)讓它包含盡量少的東西。所謂盡量少是指如刪掉任何一個(gè)它包含進(jìn)來(lái)的.h文件,都將無(wú)法正常進(jìn)行工作。其實(shí)在很多情況下,并不需要一個(gè).h文件去包含另一個(gè).h文件,完全可以通過(guò)class聲明來(lái)解決依賴關(guān)系的這種問(wèn)題。再來(lái)看下面這個(gè)例子:1Z0-043

              #include "a.h" // class A
              #include "b.h" // class B
              #include "c.h" // class C
              #include "d.h" // class D
              #include "e.h" // class E
              class X : public A, private B
              {
               public:
              E SomeFunctionCall(E someParameter);
               private:
               D m_dInstance;
              };


              當(dāng)類X從類A和類B中派生時(shí),需要知道X在內(nèi)存中都有哪些data,通常在內(nèi)存中前面是基類的data,后面緊跟的是此派生類自身定義的data,因此就必須知道類A與類B的內(nèi)部細(xì)節(jié),要不然編譯器就無(wú)法來(lái)安排內(nèi)存了。但是在處理參數(shù)以及參數(shù)返回值的時(shí)候,實(shí)際上并不需要知道這些信息,在此處定義的SomeFunctionCall()只需知道E是個(gè)class就足夠了,并不需要知道類E中的data如長(zhǎng)度等的具體細(xì)節(jié)。上面的代碼應(yīng)該改寫(xiě)成如下的形式,以減少依賴關(guān)系:

              #include "a.h" // class A
              #include "b.h" // class B
              #include "c.h" // class C
              #include "d.h" // class D
              class E;
              class X : public A, private B
              {
               public:
              E SomeFunctionCall(E someParameter);
               private:
              D m_dInstance;
              };


              5、Never treat arrays polymorphically

              不要把數(shù)組和多態(tài)一起使用,請(qǐng)看下面的例子。

              class BST { ... };
              class BalancedBST: public BST { ... };
              void printBSTArray(ostream& s, const BST array[], int numElements)
              {
              for (int i = 0; i < numElements; ++i)
              {
               s << array[i];
              // this assumes an operator<< is defined for BST
              }
              }

              BalancedBST bBSTArray[10];
              printBSTArray(cout, bBSTArray, 10);


              數(shù)組在內(nèi)存中是一個(gè)連續(xù)的內(nèi)存空間,而在數(shù)組中應(yīng)該如何來(lái)定位一個(gè)元素呢?過(guò)程是這樣的,編譯器可以知道每個(gè)數(shù)據(jù)類型的長(zhǎng)度大小,如果數(shù)組的index是0,則會(huì)自動(dòng)去取第一個(gè)元素;如果是指定了某個(gè)index,編譯器則會(huì)根據(jù)此index與該數(shù)據(jù)類型的長(zhǎng)度自動(dòng)去算出該元素的位置。

              在printBSTArray()函數(shù)中,盡管傳入的參數(shù)是BalancedBST類型,但由于其本來(lái)定義的類型是BST,那么它依然會(huì)根據(jù)BST來(lái)計(jì)算類型的長(zhǎng)度。而通常派生類實(shí)例所占的內(nèi)存要比基類實(shí)例所占的內(nèi)存大一些,因此該程序在編譯時(shí)會(huì)報(bào)錯(cuò)。請(qǐng)記住,永遠(yuǎn)不要把數(shù)組和C++的多態(tài)性放在一起使用。

              6、Prevent exceptions from leaving destructors

              析構(gòu)函數(shù)中一定不要拋出異常。通常有兩種情況會(huì)導(dǎo)致析構(gòu)函數(shù)的調(diào)用,一種是當(dāng)該類的對(duì)象離開(kāi)了它的域,或delete表達(dá)式中一個(gè)該類對(duì)象的指針,另一種是由于異常而引起析構(gòu)函數(shù)的調(diào)用。

              如果析構(gòu)函數(shù)被調(diào)用是由于exception引起,而此時(shí)在析構(gòu)函數(shù)中又拋出了異常,程序會(huì)立即被系統(tǒng)終止,甚至都來(lái)不及進(jìn)行內(nèi)存釋放。因此如果在析構(gòu)函數(shù)中拋出異常的話,就很容易混淆引起異常的原因,且這樣的軟件也會(huì)讓用戶非常惱火。由于析構(gòu)函數(shù)中很可能會(huì)調(diào)用其它的一些函數(shù),所以在寫(xiě)析構(gòu)函數(shù)的時(shí)候一定要注意,對(duì)這些函數(shù)是否會(huì)拋出異常要非常清楚,如果會(huì)的話,就一定要小心了。比如下面這段代碼:

              Session::~Session()
              {
              logDestruction(this);
              }


              比如logDestruction()函數(shù)可能會(huì)拋出異常,那么我們就應(yīng)該采用下面這種代碼的形式:

              Session::~Session()
              {
               try
              {
               logDestruction(this);
               }
               catch (...)
              {
               }
             }


              這樣程序出錯(cuò)的時(shí)候不會(huì)被立即關(guān)掉,可以給用戶一些其它的選擇,至少先讓他把目前在做的工作保存下來(lái)。

              7、Optimization:Remember the 80-20 rule

              在軟件界有一個(gè)20-80法則,其實(shí)這是一個(gè)很有趣的現(xiàn)象,比如一個(gè)程序中20%的代碼使用了該程序所占資源的80%;一個(gè)程序中20%的代碼占用了總運(yùn)行時(shí)間的80%;一個(gè)程序中20%的代碼使用了該程序所占內(nèi)存的80%;在20%的代碼上面需要花費(fèi)80%的維護(hù)力量,等等。這個(gè)規(guī)律還可以被繼續(xù)推廣下去,不過(guò)這個(gè)規(guī)律無(wú)法被證明,它是人們?cè)趯?shí)踐中觀察得出的結(jié)果。從這個(gè)規(guī)律出發(fā),我們?cè)谧龀绦騼?yōu)化的時(shí)候,就有了針對(duì)性。比如想提高代碼的運(yùn)行速度,根據(jù)這個(gè)規(guī)律可以知道其中20%的代碼占用了80%的運(yùn)行時(shí)間,因此我們只要找到這20%的代碼,并進(jìn)行相應(yīng)的優(yōu)化,那么我們程序的運(yùn)行速度就可以有較大的提高。再如有一個(gè)函數(shù),占用了程序80%的運(yùn)行時(shí)間,如果把這個(gè)函數(shù)的執(zhí)行速度提高10倍,那么對(duì)程序整體性能的提高,影響是非常巨大的。如果有一個(gè)函數(shù)運(yùn)行時(shí)間只占總時(shí)間的1%,那就算把這個(gè)函數(shù)的運(yùn)行速度提高1000倍,對(duì)程序整體性能的提高也是影響不大的。所以我們的基本思想就是找到占用運(yùn)行時(shí)間最大的那個(gè)函數(shù),然后去優(yōu)化它,哪怕只是改進(jìn)了一點(diǎn)點(diǎn),程序的整體性能也可以被提高很多。

              要想找出那20%的代碼,我們的方法就是使用Profiler,它實(shí)際上是一些公司所開(kāi)發(fā)的工具,可以檢查程序中各個(gè)模塊所分配內(nèi)存的使用情況,以及每個(gè)函數(shù)所運(yùn)行的時(shí)間等。常見(jiàn)的Profiler有Intel公司開(kāi)發(fā)的VTune,微軟公司開(kāi)發(fā)的Visual Studio profiler,DevPartner from Compuware等

            posted @ 2008-01-11 16:05 lovetiffany 閱讀(1161) | 評(píng)論 (8)編輯 收藏

            C++類庫(kù)設(shè)計(jì)的基本思想和方法

             首先說(shuō)指導(dǎo)思想。這是一個(gè)價(jià)值觀問(wèn)題,我們?cè)诖颂岢鋈龡l標(biāo)準(zhǔn):簡(jiǎn)單,高性能,可移植。

             我們?cè)陂_(kāi)篇就對(duì)簡(jiǎn)單性目標(biāo)作了敘述,這里再稍微展開(kāi)討論一下。我們提出的簡(jiǎn)單標(biāo)準(zhǔn),首先是外部接口簡(jiǎn)單,其次是內(nèi)部結(jié)構(gòu)簡(jiǎn)單。我們知道,類庫(kù)是提供給上層應(yīng)用程序使用的,也就是按照一定的接口規(guī)范,向上層提供一定的功能服務(wù)。接口設(shè)計(jì)得越簡(jiǎn)單,對(duì)上層用戶來(lái)說(shuō)就越方便,就越不容易產(chǎn)生Bug。我們可以注意到,流行的成功類庫(kù)都是擁有簡(jiǎn)單接口的。為了使接口簡(jiǎn)單,常常不得不把有關(guān)具體實(shí)現(xiàn)的復(fù)雜性封裝于類庫(kù)內(nèi)部,也就是說(shuō),關(guān)于簡(jiǎn)單性的設(shè)計(jì)原則,外部接口簡(jiǎn)單優(yōu)先于內(nèi)部實(shí)現(xiàn)簡(jiǎn)單。

             

            高性能是C++語(yǔ)言優(yōu)于其它OO語(yǔ)言的一個(gè)特性。C++的高性能應(yīng)該首先歸于它運(yùn)行模式,和大多數(shù)OO語(yǔ)言不同,C++程序編譯后直接產(chǎn)生本地平臺(tái)代碼(Native Code),理論上具備了可能的最大執(zhí)行性能。另外的一個(gè)原因是主流的C++編譯器都被設(shè)計(jì)得非常精巧,具有優(yōu)越的代碼優(yōu)化能力。對(duì)于C++類庫(kù)設(shè)計(jì)者來(lái)說(shuō),保持C++的高性能是一個(gè)重要目標(biāo)。程序的高性能可以從兩方面來(lái)評(píng)價(jià),一是時(shí)間性能,以盡量短的時(shí)間來(lái)解決盡量多的業(yè)務(wù);二是資源性能,以盡量少的資源消耗,包括CPU使用、內(nèi)存占用、網(wǎng)絡(luò)流量、磁盤空間等等,來(lái)維持正常的程序功能。提高性能的主要手段是數(shù)據(jù)結(jié)構(gòu)、算法和程序體現(xiàn)結(jié)構(gòu)的優(yōu)化設(shè)計(jì)000-861 117-102 。

             

            再說(shuō)可移植性。C++的編譯后輸出代碼是本地平臺(tái)代碼,因此C++本身不具有目標(biāo)代碼可移植性,C++的可移植性只能是源代碼可移植性。源代碼的可移植性是指,同一軟件產(chǎn)品的全部或者部分源代碼可以在不同的編譯環(huán)境中進(jìn)行編譯(不需要編譯的除外),并且其結(jié)果具有相同的品質(zhì)特性(依優(yōu)先順序包括功能性、可靠性、可用性、性能性、可維護(hù)性等)。編譯環(huán)境可以大致分為三個(gè)層次,最底層的是操作系統(tǒng),也就是平臺(tái)(Platform),其次是對(duì)源代碼直接進(jìn)行處理的編譯器,然后是其它在編譯過(guò)程中必需的中間件物品,如庫(kù)文件等。我們知道C++雖然在語(yǔ)言規(guī)范上獲得了統(tǒng)一(ISO/IEC),其編譯器卻是群雄割據(jù)的局面,具有代表性的有Borland C++系列(已經(jīng)淡出市場(chǎng)),MicrosoftVisual Studio系列的C++編譯器和GNU陣營(yíng)的壓軸產(chǎn)品gc中的g++。源代碼經(jīng)編譯環(huán)境處理后產(chǎn)生的可執(zhí)行代碼的執(zhí)行平臺(tái)稱為目標(biāo)平臺(tái),不同的編譯器的目標(biāo)平臺(tái)也不同,有的支持多平臺(tái),如g++,有的是單一平臺(tái),如Visual C++。對(duì)于類庫(kù)設(shè)計(jì)者來(lái)說(shuō),想要獲得完全的可移植性是非常困難的(除非是象STL這樣被納入語(yǔ)言規(guī)范的類庫(kù),因?yàn)椴恢С?/span>STL就是不支持標(biāo)準(zhǔn)的C++。即便如此不同的編譯環(huán)境還是存在不同的STL實(shí)現(xiàn)版本,造成“一個(gè)類庫(kù)多個(gè)實(shí)現(xiàn)”的局面),我們只能有選擇地支持一部分環(huán)境。我們?cè)陂_(kāi)篇就已經(jīng)說(shuō)明,我們選擇g++Visual C++編譯器,選擇LinuxWindows 32位目標(biāo)平臺(tái)。

             

            接下來(lái)我們來(lái)討論C++類庫(kù)設(shè)計(jì)的方法論。

             

            首先,我們采用僅用頭文件的類庫(kù)設(shè)計(jì)方式(Header-onlySTL的大多數(shù)實(shí)現(xiàn)版本都是采用Header-only的方式),也就是在頭文件(.h)中聲明和定義類,將其成員函數(shù)全部定義為內(nèi)聯(lián)函數(shù),而不使用源程序文件(.cpp)。

             

            我們知道在C語(yǔ)言的開(kāi)發(fā)環(huán)境中,所謂庫(kù)文件包含兩個(gè)部分,頭文件部分和二進(jìn)制文件部分。根據(jù)二進(jìn)制文件和用戶目標(biāo)文件結(jié)合方式的不同,又可分為靜態(tài)鏈接文件和動(dòng)態(tài)鏈接文件。這種庫(kù)的構(gòu)成模式已成為事實(shí)上的C語(yǔ)言開(kāi)發(fā)環(huán)境的標(biāo)準(zhǔn),絕大多數(shù)平臺(tái)、絕大多數(shù)編譯器都使用這種模式 117-301 190-721 。

             

            然而C++語(yǔ)言開(kāi)發(fā)環(huán)境,這種庫(kù)構(gòu)成模式遭遇到一個(gè)重大問(wèn)題,就是符號(hào)命名問(wèn)題。舉例來(lái)說(shuō),C++允許多個(gè)函數(shù)可以被重載(Overload),可以具有相同的名稱,而通過(guò)參數(shù)列表不同被予以區(qū)別。這樣就帶來(lái)一個(gè)問(wèn)題,編譯完成的目標(biāo)代碼中怎樣來(lái)區(qū)別這些在源代碼中具有相同名稱的函數(shù)?常見(jiàn)的做法是在編譯器輸出的函數(shù)的符號(hào)名稱中加入描述類型信息的字符串,這種方法通常被稱為名稱裝飾(Name decoration)或者名稱糟化(Name mangling,這個(gè)術(shù)語(yǔ)真不好翻譯,筆者的感覺(jué)是發(fā)明這個(gè)詞的人覺(jué)得編譯器把本來(lái)簡(jiǎn)單干凈的符號(hào)給搞亂了)。比如說(shuō),g++3.4.4對(duì)于函數(shù)void func(int),其編譯輸出符號(hào)名稱為_Z1funci,對(duì)于函數(shù)void func(int, int),其輸出符號(hào)名稱為_Z1funcii,等等。但是,這種名稱裝飾規(guī)則沒(méi)有統(tǒng)一規(guī)范,也就是說(shuō)不同的編譯器有各自不同的名稱裝飾規(guī)則,這樣就導(dǎo)致不同的C++編譯器只能識(shí)別自己的輸出文件,而沒(méi)有辦法處理其他編譯器的輸出文件。因此,如果將C++程序制作成二進(jìn)制的庫(kù)文件,則其能夠支持的開(kāi)發(fā)環(huán)境只能限于原始的開(kāi)發(fā)環(huán)境,基本上不具有多種開(kāi)發(fā)環(huán)境間的通用性。

             

            一個(gè)解決辦法是將庫(kù)文件保持在源代碼形態(tài)(包括頭文件和源文件),而不編譯成二進(jìn)制文件。比如STL的許多實(shí)現(xiàn)版本都是以頭文件形式存在。這樣雖然解決了名稱裝飾所帶來(lái)的不可移植問(wèn)題,但同時(shí)又會(huì)帶來(lái)代碼編譯時(shí)間增長(zhǎng),源代碼完全公開(kāi)等問(wèn)題。在C++的名稱裝飾規(guī)則未被統(tǒng)一之前,看起來(lái)這個(gè)問(wèn)題是很難兩全其美地解決了。

             

            在本系列中,我們也仿照g++STL實(shí)現(xiàn)方式,完全以頭文件形式來(lái)編寫(xiě)類庫(kù)。為什么不把代碼放到源文件中去呢?主要原因是,頭文件只需要用戶使用包含指令(#include)就可以處理了,而源文件則需要配置到用戶工程的編譯目標(biāo)列表中,和用戶的源程序形成共同編譯的形式,破壞了用戶工程的編譯目標(biāo)的封閉性,比較麻煩而且不符合軟件開(kāi)發(fā)的一般習(xí)慣。

             

            其次我們來(lái)討論如何支持多平臺(tái)。我們已經(jīng)說(shuō)過(guò)在本系列中我們的線程庫(kù)支持Linux平臺(tái)的Posix線程和Windows 32位平臺(tái)的線程模式。我們可以參考C++Pimpl“慣語(yǔ)”(Pimpl idiom,在Herb Sutter的《Exceptional C++》中有介紹),采用2層類構(gòu)造方式。上次類亦即接口類,為用戶提供統(tǒng)一的類接口,在用戶看來(lái)具有唯一的類行為定義;下層類亦即實(shí)現(xiàn)類,將接口類的行為定義轉(zhuǎn)化為某個(gè)平臺(tái)的具體實(shí)現(xiàn)。

            posted @ 2008-01-11 15:53 lovetiffany 閱讀(4615) | 評(píng)論 (29)編輯 收藏

            2008年1月3日

            C++中extern “C”含義深層探索

            1.引言

              C++語(yǔ)言的創(chuàng)建初衷是“a better C”,但是這并不意味著C++中類似C語(yǔ)言的全局變量和函數(shù)所采用的編譯和連接方式與C語(yǔ)言完全相同。作為一種欲與C兼容的語(yǔ)言,C++保留了一部分過(guò)程式語(yǔ)言的特點(diǎn)(被世人稱為“不徹底地面向?qū)ο?#8221;),因而它可以定義不屬于任何類的全局變量和函數(shù)。但是,C++畢竟是一種面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言,為了支持函數(shù)的重載,C++對(duì)全局函數(shù)的處理方式與C有明顯的不同。

               2.從標(biāo)準(zhǔn)頭文件說(shuō)起

              某企業(yè)曾經(jīng)給出如下的一道面試題:

              面試題
              為什么標(biāo)準(zhǔn)頭文件都有類似以下的結(jié)構(gòu)?

            #ifndef __INCvxWorksh
            #define __INCvxWorksh
            #ifdef __cplusplus
            extern "C" {
            #endif
            /*...*/
            #ifdef __cplusplus
            }
            #endif
            #endif /* __INCvxWorksh */


              分析
              顯然,頭文件中的編譯宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、#endif” 的作用是防止該頭文件被重復(fù)引用。

              那么

            #ifdef __cplusplus
            extern "C" {
            #endif
            #ifdef __cplusplus
            }
            #endif


              的作用又是什么呢?我們將在下文一一道來(lái)。
               3.深層揭密extern "C"

              extern "C" 包含雙重含義,從字面上即可得到:首先,被它修飾的目標(biāo)是“extern”的;其次,被它修飾的目標(biāo)是“C”的。讓我們來(lái)詳細(xì)解讀這兩重含義。

              被extern "C"限定的函數(shù)或變量是extern類型的;

              extern是C/C++語(yǔ)言中表明函數(shù)和全局變量作用范圍(可見(jiàn)性)的關(guān)鍵字,該關(guān)鍵字告訴編譯器,其聲明的函數(shù)和變量可以在本模塊或其它模塊中使用。記住,下列語(yǔ)句:

              extern int a;


              僅僅是一個(gè)變量的聲明,其并不是在定義變量a,并未為a分配內(nèi)存空間。變量a在所有模塊中作為一種全局變量只能被定義一次,否則會(huì)出現(xiàn)連接錯(cuò)誤。

              通常,在模塊的頭文件中對(duì)本模塊提供給其它模塊引用的函數(shù)和全局變量以關(guān)鍵字extern聲明。例如,如果模塊B欲引用該模塊A中定義的全局變量和函數(shù)時(shí)只需包含模塊A的頭文件即可。這樣,模塊B中調(diào)用模塊A中的函數(shù)時(shí),在編譯階段,模塊B雖然找不到該函數(shù),但是并不會(huì)報(bào)錯(cuò);它會(huì)在連接階段中從模塊A編譯生成的目標(biāo)代碼中找到此函數(shù)。

              與extern對(duì)應(yīng)的關(guān)鍵字是static,被它修飾的全局變量和函數(shù)只能在本模塊中使用。因此,一個(gè)函數(shù)或變量只可能被本模塊使用時(shí),其不可能被extern “C”修飾。

              被extern "C"修飾的變量和函數(shù)是按照C語(yǔ)言方式編譯和連接的;

               未加extern “C”聲明時(shí)的編譯方式

              首先看看C++中對(duì)類似C的函數(shù)是怎樣編譯的。

              作為一種面向?qū)ο蟮恼Z(yǔ)言,C++支持函數(shù)重載,而過(guò)程式語(yǔ)言C則不支持。函數(shù)被C++編譯后在符號(hào)庫(kù)中的名字與C語(yǔ)言的不同。例如,假設(shè)某個(gè)函數(shù)的原型為:

            void foo( int x, int y );


              該函數(shù)被C編譯器編譯后在符號(hào)庫(kù)中的名字為_(kāi)foo,而C++編譯器則會(huì)產(chǎn)生像_foo_int_int之類的名字(不同的編譯器可能生成的名字不同,但是都采用了相同的機(jī)制,生成的新名字稱為“mangled name”)。

              _foo_int_int這樣的名字包含了函數(shù)名、函數(shù)參數(shù)數(shù)量及類型信息,C++就是靠這種機(jī)制來(lái)實(shí)現(xiàn)函數(shù)重載的。例如,在C++中,函數(shù)void foo( int x, int y )與void foo( int x, float y )編譯生成的符號(hào)是不相同的,后者為_(kāi)foo_int_float。
              同樣地,C++中的變量除支持局部變量外,還支持類成員變量和全局變量。用戶所編寫(xiě)程序的類成員變量可能與全局變量同名,我們以"."來(lái)區(qū)分。而本質(zhì)上,編譯器在進(jìn)行編譯時(shí),與函數(shù)的處理相似,也為類中的變量取了一個(gè)獨(dú)一無(wú)二的名字,這個(gè)名字與用戶程序中同名的全局變量名字不同。

               未加extern "C"聲明時(shí)的連接方式

              假設(shè)在C++中,模塊A的頭文件如下:

            // 模塊A頭文件 moduleA.h
            #ifndef MODULE_A_H
            #define MODULE_A_H
            int foo( int x, int y );
            #endif


              在模塊B中引用該函數(shù):

            // 模塊B實(shí)現(xiàn)文件 moduleB.cpp
            #include "moduleA.h"
            foo(2,3);


              實(shí)際上,在連接階段,連接器會(huì)從模塊A生成的目標(biāo)文件moduleA.obj中尋找_foo_int_int這樣的符號(hào)!

               加extern "C"聲明后的編譯和連接方式

              加extern "C"聲明后,模塊A的頭文件變?yōu)椋?br>

            // 模塊A頭文件 moduleA.h
            #ifndef MODULE_A_H
            #define MODULE_A_H
            extern "C" int foo( int x, int y );
            #endif


              在模塊B的實(shí)現(xiàn)文件中仍然調(diào)用foo( 2,3 ),其結(jié)果是:

              (1)模塊A編譯生成foo的目標(biāo)代碼時(shí),沒(méi)有對(duì)其名字進(jìn)行特殊處理,采用了C語(yǔ)言的方式;

              (2)連接器在為模塊B的目標(biāo)代碼尋找foo(2,3)調(diào)用時(shí),尋找的是未經(jīng)修改的符號(hào)名_foo。

              如果在模塊A中函數(shù)聲明了foo為extern "C"類型,而模塊B中包含的是extern int foo( int x, int y ) ,則模塊B找不到模塊A中的函數(shù);反之亦然。

              所以,可以用一句話概括extern “C”這個(gè)聲明的真實(shí)目的(任何語(yǔ)言中的任何語(yǔ)法特性的誕生都不是隨意而為的,來(lái)源于真實(shí)世界的需求驅(qū)動(dòng)。我們?cè)谒伎紗?wèn)題時(shí),不能只停留在這個(gè)語(yǔ)言是怎么做的,還要問(wèn)一問(wèn)它為什么要這么做,動(dòng)機(jī)是什么,這樣我們可以更深入地理解許多問(wèn)題):
              實(shí)現(xiàn)C++與C及其它語(yǔ)言的混合編程。
              明白了C++中extern "C"的設(shè)立動(dòng)機(jī),我們下面來(lái)具體分析extern "C"通常的使用技巧。
               4.extern "C"的慣用法

              (1)在C++中引用C語(yǔ)言中的函數(shù)和變量,在包含C語(yǔ)言頭文件(假設(shè)為cExample.h)時(shí),需進(jìn)行下列處理:

            extern "C"
            {
            #include "cExample.h"
            }


              而在C語(yǔ)言的頭文件中,對(duì)其外部函數(shù)只能指定為extern類型,C語(yǔ)言中不支持extern "C"聲明,在.c文件中包含了extern "C"時(shí)會(huì)出現(xiàn)編譯語(yǔ)法錯(cuò)誤。

              筆者編寫(xiě)的C++引用C函數(shù)例子工程中包含的三個(gè)文件的源代碼如下:

            /* c語(yǔ)言頭文件:cExample.h */
            #ifndef C_EXAMPLE_H
            #define C_EXAMPLE_H
            extern int add(int x,int y);
            #endif
            /* c語(yǔ)言實(shí)現(xiàn)文件:cExample.c */
            #include "cExample.h"
            int add( int x, int y )
            {
            return x + y;
            }
            // c++實(shí)現(xiàn)文件,調(diào)用add:cppFile.cpp
            extern "C"
            {
            #include "cExample.h"
            }
            int main(int argc, char* argv[])
            {
            add(2,3);
            return 0;
            }


              如果C++調(diào)用一個(gè)C語(yǔ)言編寫(xiě)的.DLL時(shí),當(dāng)包括.DLL的頭文件或聲明接口函數(shù)時(shí),應(yīng)加extern "C" { }。

              (2)在C中引用C++語(yǔ)言中的函數(shù)和變量時(shí),C++的頭文件需添加extern "C",但是在C語(yǔ)言中不能直接引用聲明了extern "C"的該頭文件,應(yīng)該僅將C文件中將C++中定義的extern "C"函數(shù)聲明為extern類型。
              筆者編寫(xiě)的C引用C++函數(shù)例子工程中包含的三個(gè)文件的源代碼如下:70-210 1Y0-327 

            //C++頭文件 cppExample.h
            #ifndef CPP_EXAMPLE_H
            #define CPP_EXAMPLE_H
            extern "C" int add( int x, int y );
            #endif
            //C++實(shí)現(xiàn)文件 cppExample.cpp
            #include "cppExample.h"
            int add( int x, int y )
            {
            return x + y;
            }
            /* C實(shí)現(xiàn)文件 cFile.c
            /* 這樣會(huì)編譯出錯(cuò):#include "cExample.h" */
            extern int add( int x, int y );
            int main( int argc, char* argv[] )
            {
            add( 2, 3 );
            return 0;
            }


              如果深入理解了第3節(jié)中所闡述的extern "C"在編譯和連接階段發(fā)揮的作用,就能真正理解本節(jié)所闡述的從C++引用C函數(shù)和C引用C++函數(shù)的慣用法。對(duì)第4節(jié)給出的示例代碼,需要特別留意各個(gè)細(xì)節(jié)。


            posted @ 2008-01-03 11:10 lovetiffany 閱讀(990) | 評(píng)論 (1)編輯 收藏

            給C++初學(xué)者的50個(gè)忠告

            1.把C++當(dāng)成一門新的語(yǔ)言學(xué)習(xí)(和C沒(méi)啥關(guān)系!真的。);
              2.看《Thinking In C++》,不要看《C++變成死相》;
              3.看《The C++ Programming Language》和《Inside The C++ Object Model》,不要因?yàn)樗麄兒茈y而我們自己是初學(xué)者所以就不看;
              4.不要被VC、BCB、BC、MC、TC等詞匯所迷惑——他們都是集成開(kāi)發(fā)環(huán)境,而我們要學(xué)的是一門語(yǔ)言;
              5.不要放過(guò)任何一個(gè)看上去很簡(jiǎn)單的小編程問(wèn)題——他們往往并不那么簡(jiǎn)單,或者可以引伸出很多知識(shí)點(diǎn);
              6.會(huì)用Visual C++,并不說(shuō)明你會(huì)C++;
              7.學(xué)class并不難,template、STL、generic programming也不過(guò)如此——難的是長(zhǎng)期堅(jiān)持實(shí)踐和不遺余力的博覽群書(shū);
              8.如果不是天才的話,想學(xué)編程就不要想玩游戲——你以為你做到了,其實(shí)你的C++水平并沒(méi)有和你通關(guān)的能力一起變高——其實(shí)可以時(shí)刻記住:學(xué)C++是為了編游戲的;
              9.看Visual C++的書(shū),是學(xué)不了C++語(yǔ)言的;
              10.浮躁的人容易說(shuō):XX語(yǔ)言不行了,應(yīng)該學(xué)YY;——是你自己不行了吧!?
              11.浮躁的人容易問(wèn):我到底該學(xué)什么;——?jiǎng)e問(wèn),學(xué)就對(duì)了;
              12.浮躁的人容易問(wèn):XX有錢途嗎;——建議你去搶銀行;
              13.浮躁的人容易說(shuō):我要中文版!我英文不行!——不行?學(xué)呀!
              14.浮躁的人容易問(wèn):XX和YY哪個(gè)好;——告訴你吧,都好——只要你學(xué)就行;
              15.浮躁的人分兩種:a)只觀望而不學(xué)的人;b)只學(xué)而不堅(jiān)持的人;
              16.把時(shí)髦的技術(shù)掛在嘴邊,還不如把過(guò)時(shí)的技術(shù)記在心里;
              17.C++不僅僅是支持面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言;
              18.學(xué)習(xí)編程最好的方法之一就是閱讀源代碼;
              19.在任何時(shí)刻都不要認(rèn)為自己手中的書(shū)已經(jīng)足夠了;
              20.請(qǐng)閱讀《The Standard C++ Bible》(中文版:標(biāo)準(zhǔn)C++寶典),掌握C++標(biāo)準(zhǔn);
              21.看得懂的書(shū),請(qǐng)仔細(xì)看;看不懂的書(shū),請(qǐng)硬著頭皮看;
              22.別指望看第一遍書(shū)就能記住和掌握什么——請(qǐng)看第二遍、第三遍;
              23.請(qǐng)看《Effective C++》和《More Effective C++》以及《Exceptional C++》;
              24.不要停留在集成開(kāi)發(fā)環(huán)境的搖籃上,要學(xué)會(huì)控制集成開(kāi)發(fā)環(huán)境,還要學(xué)會(huì)用命令行方式處理程序;
              25.和別人一起討論有意義的C++知識(shí)點(diǎn),而不是爭(zhēng)吵XX行不行或者YY與ZZ哪個(gè)好;
              26.請(qǐng)看《程序設(shè)計(jì)實(shí)踐》,并嚴(yán)格的按照其要求去做;
              27.不要因?yàn)镃和C++中有一些語(yǔ)法和關(guān)鍵字看上去相同,就認(rèn)為它們的意義和作用完全一樣;
              28.C++絕不是所謂的C的“擴(kuò)充”——如果C++一開(kāi)始就起名叫Z語(yǔ)言,你一定不會(huì)把C和Z語(yǔ)言聯(lián)系得那么緊密 117-102 117-301
              29.請(qǐng)不要認(rèn)為學(xué)過(guò)XX語(yǔ)言再改學(xué)C++會(huì)有什么問(wèn)題——你只不過(guò)又在學(xué)一門全新的語(yǔ)言而已;
              30.讀完了《Inside The C++ Object Model》以后再來(lái)認(rèn)定自己是不是已經(jīng)學(xué)會(huì)了C++;
              31.學(xué)習(xí)編程的秘訣是:編程,編程,再編程;
              32.請(qǐng)留意下列書(shū)籍:《C++面向?qū)ο蟾咝Ь幊蹋–++ Effective Object-Oriented Software Construction)》《面向?qū)ο筌浖?gòu)造(Object-Oriented Software Construction)》《設(shè)計(jì)模式(Design Patterns)》《The Art of Computer Programming》;
              33.記住:面向?qū)ο蠹夹g(shù)不只是C++專有的;
              34.請(qǐng)把書(shū)上的程序例子親手輸入到電腦上實(shí)踐,即使配套光盤中有源代碼;
              35.把在書(shū)中看到的有意義的例子擴(kuò)充;
              36.請(qǐng)重視C++中的異常處理技術(shù),并將其切實(shí)的運(yùn)用到自己的程序中;
              37.經(jīng)常回顧自己以前寫(xiě)過(guò)的程序,并嘗試重寫(xiě),把自己學(xué)到的新知識(shí)運(yùn)用進(jìn)去;
              38.不要漏掉書(shū)中任何一個(gè)練習(xí)題——請(qǐng)全部做完并記錄下解題思路;
              39.C++語(yǔ)言和C++的集成開(kāi)發(fā)環(huán)境要同時(shí)學(xué)習(xí)和掌握;
              40.既然決定了學(xué)C++,就請(qǐng)堅(jiān)持學(xué)下去,因?yàn)閷W(xué)習(xí)程序設(shè)計(jì)語(yǔ)言的目的是掌握程序設(shè)計(jì)技術(shù),而程序設(shè)計(jì)技術(shù)是跨語(yǔ)言的;
              41.就讓C++語(yǔ)言的各種平臺(tái)和開(kāi)發(fā)環(huán)境去激烈的競(jìng)爭(zhēng)吧,我們要以學(xué)習(xí)C++語(yǔ)言本身為主;
              42.當(dāng)你寫(xiě)C++程序?qū)懙揭话雲(yún)s發(fā)現(xiàn)自己用的方法很拙劣時(shí),請(qǐng)不要馬上停手;請(qǐng)盡快將余下的部分粗略的完成以保證這個(gè)設(shè)計(jì)的完整性,然后分析自己的錯(cuò)誤并重新設(shè)計(jì)和編寫(xiě)(參見(jiàn)43);
              43.別心急,設(shè)計(jì)C++的class確實(shí)不容易;自己程序中的class和自己的class設(shè)計(jì)水平是在不斷的編程實(shí)踐中完善和發(fā)展的;
              44.決不要因?yàn)槌绦?#8220;很小”就不遵循某些你不熟練的規(guī)則——好習(xí)慣是培養(yǎng)出來(lái)的,而不是一次記住的;
              45.每學(xué)到一個(gè)C++難點(diǎn)的時(shí)候,嘗試著對(duì)別人講解這個(gè)知識(shí)點(diǎn)并讓他理解——你能講清楚才說(shuō)明你真的理解了;
              46.記錄下在和別人交流時(shí)發(fā)現(xiàn)的自己忽視或不理解的知識(shí)點(diǎn);
              47.請(qǐng)不斷的對(duì)自己寫(xiě)的程序提出更高的要求,哪怕你的程序版本號(hào)會(huì)變成Version 100.XX;
              48.保存好你寫(xiě)過(guò)的所有的程序——那是你最好的積累之一;
              49.請(qǐng)不要做浮躁的人;
              50.請(qǐng)熱愛(ài)C++!

            posted @ 2008-01-03 11:08 lovetiffany 閱讀(523) | 評(píng)論 (5)編輯 收藏

            我的Visual C++之路

            一.C++語(yǔ)言的基礎(chǔ)
                說(shuō)起入門慢,第一個(gè)原因莫過(guò)于語(yǔ)言基礎(chǔ)了.高中時(shí)期學(xué)校組織的微機(jī)興趣小組學(xué)習(xí)的是PASCAL語(yǔ)言(我也不知道為什么要講這個(gè)語(yǔ)言,如果說(shuō)是為了應(yīng)付比賽,當(dāng)時(shí)也有C語(yǔ)言組呀),所以在大學(xué)轉(zhuǎn)向Windows編程的時(shí)候,我首先選擇了Delphi.大三的時(shí)候?qū)W校要求考國(guó)家二級(jí),二級(jí)沒(méi)有 Delphi,于是又轉(zhuǎn)向了VB,原因是VB做起來(lái)與Delphi很像。后來(lái)發(fā)現(xiàn)VB的IDE做的比Delphi好用,而且BASIC語(yǔ)言寫(xiě)起來(lái)簡(jiǎn)單,于是便棄Delphi而去(明眼人恐怕又要罵我了,若不是太懶,怎么會(huì)喜歡VB的IDE呢?的確是這樣,后文會(huì)提到,懶不僅僅是學(xué)習(xí)VC的大敵,而且懶人是什么都學(xué)不好的)。長(zhǎng)年與VB打交道,讓我對(duì)C
            /C++語(yǔ)言很不習(xí)慣——我不喜歡C++寫(xiě)一個(gè)句語(yǔ)要打一個(gè)分號(hào),我不喜歡大小寫(xiě)字母要嚴(yán)格區(qū)分,我不喜歡比較的時(shí)候要寫(xiě)兩個(gè)等號(hào),我不喜歡……總之,對(duì)C++很沒(méi)好感,沒(méi)好感也就沒(méi)興趣學(xué)了(后面提到興趣是相當(dāng)重要的)。當(dāng)然如果你現(xiàn)在再問(wèn)我應(yīng)該學(xué)習(xí)什么語(yǔ)言,我會(huì)毫不猶豫地向你推薦C++,因?yàn)榫统S谜Z(yǔ)言而言,C++語(yǔ)言中包含的知識(shí)是相當(dāng)全面的——從面向過(guò)程,到基于對(duì)象/面向?qū)ο螅俚侥0搴头缎停梢哉f(shuō)是應(yīng)有盡有,不夸張地說(shuō),別的語(yǔ)言在某種程度上而言是C++語(yǔ)言的子集或者說(shuō)是在模仿C++、向C++靠攏。

                                              在數(shù)次失敗中,給我很明顯的感覺(jué)就是,不學(xué)好C
            ++語(yǔ)言就學(xué)習(xí)Visual 
                                            C
            ++純粹是一種自虐。這次入門之前,我花了3個(gè)多月的時(shí)間系統(tǒng)地學(xué)習(xí)了C++語(yǔ)言,夠意思吧。然后我信心實(shí)足地敲響Visual 
                                            C
            ++的家門,呵呵,這次她終于肯給面子了。舉個(gè)例子吧,在看Dll的調(diào)用時(shí),用到“函數(shù)指針”,順理成章就看下去了,想一想如果沒(méi)有C++語(yǔ)言的基礎(chǔ),基本是不可能的。所以說(shuō),沒(méi)學(xué)會(huì)中文之前,別看《紅樓夢(mèng)》,那不是《看圖識(shí)字》。奉勸想從VB轉(zhuǎn)向VC學(xué)習(xí)的朋友,如果你指望能像學(xué)習(xí)VB一樣邊學(xué)習(xí)VC 邊學(xué)習(xí)C++語(yǔ)言,那你可就錯(cuò)了。

                                              順便提醒C
            ++語(yǔ)言入門的朋友一點(diǎn),應(yīng)該關(guān)注ANSI/ISO 
                                            C
            ++,也就是標(biāo)準(zhǔn)C++了,市面上C++的書(shū)良莠不齊,很多書(shū)是“舊書(shū)換新皮”,講的仍然是非標(biāo)準(zhǔn)C++,一定要選好。計(jì)算機(jī)書(shū)很貴,大家不妨找電子版的來(lái)看,網(wǎng)上有很多,甚至《C++ 
                                            Primer》或者《C
            ++沉思錄》這樣的好書(shū)也有熱心朋友放到了網(wǎng)上。不過(guò),我最喜歡的是《C++編程金典》這本書(shū),不愧是教育大師寫(xiě)的書(shū),用來(lái)學(xué)習(xí)很合適。至于編譯器的選擇,如果條件允許就安裝VS.NET2003吧,據(jù)說(shuō)Visual 
                                            C
            ++7.1的編譯器是目前對(duì)標(biāo)準(zhǔn)C++支持的最好的編譯器了。

                                              二.VC學(xué)習(xí)資料的選擇

                                              VC入門難有很多原因,其中不容忽視的一個(gè)就是優(yōu)秀的VC學(xué)習(xí)資相對(duì)較少。C
            ++語(yǔ)言較深,Visual 
                                            C
            ++用起來(lái)復(fù)雜,再加上資料少——難上加難。資料少,并不意味著沒(méi)有,怎樣選擇或者說(shuō)挖掘就是關(guān)鍵。暫把資料分為光盤、書(shū)籍(包括電子書(shū))和文檔(包括網(wǎng)上的)三類。

                                              在選擇資料方面,大家一定要擯棄中國(guó)人思想中的兩大劣根性:
            <1>不勞而獲<2>一夜暴富。

                                              “不勞而獲”的思想會(huì)導(dǎo)致趨向于選擇“講課”類的資料,比如多媒體光盤。結(jié)果是光盤容量往往很少但又要求內(nèi)容面面俱到(不然怎么賣出去呀),這就造成了知識(shí)的連貫性差而且講的又飛快,任你一遍一遍地聽(tīng),不見(jiàn)成效又打擊信心,最后只能放棄。期待早日有內(nèi)容豐富,講解精彩的光盤面市。

                                              “一夜暴富”的思想會(huì)讓你趨向于選擇“速成”類教材。那樣的教材大多是騙人的——能寫(xiě)個(gè)彈出窗口Hello一下World,這就能算是會(huì)Visual 
                                            C
            ++了?我們還是不要自欺欺人的好。至少也要能連數(shù)據(jù)庫(kù)、能使用Socket吧……而這些知識(shí)怎么可能“速成”呢?

                                              我有很多Visual 
                                            C
            ++的學(xué)習(xí)資料,但沒(méi)有一本我是抱著一啃到底的,因?yàn)闆](méi)有哪本書(shū)十全十美,我是交替著使用些資料,這樣做的好處在于:

                                              <
            1>知識(shí)的連貫性好,跳躍性小,進(jìn)階坡度較小,讀起來(lái)舒服。都說(shuō)Visual 
                                            C
            ++的學(xué)習(xí)坡度比較陡,那個(gè)陡坡是出現(xiàn)在由單純的C++語(yǔ)言學(xué)習(xí)轉(zhuǎn)向Windows編程的時(shí)候,C++語(yǔ)言本身的學(xué)習(xí)并沒(méi)有那么困難。

                                              <
            2>有積累效應(yīng),這本書(shū)講的不精不透,另一本書(shū)會(huì)幫你補(bǔ)上,這本書(shū)你沒(méi)留心,下本書(shū)總該長(zhǎng)個(gè)心眼。還有就是一些小例子程序,把MFC的類或者函數(shù)拆開(kāi)來(lái)給你看,目的非常明確,效果也不錯(cuò)。每天學(xué)一點(diǎn),不圖快,圖扎實(shí)。呵呵,跟VC搞“面向?qū)ο?#8221;,當(dāng)然要一天一點(diǎn)戀愛(ài)了。

                                              <
            3>舉一返三,動(dòng)手實(shí)踐。如果多本書(shū)中都把它列為重點(diǎn),那就一定要熟記在心而且上機(jī)操作,書(shū)上的例子一定要分析透徹,不能有“差不多”的思想——差多少算多呢?程序這東東,錯(cuò)一個(gè)字母都不行呀。光看會(huì)了還差遠(yuǎn)著呢,自己要能寫(xiě),而且能對(duì)例子進(jìn)行擴(kuò)展才行。

                                              
            <4>內(nèi)容詳實(shí)豐富,這一點(diǎn)上,首推MSDN啦,還有就是在網(wǎng)上能找到的微軟出的Visual 
                                            C
            ++的叢書(shū),希望譯的電子版,是wdl格式的。雖然MSDN是英文版,但其中的英文并不難——您盡可以相信我,因?yàn)樵谙碌挠⑽乃绞瞧鏍€無(wú)比的。 MSDN有兩種用法,一種是當(dāng)字典用,因?yàn)閮?nèi)容全;一種是當(dāng)消遣,沒(méi)事了看一個(gè)類,敲幾行代碼,看到那個(gè)MFC的繼承圖了嗎,挺好玩兒的,感覺(jué)像逛街—— 而且東西不要錢,help 
                                            yourself。

                                              互聯(lián)網(wǎng)上的資源是非常非常豐富的,千萬(wàn)不要錯(cuò)過(guò)!好網(wǎng)站和下載站BB皆是。還有論壇、新聞組、在線QQ群……你問(wèn)我有哪些?呵呵,遠(yuǎn)在天邊近在眼前呀:)

                                            三.內(nèi)因與外因:“三心二意”和“高手朋友”你有嗎?

                                              啊哦,我不是在開(kāi)玩笑。“三心”是指決心,信心和耐心。決心來(lái)源于動(dòng)機(jī),說(shuō)來(lái)好笑,我最初動(dòng)機(jī)很簡(jiǎn)單,大學(xué)時(shí)有個(gè)朋友,計(jì)算機(jī)系的,我總認(rèn)為我比他聰明(我的天~~~~),他會(huì)VC我不會(huì),我就想超過(guò)他,現(xiàn)在都畢業(yè)兩年了,最初的動(dòng)機(jī)早已經(jīng)不在了,而學(xué)習(xí)卻VC已經(jīng)成了我的心愿——最關(guān)鍵的一點(diǎn)是我的愿望是寫(xiě)自己的輸入法,而寫(xiě)輸入法只能用Visual 
                                            C
            ++去實(shí)現(xiàn),所以我會(huì)有決心學(xué)好Visual 
                                            C
            ++。至于信心,有兩次失敗完全是信心不足造成的,促成這次成功的信心說(shuō)起來(lái)還挺傳奇:我去北京玩兒,回家的火車上一姓趙位老師看見(jiàn)我別著一個(gè)MCP的領(lǐng)章就過(guò)來(lái)跟我聊天,得知他是一位有著十多年VC開(kāi)發(fā)經(jīng)驗(yàn)的程序員,敬意油然而生。聊天的過(guò)程中,趙老師給了我極大的鼓勵(lì)和支持——我問(wèn)他像我這種 Wood 
                                            Head能不能在半年內(nèi)入門VC,他告訴我,一定能,于是我就堅(jiān)定了自己的信心,現(xiàn)在剛好是4個(gè)月,如果趙老師有機(jī)會(huì)看到這篇文章——我在這里謝謝您啦! (花絮:下車,兩個(gè)小時(shí)后我與女友分手了,是被甩呀同志們!隨后的一段日子里,一直與VC相伴……) 
                                            還要說(shuō)說(shuō)耐心:如果您已經(jīng)看到這里了,說(shuō)明您很有耐心(竟然能看到這里還沒(méi)有拂袖而去),耐心與個(gè)人的風(fēng)格有關(guān),沒(méi)耐心的人多半是懶人,懶人什么都做不成,學(xué)習(xí)VC就是不能懶,書(shū)懶得看,問(wèn)題懶得問(wèn),英語(yǔ)懶得譯……或者是有點(diǎn)挫折就放棄,學(xué)好VC是沒(méi)指望了。我不知道別人怎樣,反正我是沒(méi)少受挫,其實(shí)有兩次離入門就那么一點(diǎn)點(diǎn)了,我放棄了……學(xué)VC要越挫越勇,學(xué)VC要肯定執(zhí)著,Gogogo!

                                              “二意”是指第一你要感覺(jué)學(xué)習(xí)VC有“意思”,二是你要感覺(jué)學(xué)習(xí)VC有“意義”。有意思,就是說(shuō)你喜歡寫(xiě)程序,“三心”的源動(dòng)力來(lái)源于你對(duì)程序設(shè)計(jì)的熱愛(ài),不喜歡編程的人可能能學(xué)好VB但絕學(xué)不好VC。有意義,就是說(shuō)你要給自己一個(gè)理由:自己都不能給自己一個(gè)交待的事情是做不長(zhǎng)久的。前面說(shuō)過(guò),我是為了寫(xiě)自己的輸入法,解放中國(guó)人的雙手,這個(gè)理由夠純潔夠崇高,還有一個(gè)理由就是通過(guò)學(xué)習(xí)VC來(lái)礪練自己,成為一個(gè)真正的程序員。你可以有自己的理由,比如提高薪水或者取得認(rèn)證云云,一定要有!這就像是給自己的“報(bào)酬”,沒(méi)有報(bào)酬只憑激情做事是任何事都做不長(zhǎng)久的。

                                              我小小的成功,有嚴(yán)重的原因是因?yàn)槲矣形?#8220;高手朋友”——楊W,他是個(gè)VC高手,大家會(huì)好奇地問(wèn):他教你寫(xiě)什么呢?是MFC還是ATL或者是COM?呵呵,都不是,他從來(lái)沒(méi)教我寫(xiě)過(guò)一行代碼,但他對(duì)我的每一次幫助都彌足珍貴,當(dāng)我不知道從哪里查找類庫(kù)資源的時(shí)候,他告訴我:MSDN;當(dāng)我不知道從哪里找到類的成員函數(shù)時(shí),他告訴我:在頁(yè)面的左下角有一個(gè)class 
                                            member鏈接,當(dāng)我問(wèn)他能不能完成XXXX時(shí),他說(shuō):別白費(fèi)力氣了……在他的幫助下,我少走了很多彎路,這也正是高手朋友的可貴之處。在此,我要衷心地說(shuō)一聲:謝謝!

                                              并不是每位學(xué)習(xí)VC的朋友都有我這么好的運(yùn)氣,如果你身邊沒(méi)有這樣的朋友也不用著急嗎,我這位好朋友可是經(jīng)常出沒(méi)于CSDN的壇壇里,明白了?不過(guò),提醒與我一樣的初學(xué)者:一定要做一個(gè)會(huì)問(wèn)問(wèn)題的人哦!怎么做一個(gè)會(huì)問(wèn)題的人呢?概括一下就是:目的明確,言簡(jiǎn)意賅,核心代碼,客氣謙虛。

                                              四.VC入門隨筆

                                              本人寫(xiě)東西向來(lái)思緒凌亂、顛三倒四。剩下好多東西不知道寫(xiě)到哪里,沒(méi)辦法了,只好叫“隨筆”咯。

                                              ……學(xué)習(xí)VC編程,首先要豎立一個(gè)“系統(tǒng)
            /全局觀”。無(wú)論是VB、C#、Delphi,寫(xiě)程序的時(shí)候只需要考慮程序本身就行了,換句話說(shuō)就是你不用考慮消息是如何映射和傳遞的。而VC寫(xiě)程序就要多多少少考慮到這些東西。打個(gè)比方:以前用VB寫(xiě)程序,就好像是在一座山上建一個(gè)亭子,山是山,亭子是亭子,我只管造亭子就是了;而用VC寫(xiě)程序,還是這個(gè)亭子,那么你應(yīng)該意識(shí)到,亭子是山的亭子,是山的一部分而不是一個(gè)孤立的建筑。“亭子”就是程序,“山”就是Windows系統(tǒng),亭子的地基是山留給建筑的“接口”,也就是API了……

                                              ……VC相對(duì)VB入門難,一上來(lái)不是像VB那樣給個(gè)窗體從頭做起,而且AppWizard要分好幾步,每一步里還有一大堆不知所云的選項(xiàng),不等生成一個(gè)程序就已經(jīng)暈頭轉(zhuǎn)向了。怎么辦呢?一句話,從對(duì)話框程序入手,因?yàn)樗詈?jiǎn)單,生成的類最少,而且相對(duì)是與VB編程最“像”的。在對(duì)話框程序里,你可以充分練習(xí)添加類和成員變量或者成員函數(shù)。……不過(guò)我有一點(diǎn)始終搞不明白,由易到難是對(duì)話框程序、單文檔程序、多文檔程序,在AppWizard里微軟為什么不按這個(gè)順序排列,非要倒著來(lái)呢?成心跟我們這些初學(xué)的做對(duì)!(國(guó)罵省去)……

                                              ……又是沒(méi)大寫(xiě)……又是少分號(hào)……又是少一個(gè)等號(hào)……提醒VB轉(zhuǎn)過(guò)來(lái)的程序員,別總像我這么沒(méi)記性哦!

                                              ……還是提醒那些學(xué)習(xí)了VB或者是VB.NET
            /C#的DDMM,MFC的類雖然是面向?qū)ο蟮模鼪](méi)有“屬性”這個(gè)概念地!不要指望有Me.TextBox1.Text="Hello 
                                            World!"這樣的語(yǔ)法,C/C++是函數(shù)型的語(yǔ)言,類已經(jīng)把“屬性”封裝成了成員變量,那些私有的成員變量你看不到,只能通過(guò)函數(shù)來(lái)更改——this ->myTextBox.SetWindowText("Hello 
                                            World!
            ");……190-823 117-202 1Z0-043 1z0-042  

                                              ……暈,原來(lái)Win32程序和MFC程序不是一回事呀(看看,這就是一本爛書(shū)帶給我的,讓我一直以為Win32程序就是MFC程序,直到拜讀《深入淺出MFC》時(shí)才恍然大悟)……

                                              ……VC好還是VB好?(拜托,別再問(wèn)這種無(wú)聊的問(wèn)題了)……

                                              ……VC的確能做底層,但不是最底層;VC的確功能強(qiáng)大,但不是萬(wàn)能的——拿手術(shù)刀切西瓜或者用菜刀動(dòng)手術(shù)都不對(duì)……

                                              ……VC高手都是用記事本寫(xiě)程序的:笑不笑由你……190-802 000-834 000-861

                                              ……VC程序員比VB程序員強(qiáng):呵呵,毛主席說(shuō)過(guò),武器不是戰(zhàn)爭(zhēng)勝利的決定因素……

                                              ……在快速開(kāi)發(fā)工具(RAD)中,控件與后臺(tái)代碼是捆綁在一起的,而MFC的“控件類”不一樣,它的“資源”(或者說(shuō)是皮)與“類”(或者說(shuō)是瓤)是分開(kāi)的,要通過(guò)ClassWizard把它們“粘”起來(lái)……

                                              ……如果說(shuō)C
            ++是一種程序設(shè)計(jì)語(yǔ)言,那么Visual 
                                            C
            ++中的C++語(yǔ)言不如叫“Windows語(yǔ)言”更合適——Visual 
                                            C
            ++就是在編程Windows,用到的宏或者Windows數(shù)據(jù)類型和Windows結(jié)構(gòu)數(shù)不勝數(shù),做好心理準(zhǔn)備哦!……

                                              ……我的天,那么長(zhǎng)的函數(shù)或者結(jié)構(gòu)都要一個(gè)字母一個(gè)字母寫(xiě)呀!呵呵,按一下Ctrl
            +J看看發(fā)生了什么?我就奇怪了,幾乎沒(méi)看到有書(shū)上提醒我們的初學(xué)者要這樣去做。這可是著實(shí)嚇跑了不少初學(xué)者呢!(至少我就被嚇跑過(guò))。器利工善,我們要把IDE用熟哦,微軟送的好禮物可不能浪費(fèi)……

                                              ……很多書(shū)在添加完對(duì)新話框類之后都寫(xiě)著要在主對(duì)話框類里手動(dòng)添加對(duì)這個(gè)新類頭文件的引用,何必呢?用添加成員變量的方法添加這個(gè)新對(duì)話框類的實(shí)例,頭文件自動(dòng)引用,一舉兩得。一句話:盡量多用Class 
                                            Wizard,能不手寫(xiě)的地方就不手寫(xiě)……

                                              五.virtual BOOL LongWayToGo(void)

                                              {
                                               
            //頭一次寫(xiě)文章,其中Bug肯定少不了,大家一起來(lái)DeBug。
                                               
            //由于是入門級(jí)文章,如果有錯(cuò)誤,很可能影響初學(xué)者學(xué)習(xí),恐誤人子弟,有錯(cuò)必糾!
                                               
            //希望大家多提寶貴意見(jiàn),幫助我前進(jìn),謝謝先!
                                               
            //這是虛函數(shù),留待有所得時(shí)續(xù)以后文。我還有很長(zhǎng)的路……
                                               return 
            TRUE;
                                              } 

            posted @ 2008-01-03 11:06 lovetiffany 閱讀(343) | 評(píng)論 (0)編輯 收藏

            C/C++語(yǔ)言誤區(qū)一

                 摘要: 很多人甚至市面上的一些書(shū)籍,都使用了void main( ) ,其實(shí)這是錯(cuò)誤的。C/C++ 中從來(lái)沒(méi)有定義過(guò)void main( ) 。C++ 之父 Bjarne Stroustrup 在他的主頁(yè)上的 FAQ 中明確地寫(xiě)著 The definition void main( ) { /* ... */ } is not and never has been C++, nor has it even been C.( void main( ) 從來(lái)就不存在于 C++ 或者 C )。下面我分別說(shuō)一下 C 和 C++ 標(biāo)準(zhǔn)中對(duì) main 函數(shù)的定義  閱讀全文

            posted @ 2008-01-03 11:03 lovetiffany 閱讀(395) | 評(píng)論 (0)編輯 收藏

            2008年1月2日

            C++ Builder 2007 之 快速安裝

            對(duì)于崇尚中庸之道的朋友,就不必理會(huì)這篇文章了。簡(jiǎn)單說(shuō)明一下目前網(wǎng)絡(luò)安裝的簡(jiǎn)單過(guò)程:
            安裝向?qū)builder2007trialsetup.exe檢測(cè)是否有.net 2.0環(huán)境,這個(gè)好辦,如果沒(méi)有安裝環(huán)境,在網(wǎng)上可以下載到并安裝。但是它的本體,全部安裝文件400多M,通過(guò)安裝向?qū)У膯尉€程進(jìn)行下載,臨近每個(gè)文件結(jié)尾的時(shí)候還留下非常充裕的時(shí)間給你上wc,你需要上這么多次嗎?所以我強(qiáng)烈推薦我們的快車最新版,開(kāi)8個(gè)線程真是牛啊。好了,廣告時(shí)間已過(guò),開(kāi)始我們的旅程。

            7zip文件管理器,用于解壓7zip格式的文件:
            http://www.skyuc.com/bbs/showthread.php?t=62&goto=nextoldest

            .net 2.0環(huán)境所需要安裝的3個(gè)文件:
            1)http://installers.codegear.com/prereq/radstudio/5.0/microsoft%20.net%20framework%202.0.7zip
            2)http://installers.codegear.com.edgesuite.net/prereq/radstudio/5.0/microsoft%20jsharp%20runtime%202.0.7zip
            3)http://installers.codegear.com.edgesuite.net/prereq/radstudio/5.0/microsoft%20.net%202.0%20english%20framework%20sdk%20x86.7zip

            http://www.microsoft.com/downloads/info.aspx?na=90&p=&SrcDisplayLang=zh-cn&SrcCategoryId=&SrcFamilyId=fe6f2099-b7b4-4f47-a244-c96d69c35dec&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2fb%2fe%2fa%2fbea35549-7804-4e28-beef-a7d9d1675f4c%2fsetup.exe

            在www.codegear.com上注冊(cè),拿到你的安裝序列號(hào),下載安裝向?qū)builder2007trialsetup.exe:
            C++Builder 2007 Enterprise Trial
            Serial Number: xxxxx
            CDN Login Name: xxx
            First Name: xxx
            Last Name: xxx

            重要提示:注意下文的編號(hào),如1)和(1)是不同的,并不是我寫(xiě)錯(cuò)了

            需要特別注意的4個(gè)目錄:
            1)2007安裝目錄,自定義:我的為J:\CodeGear\RAD Studio\5.0
            2)示例文件目錄,自定義:J:\CodeGear\RAD Studio\5.0\Demos
            3)安裝向?qū)Т鎯?chǔ)2007安裝文件用到的臨時(shí)目錄,自定義:H:\download\bcb2007
            4)安裝向?qū)builder2007trialsetup.exe解壓3)的安裝文件用到的臨時(shí)目錄,不可自定義,所以一般要保證c盤剩余空間在1.2g以上(安裝了.net 2.0之后,系統(tǒng)仍提示需要1.7g,實(shí)際上不要這么多,可以忽視它的提示)對(duì)于所有用戶使用方式安裝的為:C:\Documents and Settings\All Users\ApplicationData\{2EB4C530-C94F-4893-ABDC-C1E05A89956E}

            安裝的4個(gè)步驟:
            (1)安裝.net 2.0環(huán)境的3個(gè)文件,網(wǎng)上遍地都是。如果是7zip格式的文件,你要先安裝7zip的解壓軟件
            (2)運(yùn)行cbuilder2007trialsetup.exe,設(shè)置各種目錄,直到它開(kāi)始向3)的目錄下載并自動(dòng)解壓到4)中,這是一個(gè)環(huán)繞地球80天的等待步驟,就讓它自動(dòng)運(yùn)行好了
            (3)在(2)的同時(shí),我們用快車之類的軟件先下載3)用到的所有文件約423M并放到3)中的目錄,下面的鏈接你可以手工一條條地輸入快車,也可以做成一個(gè)html文件批量導(dǎo)入,方法我就不說(shuō)了,留給大家發(fā)揮:
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/vcldotnetruntimes.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/vclwin32runtimes.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/bcbwin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/bcbwin32%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/dunit.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/core.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/core%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/corex.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/corecx.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/transm// 本文轉(zhuǎn)自 C++Builder 研究 - http://www.ccrun.com/article.asp?i=1024&d=lmp27s
            ogrifierc.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/corewin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/corewin32%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/xmlmapper.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/coredelphibcb.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/database.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/databasew32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/dbexplr.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/ibxw32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/ibxbcbwin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/ibxbcbwin32%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/dbgow32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/sqlbuilder.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/sqlbuilder%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/dbxcomponentsw32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/dbexpress.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/dbexpress%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/dbexpressx.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/unittesting.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/unittesting_bcb.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/htmldesigner.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/htmldesigner%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/ite.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/etm.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/itewin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/indywin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/indybcbwin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/intraweb.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/iwbcbwin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/sampleprograms.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/samplesbcbwin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/helpwin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/helpwin32%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/helpcommon.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/helpcommon%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/codevisualization.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/codevisualizationx.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/codevisualizationx%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/codevisualization_bcbw32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/internetcontrols.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/internetctrlswin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/internetctrlswin32x.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/soapwin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/webappdebugger.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/msofficecontrolscore.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/msofficebcbwin32.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/corba.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/codeguard.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/bde.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/bdecommon.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/bdecommon%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/bde_pro.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/bdecommon_pro.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/bdecommon_pro%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/databasedesktop.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/databasedesktop%20english.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/imagefiles.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/sampledatafiles.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/rave%20reports%20installer.7zip
            http://installers.codegear.com/release/radstudio/11.0.2709.7128/borland%20database%20engine%20professional%20english.7zip

            (4)完成了(3)之后,停止(2)的運(yùn)行,再重新運(yùn)行(2),這時(shí)候它就不再下載,只會(huì)自動(dòng)解壓3)中的所有文件并放到4)去。當(dāng)然,(3)中的文件下漏幾個(gè)也無(wú)所謂,反正它會(huì)補(bǔ)下。它仍需要通過(guò)網(wǎng)絡(luò)驗(yàn)證這些文件的有效性,所以這也是一個(gè)環(huán)游地球一圈的漫長(zhǎng)時(shí)間。最后,提示你注冊(cè),如果你沒(méi)有那個(gè)可愛(ài)的破解文件bds.exe(版本11.0.2709.7128),估計(jì)你會(huì)進(jìn)不去。如果你今天才來(lái),很不幸,http://www.ccrun.com/forum/forum_posts.asp?TID=6071提到的破解文件已經(jīng)被刪除了,但是我成為了幸運(yùn)者,所以我才能夠完成這篇文章。替換bds.exe,運(yùn)行,啟動(dòng)速度比起我無(wú)法忍受的turbo c++ 2006 professional(和bds 2006是兄弟)快了很多。com支持已經(jīng)存在了,非常好。

            posted @ 2008-01-02 09:26 lovetiffany 閱讀(559) | 評(píng)論 (0)編輯 收藏

            如何禁用一個(gè)樹(shù)節(jié)點(diǎn)(Disable Tree Node)

            TTreeView是VCL中提供的樹(shù)列表控件,樹(shù)的每個(gè)節(jié)點(diǎn)是一個(gè)TTreeNode類,TTreeNode組件的屬性和方法可以參考Borland提供的幫助(雖然不如MSDN全面,但有總比沒(méi)有強(qiáng))。實(shí)際應(yīng)用中我們可能需要禁用某個(gè)節(jié)點(diǎn)(界面上反應(yīng)的效果是:節(jié)點(diǎn)字體呈灰色顯示,節(jié)點(diǎn)無(wú)法選中等)。但是VCL沒(méi)有提供Node->Disable();或Node->Enable = false;這樣的功能,我們只好自己動(dòng)手實(shí)現(xiàn)了。首先我們需要為每個(gè)節(jié)點(diǎn)設(shè)定一個(gè)標(biāo)志,用來(lái)標(biāo)識(shí)此節(jié)點(diǎn)是否可用,標(biāo)識(shí)方法有很多,比如判斷節(jié)點(diǎn)的文本(Text),節(jié)點(diǎn)的絕對(duì)索引值(AbsoluteIndex),節(jié)點(diǎn)的索引(Index)加縮進(jìn)(Indent)等,在本例中我們用節(jié)點(diǎn)的Data屬性作標(biāo)識(shí)(一個(gè)void *型數(shù)據(jù),其實(shí)可以存放N多東西)。如果在你的應(yīng)用中恰好用了Data屬性,就另外想個(gè)用來(lái)作標(biāo)志的方法吧。:)

            我們寫(xiě)一個(gè)自定義函數(shù),用來(lái)啟用/禁用一個(gè)節(jié)點(diǎn):
            //---------------------------------------------------------------------------
            // 函數(shù)功能: 改變指定節(jié)點(diǎn)的啟用/禁用狀態(tài)
            //     bEnable: 啟用: true; 禁用: false
            //     pNode: 要改變狀態(tài)的節(jié)點(diǎn)

            void __fastcall CrnEnableTreeNode(bool bEnable, TTreeNode *pNode)
            {
                // 設(shè)定規(guī)則,如果節(jié)點(diǎn)允許使用,Node的Data存放0(默認(rèn)就是0),
                // 如果禁用,Data存放0xFFFF

                pNode->Data = bEnable? NULL: (void *)0xFFFF;
                // 標(biāo)志改變后重繪一下TreeView
            // 本文轉(zhuǎn)自 C++Builder 研究 - http://www.ccrun.com/article.asp?i=1015&d=r2tf61
                pNode->TreeView->Invalidate();
            }
            然后考慮如何達(dá)到禁用節(jié)點(diǎn)的效果,前面說(shuō)了,我們只需實(shí)現(xiàn)這兩個(gè)效果:
            1. 節(jié)點(diǎn)字體呈灰色顯示
            2. 節(jié)點(diǎn)無(wú)法選中
            節(jié)點(diǎn)字體呈灰色顯示可以通過(guò)TreeView的OnCustomDrawItem事件中的自繪實(shí)現(xiàn),在設(shè)計(jì)時(shí)狀態(tài),選中TreeView,Events選項(xiàng)卡雙擊OnCustomDrawItem事件,添加以下代碼:
            //---------------------------------------------------------------------------
            void __fastcall TForm1::TreeView1CustomDrawItem(TCustomTreeView *Sender,
                  TTreeNode *Node, TCustomDrawState State, bool &DefaultDraw)
            {
                // 判斷當(dāng)前繪制節(jié)點(diǎn)是否被禁用
                // 63 63 72 75 6E 2E 63 6F 6D

                if(int(Node->Data) == 0xFFFF)
                {
                    // 如果節(jié)點(diǎn)被禁用則用灰色字體顯示,默認(rèn)為黑色
                    Sender->Canvas->Font->Color = clGray;
                }
            }
            節(jié)點(diǎn)無(wú)法選中則可以通過(guò)TreeView的Changing事件來(lái)處理,在設(shè)計(jì)時(shí)狀態(tài),選中TreeView,Events選項(xiàng)卡雙擊OnChanging事件,添加以下代碼:
            //---------------------------------------------------------------------------
            void __fastcall TForm1::TreeView1Changing(TObject *Sender, TTreeNode *Node,
                  bool &AllowChange)
            {
                // 欲選中這個(gè)節(jié)點(diǎn)時(shí),判斷如果當(dāng)前節(jié)點(diǎn)被禁用則禁止改變?cè)?jié)點(diǎn)的選擇狀態(tài)
                AllowChange = (int(Node->Data) != 0xFFFF);
            }
            有以上的實(shí)現(xiàn),效果基本就出來(lái)了:
            Disable tree node

            測(cè)試代碼:
            //---------------------------------------------------------------------------
            void __fastcall TForm1::Button1Click(TObject *Sender)
            {
                // 禁用當(dāng)前選中的節(jié)點(diǎn)
                if(TreeView1->Selected)
                    CrnEnableTreeNode(true, TreeView1->Selected);
            }
            //---------------------------------------------------------------------------
            void __fastcall TForm1::Button2Click(TObject *Sender)
            {
                // 啟用第二個(gè)節(jié)點(diǎn)
                CrnEnableTreeNode(true, TreeView1->Items->Item[1]);
            }
            //---------------------------------------------------------------------------
            為看到比較好的效果,可在測(cè)試時(shí)展開(kāi)所有節(jié)點(diǎn)為:
            TreeView1->FullExpand();

            posted @ 2008-01-02 09:19 lovetiffany 閱讀(935) | 評(píng)論 (1)編輯 收藏

            僅列出標(biāo)題  下一頁(yè)
            <2008年3月>
            2425262728291
            2345678
            9101112131415
            16171819202122
            23242526272829
            303112345

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            留言簿(4)

            隨筆檔案

            網(wǎng)站收藏

            搜索

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久国产美女免费观看精品| 亚洲精品乱码久久久久久| 成人a毛片久久免费播放| 久久精品国产清自在天天线| 欧美日韩中文字幕久久久不卡 | 久久夜色精品国产网站| 精品久久久久久久无码| 国产精品成人久久久久三级午夜电影 | 久久精品青青草原伊人| 国产亚洲综合久久系列| 国产精品伊人久久伊人电影| 欧美精品九九99久久在观看| 久久久久久亚洲AV无码专区| 国产精品青草久久久久福利99| 一本色道久久88综合日韩精品 | 国产69精品久久久久9999APGF| www.久久热.com| 亚洲国产精品嫩草影院久久 | 国产aⅴ激情无码久久| 日韩精品国产自在久久现线拍| 久久久久亚洲AV片无码下载蜜桃| 丁香五月网久久综合| 日韩精品无码久久一区二区三| 午夜精品久久久久久99热| 国产午夜电影久久| 热re99久久精品国99热| 日本久久中文字幕| 久久国产成人精品麻豆| 亚洲精品无码成人片久久| 久久久久亚洲av毛片大| 久久精品男人影院| 亚洲va久久久噜噜噜久久| 人妻无码精品久久亚瑟影视| 国内精品久久久久| 亚洲精品乱码久久久久久久久久久久| 国产精自产拍久久久久久蜜| 久久久久女人精品毛片| av色综合久久天堂av色综合在| 久久国产三级无码一区二区| 久久99精品国产麻豆宅宅| 色欲久久久天天天综合网精品 |