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

            來吧,朋友!

            為C++瘋狂

            2009年8月13日

            http://www.scs.stanford.edu/~dm/home/papers/c++-new.html
            posted @ 2009-08-13 14:47 yanghaibao| 編輯 收藏

            2009年8月12日


            一、選擇公司的形式:

            普通的有限責(zé)任公司,最低注冊資金3萬元,需要2個或2個以上的股東,

            從06年1月起新的公司法規(guī)定,允許1個股東注冊有限責(zé)任公司,這種特殊的有限責(zé)任公司又稱“一人有限公司”(但公司名稱中不會有“一人”字樣,執(zhí)照上會注明“自然人獨資”),最低注冊資金10萬元。如果只有你一個人作為股東,則選擇一人有限公司,最低注冊資金10萬元;如果你和朋友、家人合伙投資創(chuàng)業(yè),可選擇普通的有限公司,最低注冊資金3萬元。建議你準(zhǔn)備好注冊資金3萬元。

            二、注冊公司所需的注冊資料:
            (1)個人資料(身份證、法人戶口本復(fù)印件或戶籍證明、居住地址、電話號碼)
            (2)注冊資金
            (3)擬訂注冊公司名稱若干
            (4)公司經(jīng)營范圍
            (5)租房房產(chǎn)證、租賃合同
            (6)公司住所
            (7)股東名冊及股東聯(lián)系電話、聯(lián)系地址
            (8)公司的機構(gòu)及其產(chǎn)生辦法、職權(quán)、議事規(guī)則
            (9)公司章程

            三、注冊公司的步驟:

            1.核名:到工商局去領(lǐng)取一張“企業(yè)(字號)名稱預(yù)先核準(zhǔn)申請表”,填寫你準(zhǔn)備取的公司名稱,由工商局上工商局內(nèi)部網(wǎng)檢索是否有重名,如果沒有重名,就可以使用這個名稱,就會核發(fā)一張“企業(yè)(字號)名稱預(yù)先核準(zhǔn)通知書”。工商名稱核準(zhǔn)費是40元,交給工商局。 40元可以幫你檢索5個名字,很多名字重復(fù),所以一般常見的名字就不用試了,免得花冤枉錢。

            2.租房: 去專門的寫字樓租一間辦公室,如果你自己有廠房或者辦公室也可以,有的地方不允許在居民樓里辦公。 你要交房租給所租辦公室的房東(所有權(quán)人),假設(shè)辦公室的房租是1000元/月,一般起租最少6個月,6個月的房租是6000元。

            3.簽訂租房合同:你要與你所租的辦公室的房東簽定租房合同,并讓房東提供房產(chǎn)證的復(fù)印件。租房合同打印費5份15元,房產(chǎn)證復(fù)印件5張2.5元。

            4.買租房的印花稅:你要到稅務(wù)局去買印花稅,按年租金的千分之一的稅率購買,貼在房租合同的首頁。例如你的每年房租是1.2萬元,那就要買12元錢的印花稅,后面凡是需要用到房租合同的地方,都需要是貼了印花稅的合同復(fù)印件。

            5.編寫“公司章程”:可以在工商局網(wǎng)站下載“公司章程”的樣本,修改一下就可以了。章程的最后由所有股東簽名。 假設(shè)章程打印5份(股東2人各2份、工商局1份、銀行1份、會計師事務(wù)所1份),章程打印費15元、下載公司章程的上網(wǎng)費2元。

            6.刻私章: 去街上刻章的地方刻一個私章,給他們講刻法人私章(方形的)。刻章費用20元。

            7.到會計師事務(wù)所領(lǐng)取“銀行詢征函”:聯(lián)系一家會計師事務(wù)所,領(lǐng)取一張“銀行詢征函”,必須是原件,會計師事務(wù)所蓋鮮章。如果你不清楚,可以看報紙上的分類廣告,有很多會計師事務(wù)所的廣告。銀行詢征函10元。

            8.去銀行開立公司驗資戶: 所有股東帶上自己入股的那一部分錢到銀行,帶上公司章程、工商局發(fā)的核名通知、法人代表的私章、身份證、用于驗資的錢、空白詢征函表格,到銀行去開立公司帳戶,你要告訴銀行是開驗資戶。開立好公司帳戶后,各個股東按自己出資額向公司帳戶中存入相應(yīng)的錢。 銀行會發(fā)給每個股東繳款單、并在詢征函上蓋銀行的章。公司驗資戶開戶費20元。

            注意:公司法規(guī)定,注冊公司時,投資人(股東)必須繳納足額的資本,可以以貸幣形式(也就是人民幣)出資,也可以以實物(如汽車、房產(chǎn)、知識產(chǎn)權(quán)等)出資。到銀行辦的只是貨幣出資這一部分,如果你有實物、房產(chǎn)等作為出資的,需要到會計師事務(wù)所鑒定其價值后再以其實際價值出資,比較麻煩,因此建議你直接拿錢來出資,公司法不管你用什么手段拿的錢,自己的也好、借的也好,只要如數(shù)繳足出資款即可。

            9.辦理驗資報告:拿著銀行出具的股東繳款單、銀行蓋章后的詢征函,以及公司章程、核名通知、房租合同、房產(chǎn)證復(fù)印件,到會計師事務(wù)所辦理驗資報告,會計師事務(wù)師驗資報告按注冊資本收費。50萬元以下注冊資金驗資費500元。

            10.注冊公司:到工商局領(lǐng)取公司設(shè)立登記的各種表格,包括設(shè)立登記申請表、股東(發(fā)起人)名單、董事經(jīng)理監(jiān)理情況、法人代表登記表、指定代表或委托代理人登記表。注冊登記費,按注冊資金的萬分之8收取。填好后,連同核名通知、公司章程、房租合同、房產(chǎn)證復(fù)印件、驗資報告一起交給工商局。大概3個工作日后可領(lǐng)取執(zhí)照。注冊公司手續(xù)費300元。

            11.憑營業(yè)執(zhí)照,到公安局特行科指定的刻章社,去刻公章、財務(wù)章。后面步驟中,均需要用到公章或財務(wù)章。公章50元,財務(wù)章50元。

            12.辦理企業(yè)組織機構(gòu)代碼證:憑營業(yè)執(zhí)照到技術(shù)監(jiān)督局辦理組織機構(gòu)代碼證,費用是80元。辦這個證需要半個月,技術(shù)監(jiān)督局會首先發(fā)一個預(yù)先受理代碼證明文件,憑這個文件就可以辦理后面的稅務(wù)登記證、銀行基本戶開戶手續(xù)了。

            13.去銀行開基本戶:憑營業(yè)執(zhí)照、組織機構(gòu)代碼證,去銀行開立基本帳號。最好是在原來辦理驗資時的那個銀行的同一網(wǎng)點去辦理,否則,會多收100元的驗資帳戶費用。 開基本戶需要填很多表,你最好把能帶齊的東西全部帶上,要不然要跑很多趟,包括營業(yè)執(zhí)照正本原件、身份證、組織機構(gòu)代碼證、公財章、法人章。

            開基本戶時,還需要購買一個密碼器(從2005年下半年起,大多銀行都有這個規(guī)定),今后你的公司開支票、劃款時,都需要使用密碼器來生成密碼。公司基本帳號開戶費20元,密碼器280元。

            14.辦理稅務(wù)登記:領(lǐng)取執(zhí)照后,30日內(nèi)到當(dāng)?shù)囟悇?wù)局申請領(lǐng)取稅務(wù)登記證。一般的公司都需要辦理2種稅務(wù)登記證,即國稅和地稅。費用是各40元,共80元。

            15.請兼職會計:辦理稅務(wù)登記證時,必須有一個會計,因為稅務(wù)局要求提交的資料其中有一項是會計資格證和身份證。你可先請一個兼職會計,小公司剛開始請的兼職會計一般200元工資就可以了。

            16.申請領(lǐng)購發(fā)票:如果你的公司是銷售商品的,應(yīng)該到國稅去申請發(fā)票,如果是服務(wù)性質(zhì)的公司,則到地稅申領(lǐng)發(fā)票。 開始可先領(lǐng)購500元的發(fā)票。

            最后就開始營業(yè)了。

            四、注冊公司的費用:
            1、工商局工商名稱核準(zhǔn),40元
            2、公司辦公室房租6個月,6000元
            3、租房合同打印費5份15元,房產(chǎn)證復(fù)印件5張2.5元
            4、租房的印花稅12元
            5、下載公司章程的上網(wǎng)費2元,公司章程打印費15元
            6、刻法人私章20元
            7、會計師事務(wù)所的銀行詢征函10元
            8、銀行開立公司驗資戶開戶費20元
            9、會計師事務(wù)所辦理驗資報告500元
            10、工商局注冊公司手續(xù)費300元,信息卡120元
            11、公章2個120元,財務(wù)章1個60元
            12、技術(shù)監(jiān)督局辦理組織機構(gòu)代碼證148元
            13、銀行開立公司基本帳號開戶費20元、密碼器280元
            14、國稅稅務(wù)登記證60元,地稅稅務(wù)登記證60元
            15、兼職會計工資,200元
            16、申請領(lǐng)購發(fā)票,500元
            合計:8502.5元
            如果不算房租、會計工資、發(fā)票,則合計1802.5元。

            注冊資本最少3萬元。

            注冊登記費按注冊資本的0.08%(1000萬以內(nèi)),0.04%(1000萬以上的超過部分)收取

            營業(yè)稅:銷售商品的公司,按所開發(fā)票額的4%征收增殖稅;提供服務(wù)的公司,按所開發(fā)票額的5%征收營業(yè)稅。

            所得稅:對企業(yè)的純利潤征收18-33%的企業(yè)所得稅。
            利潤收入在3萬元(含3萬元)以下的稅率為18%,利潤收入在3萬元以上10萬元(含10萬元)的稅率為27%,10萬元以上的為33%。

            五、注冊公司的相關(guān)說明:

            1.注冊公司會不到半年時間,最快需要20天時間。地區(qū)不同注冊公司的費用也有所不同。

            2.要注冊一個公司,首先想好經(jīng)營什么,怎樣經(jīng)營好,再來注冊。要不,注冊了也沒有用,注冊了公司是需要很多成本的,不是一件“好玩”的事情。

            3.注冊個體簡單易辦;而注冊公司要有章程、合同,要驗資,程序挺多的。 在投入不是太多時,還是注冊個體為好。

            4.前期可行性分析調(diào)查,建議你自己認(rèn)真的考慮一下

            5.公司必須建立健全的會計制度,你可能擔(dān)心自己不會,怎么辦?剛開始成立的公司,業(yè)務(wù)少,對會計的工作量也非常小,你可以請一個兼職會計,每個月到你的公司幫你建帳,二、三天時間就夠了,給他200-500左右的工資即可。

            6.每個月1日-10日按時向稅務(wù)申報稅,即使沒有開展業(yè)務(wù)不需要繳稅,也要進行零申報,否則會被罰款的。罰款額度超過一天100元。營業(yè)執(zhí)照辦理下來后一個月內(nèi)必須辦理稅務(wù)登記。每年3-6月年定時年檢營業(yè)執(zhí)照。

            7.對企業(yè)所得稅,做帳很關(guān)鍵,如果帳面上你的利潤很多,那稅率就高。所以,平常的購買設(shè)備都要開發(fā)票,你吃飯、坐車的票都留起來,可以做為你的企業(yè)運作成本。

            8.營業(yè)稅是對營業(yè)額征稅,不管你賺沒有賺錢,只有發(fā)生了交易,開了發(fā)票,就要征稅;所得稅,是對利潤征稅,利潤就是營業(yè)額扣減各種成本后剩余的錢,只有賺了錢,才會征所得稅。

            9.有限責(zé)任公司可以注冊分公司。

            10.開辦費是指企業(yè)在籌建期間發(fā)生的費用,包括籌建期人員工資、辦公費、培訓(xùn)費、差旅費、印刷費、注冊登記費以及不計入固定資產(chǎn)和無形資產(chǎn)購建成本的匯兌損益和利息支出。籌建期是指企業(yè)被批準(zhǔn)籌建之日起至開始生產(chǎn)、經(jīng)營(包括試生產(chǎn)、試營業(yè))之日的期間。
            posted @ 2009-08-12 16:33 yanghaibao| 編輯 收藏

            2009年7月24日

             c++中的explicit關(guān)鍵字用來修飾類的構(gòu)造函數(shù),表明該構(gòu)造函數(shù)是顯式的,既然有"顯式"那么必然就有"隱式",那么什么是顯示而什么又是隱式的呢?

            如果c++類的構(gòu)造函數(shù)有一個參數(shù),那么在編譯的時候就會有一個缺省的轉(zhuǎn)換操作:將該構(gòu)造函數(shù)對應(yīng)數(shù)據(jù)類型的數(shù)據(jù)轉(zhuǎn)換為該類對象,如下面所示:
            class MyClass
            {
            public:
            MyClass( int num );
            }
            ....
            MyClass obj = 10; //ok,convert int to MyClass
            在上面的代碼中編譯器自動將整型轉(zhuǎn)換為MyClass類對象,實際上等同于下面的操作:
            MyClass temp(10);
            MyClass obj = temp;
            上面的所有的操作即是所謂的"隱式轉(zhuǎn)換"。

            如果要避免這種自動轉(zhuǎn)換的功能,我們該怎么做呢?嘿嘿這就是關(guān)鍵字explicit的作用了,將類的構(gòu)造函數(shù)聲明為"顯示",也就是在聲明構(gòu)造函數(shù)的時候前面添加上explicit即可,這樣就可以防止這種自動的轉(zhuǎn)換操作,如果我們修改上面的MyClass類的構(gòu)造函數(shù)為顯示的,那么下面的代碼就不能夠編譯通過了,如下所示:
            class MyClass
            {
            public:
            explicit MyClass( int num );
            }
            ....
            MyClass obj = 10; //err,can't non-explict convert

            class isbn_mismatch:public std::logic_error{
            public:
            explicit isbn_missmatch(const std::string &s):std:logic_error(s){}
            isbn_mismatch(const std::string &s,const std::string &lhs,const std::string &rhs):
            std::logic_error(s),left(lhs),right(rhs){}
            const std::string left,right;
            virtual ~isbn_mismatch() throw(){}
            };


            Sales_item& operator+(const Sales_item &lhs,const Sales_item rhs)
            {
            if(!lhs.same_isbn(rhs))
               throw isbn_mismatch("isbn missmatch",lhs.book(),rhs.book());
            Sales_item ret(lhs);
            ret+rhs;
            return ret;
            }


            Sales_item item1,item2,sum;
            while(cin>>item1>>item2)
            {
            try{
               sun=item1+item2;
            }catch(const isbn_mismatch &e)
            {
               cerr<<e.what()<<"left isbn is:"<<e.left<<"right isbn is:"<<e.right<<endl;
            }
            }

            用于用戶自定義類型的構(gòu)造函數(shù),指定它是默認(rèn)的構(gòu)造函數(shù),不可用于轉(zhuǎn)換構(gòu)造函數(shù).因為構(gòu)造函數(shù)有三種:1拷貝構(gòu)造函數(shù)2轉(zhuǎn)換構(gòu)造函數(shù)3一般的構(gòu)造函數(shù)(我自己的術(shù)語^_^)
            另:如果一個類或結(jié)構(gòu)存在多個構(gòu)造函數(shù)時,explicit 修飾的那個構(gòu)造函數(shù)就是默認(rèn)的

            class isbn_mismatch:public std::logic_error{
            public:
            explicit isbn_missmatch(const std::string &s):std:logic_error(s){}
            isbn_mismatch(const std::string &s,const std::string &lhs,const std::string &rhs):
            std::logic_error(s),left(lhs),right(rhs){}
            const std::string left,right;
            virtual ~isbn_mismatch() throw(){}
            };


            Sales_item& operator+(const Sales_item &lhs,const Sales_item rhs)
            {
            if(!lhs.same_isbn(rhs))
               throw isbn_mismatch("isbn missmatch",lhs.book(),rhs.book());
            Sales_item ret(lhs);
            ret+rhs;
            return ret;
            }


            Sales_item item1,item2,sum;
            while(cin>>item1>>item2)
            {
            try{
               sun=item1+item2;
            }catch(const isbn_mismatch &e)
            {
               cerr<<e.what()<<"left isbn is:"<<e.left<<"right isbn is:"<<e.right<<endl;
            }
            }

             

            這個 《ANSI/ISO C++ Professional Programmer's Handbook 》是這樣說的

            explicit Constructors
            A constructor that takes a single argument is, by default, an implicit conversion operator, which converts its argument to
            an object of its class (see also Chapter 3, "Operator Overloading"). Examine the following concrete example:
            class string
            {
            private:
            int size;
            int capacity;
            char *buff;
            public:
            string();
            string(int size); // constructor and implicit conversion operator
            string(const char *); // constructor and implicit conversion operator
            ~string();
            };
            Class string has three constructors: a default constructor, a constructor that takes int, and a constructor that
            constructs a string from const char *. The second constructor is used to create an empty string object with an
            initial preallocated buffer at the specified size. However, in the case of class string, the automatic conversion is
            dubious. Converting an int into a string object doesn't make sense, although this is exactly what this constructor does.

            Consider the following:
            int main()
            {
            string s = "hello"; //OK, convert a C-string into a string object
            int ns = 0;
            s = 1; // 1 oops, programmer intended to write ns = 1,
            }
            In the expression s= 1;, the programmer simply mistyped the name of the variable ns, typing s instead. Normally,
            the compiler detects the incompatible types and issues an error message. However, before ruling it out, the compiler first
            searches for a user-defined conversion that allows this expression; indeed, it finds the constructor that takes int.
            Consequently, the compiler interprets the expression s= 1; as if the programmer had written
            s = string(1);
            You might encounter a similar problem when calling a function that takes a string argument. The following example
            can either be a cryptic coding style or simply a programmer's typographical error. However, due to the implicit
            conversion constructor of class string, it will pass unnoticed:
            int f(string s);
            int main()
            {
            f(1); // without a an explicit constructor,
            //this call is expanded into: f ( string(1) );
            //was that intentional or merely a programmer's typo?
            }
            'In order to avoid such implicit conversions, a constructor that takes one argument needs to be declared explicit:
            class string
            {
            //...
            public:
            explicit string(int size); // block implicit conversion
            string(const char *); //implicit conversion
            ~string();
            };
            An explicit constructor does not behave as an implicit conversion operator, which enables the compiler to catch the
            typographical error this time:
            int main()
            {
            string s = "hello"; //OK, convert a C-string into a string object
            int ns = 0;
            s = 1; // compile time error ; this time the compiler catches the typo
            }
            Why aren't all constructors automatically declared explicit? Under some conditions, the automatic type conversion is
            useful and well behaved. A good example of this is the third constructor of string:
            string(const char *);

            The implicit type conversion of const char * to a string object enables its users to write the following:
            string s;
            s = "Hello";
            The compiler implicitly transforms this into
            string s;
            //pseudo C++ code:
            s = string ("Hello"); //create a temporary and assign it to s
            On the other hand, if you declare this constructor explicit, you have to use explicit type conversion:
            class string
            {
            //...
            public:
            explicit string(const char *);
            };
            int main()
            {
            string s;
            s = string("Hello"); //explicit conversion now required
            return 0;
            }
            Extensive amounts of legacy C++ code rely on the implicit conversion of constructors. The C++ Standardization
            committee was aware of that. In order to not make existing code break, the implicit conversion was retained. However, a
            new keyword, explicit, was introduced to the languageto enable the programmer to block the implicit conversion
            when it is undesirable. As a rule, a constructor that can be invoked with a single argument needs to be declared
            explicit. When the implicit type conversion is intentional and well behaved, the constructor can be used as an
            implicit conversion operator.

            網(wǎng)上找的講的最好的貼:

            C++ 中 explicit 關(guān)鍵字的作用
            在 C++ 中, 如果一個類有只有一個參數(shù)的構(gòu)造函數(shù),C++ 允許一種特殊的聲明類變量的方式。在這種情況下,可以直接將一個對應(yīng)于構(gòu)造函數(shù)參數(shù)類型的數(shù)據(jù)直接賦值給類變量,編譯器在編譯時會自動進行類型轉(zhuǎn)換,將對應(yīng)于構(gòu)造函數(shù)參數(shù)類型的數(shù)據(jù)轉(zhuǎn)換為類的對象。 如果在構(gòu)造函數(shù)前加上 explicit 修飾詞, 則會禁止這種自動轉(zhuǎn)換,在這種情況下,即使將對應(yīng)于構(gòu)造函數(shù)參數(shù)類型的數(shù)據(jù)直接賦值給類變量,編譯器也會報錯。

            下面以具體實例來說明。

            建立people.cpp 文件,然后輸入下列內(nèi)容:

            class People
            {
            public:
            int age;
            explicit People (int a)
             {
              age=a;
             }
            };
            void foo ( void )
            {
             People p1(10);  //方式一
             People* p_p2=new People(10); //方式二
             People p3=10; //方式三
            }
            這段 C++ 程序定義了一個類 people ,包含一個構(gòu)造函數(shù), 這個構(gòu)造函數(shù)只包含一個整形參數(shù) a ,可用于在構(gòu)造類時初始化 age 變量。

            然后定義了一個函數(shù)foo,在這個函數(shù)中我們用三種方式分別創(chuàng)建了三個10歲的“人”。第一種是最一般的類變量聲明方式。第二種方式其實是聲明了一個people類的指針變量,然后在堆中動態(tài)創(chuàng)建了一個people實例,并把這個實例的地址賦值給了p_p2。第三種方式就是我們所說的特殊方式,為什么說特殊呢?我們都知道,C/C++是一種強類型語言,不同的數(shù)據(jù)類型是不能隨意轉(zhuǎn)換的,如果要進行類型轉(zhuǎn)換,必須進行顯式強制類型轉(zhuǎn)換,而這里,沒有進行任何顯式的轉(zhuǎn)換,直接將一個整型數(shù)據(jù)賦值給了類變量p3。

            因此,可以說,這里進行了一次隱式類型轉(zhuǎn)換,編譯器自動將對應(yīng)于構(gòu)造函數(shù)參數(shù)類型的數(shù)據(jù)轉(zhuǎn)換為了該類的對象,因此方式三經(jīng)編譯器自動轉(zhuǎn)換后和方式一最終的實現(xiàn)方式是一樣的。

            不相信? 耳聽為虛,眼見為實,讓我們看看底層的實現(xiàn)方式。

            為了更容易比較方式一和方式三的實現(xiàn)方式,我們對上面的代碼作一點修改,去除方式二:

            void foo ( void )
            {
             People p1(10);  //方式一
             People p3=10; //方式三
            }
            去除方式二的原因是方式二是在堆上動態(tài)創(chuàng)建類實例,因此會有一些額外代碼影響分析。修改完成后,用下列命令編譯 people.cpp

            $ gcc -S people.cpp

            "-S"選項是GCC輸出匯編代碼。命令執(zhí)行后,默認(rèn)生成people.s。 關(guān)鍵部分內(nèi)容如下:

            .globl _Z3foov
            .type _Z3foov, @function
            _Z3foov:
            .LFB5:
             pushl %ebp
            .LCFI2:
             movl %esp, %ebp
            .LCFI3:
             subl $24, %esp
            .LCFI4:
             movl $10, 4(%esp)
             leal -4(%ebp), %eax
             movl %eax, (%esp)
             call _ZN6PeopleC1Ei
             movl $10, 4(%esp)
             leal -8(%ebp), %eax
             movl %eax, (%esp)
             call _ZN6PeopleC1Ei
             leave
             ret

            看“.LCFI4” 行后面的東西,1-4行和5-8行幾乎一模一樣,1-4行即為方式一的匯編代碼,5-8即為方式三的匯編代碼。 細(xì)心的你可能發(fā)現(xiàn)2和6行有所不同,一個是 -4(%ebp) 而另一個一個是 -8(%ebp) ,這分別為類變量P1和P3的地址。

            對于不可隨意進行類型轉(zhuǎn)換的強類型語言C/C++來說, 這可以說是C++的一個特性。哦,今天好像不是要說C++的特性,而是要知道explicit關(guān)鍵字的作用?

            explicit關(guān)鍵字到底是什么作用呢? 它的作用就是禁止這個特性。如文章一開始而言,凡是用explicit關(guān)鍵字修飾的構(gòu)造函數(shù),編譯時就不會進行自動轉(zhuǎn)換,而會報錯。

            讓我們看看吧! 修改代碼:

            class People
            {
            public:
            int age;
            explicit People (int a)
             {
              age=a;
             }
            };
            然后再編譯:
            $ gcc -S people.cpp
            編譯器立馬報錯:
            people.cpp: In function ‘void foo()’:
            people.cpp:23: 錯誤:請求從 ‘int’ 轉(zhuǎn)換到非標(biāo)量類型 ‘People’
            posted @ 2009-07-24 16:49 yanghaibao| 編輯 收藏

            2009年7月22日

            軟件設(shè)計中會碰到這樣的關(guān)系:一個對象依賴于另一個對象,必須根據(jù)后者的狀態(tài)更新自己的狀態(tài),可以把后者稱作目標(biāo)對象,前者稱作觀察者對象。不但觀察者依賴于目標(biāo),當(dāng)目標(biāo)的狀態(tài)改變時也要通知觀察者,這就出現(xiàn)了雙向的依賴。兩個對象互相依賴的后果是它們必須一起復(fù)用。如果一個目標(biāo)有多個觀察者,那么目標(biāo)也依賴所有觀察者,從而目標(biāo)對象無法獨立復(fù)用。如何消除目標(biāo)和觀察者之間的互相依賴呢?觀察者模式幫助我們解決這個問題。

            觀察者模式把目標(biāo)對觀察者的依賴進行抽象:使目標(biāo)只知道自己有若干觀察者,但不知道這些觀察者具體是誰,可能有多少個;當(dāng)目標(biāo)狀態(tài)改變時只要給這些觀察者一個通知,不必作更多的事情。這樣目標(biāo)對觀察者的依賴就達到了抽象和最小,而目標(biāo)對具體觀察者的依賴被解除了。

            類圖如下:

            Observer.JPG

            Subject 對象保存一個Observer引用的列表,當(dāng)我們讓一個ConcreteObserver對象觀察Subject對象時,調(diào)用后者的Attach()方法,將前者的引用加入該列表中。當(dāng)Subject對象狀態(tài)改變時,它調(diào)用自身的Notify方法,該方法調(diào)用列表中每一個Observer的 Update()方法。一個ConcreteObserver只要重定義Update()就能收到通知,作為對通知的響應(yīng),Update()調(diào)用 Subject對象的getStatus()獲取數(shù)據(jù),然后更新自身。當(dāng)不需要繼續(xù)觀察時,ConcreteObserver對象調(diào)用Subject對象的Detach()方法,其引用被從列表中移除。

            解除目標(biāo)對具體觀察者的依賴以后,很容易增加新的具體觀察者,因為不受依賴的方面就可以自由變化;而目標(biāo)也可以獨立地復(fù)用,因為無所依賴的方面就可以不受影響。

            以上主要考慮了一個目標(biāo)有多個觀察者的情況,我們設(shè)法解除了目標(biāo)對具體觀察者的依賴,使具體觀察者的種類和數(shù)目容易改變。有時候一個觀察者觀察多個目標(biāo)也是有意義的,在前面的類圖中,觀察者對具體目標(biāo)的依賴仍然存在,因此無法適應(yīng)目標(biāo)方面的變化。怎樣抽象這種依賴呢?使觀察者只知道若干個目標(biāo)會向自己發(fā)出通知,而不知道這些目標(biāo)具體是誰,可能有多少個;在目標(biāo)向觀察者發(fā)送通知時,將一個自身的引用作為參數(shù),然后觀察者調(diào)用其抽象方法就可以獲得目標(biāo)狀態(tài)。這就使得觀察者對目標(biāo)的依賴是抽象的,觀察者對具體目標(biāo)的依賴被解除了。

            類圖如下:

            Observer2.JPG

            參考資料:

            1.《設(shè)計模式-可復(fù)用面向?qū)ο筌浖幕A(chǔ)》/Erich Gamma等著,李英軍等譯 機械工業(yè)出版社
            posted @ 2009-07-22 23:19 yanghaibao| 編輯 收藏
            起學(xué)習(xí) NT/2000方法:

            #include <windows.h>
            #include <conio.h>
            #include <stdio.h>

            #define SystemBasicInformation 0
            #define SystemPerformanceInformation 2
            #define SystemTimeInformation 3

            #define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 (double)((x).LowPart))

            typedef struct
            {
            DWORD dwUnknown1;
            ULONG uKeMaximumIncrement;
            ULONG uPageSize;
            ULONG uMmNumberOfPhysicalPages;
            ULONG uMmLowestPhysicalPage;
            ULONG uMmHighestPhysicalPage;
            ULONG uAllocationGranularity;
            PVOID pLowestUserAddress;
            PVOID pMmHighestUserAddress;
            ULONG uKeActiveProcessors;
            BYTE bKeNumberProcessors;
            BYTE bUnknown2;
            WORD wUnknown3;
            } SYSTEM_BASIC_INFORMATION;

            typedef struct
            {
            LARGE_INTEGER liIdleTime;
            DWORD dwSpare[76];
            } SYSTEM_PERFORMANCE_INFORMATION;

            typedef struct
            {
            LARGE_INTEGER liKeBootTime;
            LARGE_INTEGER liKeSystemTime;
            LARGE_INTEGER liExpTimeZoneBias;
            ULONG uCurrentTimeZoneId;
            DWORD dwReserved;
            } SYSTEM_TIME_INFORMATION;


            // ntdll!NtQuerySystemInformation (NT specific!)
            //
            // The function copies the system information of the
            // specified type into a buffer
            //
            // NTSYSAPI
            // NTSTATUS
            // NTAPI
            // NtQuerySystemInformation(
            // IN UINT SystemInformationClass, // information type
            // OUT PVOID SystemInformation, // pointer to buffer
            // IN ULONG SystemInformationLength, // buffer size in bytes
            // OUT PULONG ReturnLength OPTIONAL // pointer to a 32-bit
            // // variable that receives
            // // the number of bytes
            // // written to the buffer
            // );
            typedef LONG (WINAPI *PROCNTQSI)(UINT,PVOID,ULONG,PULONG);

            PROCNTQSI NtQuerySystemInformation;


            void main(void)
            {
            SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
            SYSTEM_TIME_INFORMATION SysTimeInfo;
            SYSTEM_BASIC_INFORMATION SysBaseInfo;
            double dbIdleTime;
            double dbSystemTime;
            LONG status;
            LARGE_INTEGER liOldIdleTime = {0,0};
            LARGE_INTEGER liOldSystemTime = {0,0};

            NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(
            GetModuleHandle("ntdll"),
            "NtQuerySystemInformation"
            );

            if (!NtQuerySystemInformation)
            return;

            // get number of processors in the system
            status = NtQuerySystemInformation(SystemBasicInformation,&SysBaseInfo,sizeof(SysBaseInfo),NULL);
            if (status != NO_ERROR)
            return;

            printf("\nCPU Usage (press any key to exit): ");
            while(!_kbhit())
            {
            // get new system time
            status = NtQuerySystemInformation(SystemTimeInformation,&SysTimeInfo,sizeof(SysTimeInfo),0);
            if (status!=NO_ERROR)
            return;

            // get new CPU's idle time
            status = NtQuerySystemInformation(SystemPerformanceInformation,&SysPerfInfo,sizeof(SysPerfInfo),NULL);
            if (status != NO_ERROR)
            return;

            // 本文轉(zhuǎn)自 C Builder研究 - http://www.ccrun.com/article.asp?i=424&d=7jw23a
            // if it's a first call - skip it
            if (liOldIdleTime.QuadPart != 0)
            {
            // CurrentValue = NewValue - OldValue
            dbIdleTime = Li2Double(SysPerfInfo.liIdleTime) - Li2Double(liOldIdleTime);
            dbSystemTime = Li2Double(SysTimeInfo.liKeSystemTime) - Li2Double(liOldSystemTime);

            // CurrentCpuIdle = IdleTime / SystemTime
            dbIdleTime = dbIdleTime / dbSystemTime;

            // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
            dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SysBaseInfo.bKeNumberProcessors 0.5;

            printf("\b\b\b\b=%%",(UINT)dbIdleTime);
            }

            // store new CPU's idle and system time
            liOldIdleTime = SysPerfInfo.liIdleTime;
            liOldSystemTime = SysTimeInfo.liKeSystemTime;

            // wait one second
            Sleep(1000);
            }
            printf("\n");
            }

            //-------------------------------------------------------------

            W9X:
            #include <windows.h>
            #include <conio.h>
            #include <stdio.h>

            void main(void)
            {
            HKEY hkey;
            DWORD dwDataSize;
            DWORD dwType;
            DWORD dwCpuUsage;

            // starting the counter
            if ( RegOpenKeyEx( HKEY_DYN_DATA,
            "PerfStats\\StartStat",
            0,KEY_ALL_ACCESS,
            &hkey ) != ERROR_SUCCESS)
            return;

            dwDataSize=sizeof(DWORD);
            RegQueryValueEx( hkey,
            "KERNEL\\CPUUsage",
            NULL,&dwType,
            (LPBYTE)&dwCpuUsage,
            &dwDataSize );

            RegCloseKey(hkey);

            // geting current counter's value
            if ( RegOpenKeyEx( HKEY_DYN_DATA,
            "PerfStats\\StatData",
            0,KEY_READ,
            &hkey ) != ERROR_SUCCESS)
            return;

            printf("\nCPU Usage (press any key to exit): ");
            while(!_kbhit())
            {
            dwDataSize=sizeof(DWORD);
            RegQueryValueEx( hkey,
            "KERNEL\\CPUUsage",
            NULL,&dwType,
            (LPBYTE)&dwCpuUsage,
            &dwDataSize );
            Sleep(500);
            printf("\b\b\b\b=%%",dwCpuUsage);
            }
            printf("\n");

            RegCloseKey(hkey);

            // stoping the counter
            if ( RegOpenKeyEx( HKEY_DYN_DATA,
            "PerfStats\\StopStat",
            0,KEY_ALL_ACCESS,
            &hkey ) != ERROR_SUCCESS)
            return;

            dwDataSize=sizeof(DWORD);
            RegQueryValueEx( hkey,
            "KERNEL\\CPUUsage",
            NULL,&dwType,
            (LPBYTE)&dwCpuUsage,
            &dwDataSize );

            RegCloseKey(hkey);
            } NT/2000方法:

            #include <windows.h>
            #include <conio.h>
            #include <stdio.h>

            #define SystemBasicInformation 0
            #define SystemPerformanceInformation 2
            #define SystemTimeInformation 3

            #define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 (double)((x).LowPart))

            typedef struct
            {
            DWORD dwUnknown1;
            ULONG uKeMaximumIncrement;
            ULONG uPageSize;
            ULONG uMmNumberOfPhysicalPages;
            ULONG uMmLowestPhysicalPage;
            ULONG uMmHighestPhysicalPage;
            ULONG uAllocationGranularity;
            PVOID pLowestUserAddress;
            PVOID pMmHighestUserAddress;
            ULONG uKeActiveProcessors;
            BYTE bKeNumberProcessors;
            BYTE bUnknown2;
            WORD wUnknown3;
            } SYSTEM_BASIC_INFORMATION;

            typedef struct
            {
            LARGE_INTEGER liIdleTime;
            DWORD dwSpare[76];
            } SYSTEM_PERFORMANCE_INFORMATION;

            typedef struct
            {
            LARGE_INTEGER liKeBootTime;
            LARGE_INTEGER liKeSystemTime;
            LARGE_INTEGER liExpTimeZoneBias;
            ULONG uCurrentTimeZoneId;
            DWORD dwReserved;
            } SYSTEM_TIME_INFORMATION;


            // ntdll!NtQuerySystemInformation (NT specific!)
            //
            // The function copies the system information of the
            // specified type into a buffer
            //
            // NTSYSAPI
            // NTSTATUS
            // NTAPI
            // NtQuerySystemInformation(
            // IN UINT SystemInformationClass, // information type
            // OUT PVOID SystemInformation, // pointer to buffer
            // IN ULONG SystemInformationLength, // buffer size in bytes
            // OUT PULONG ReturnLength OPTIONAL // pointer to a 32-bit
            // // variable that receives
            // // the number of bytes
            // // written to the buffer
            // );
            typedef LONG (WINAPI *PROCNTQSI)(UINT,PVOID,ULONG,PULONG);

            PROCNTQSI NtQuerySystemInformation;


            void main(void)
            {
            SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
            SYSTEM_TIME_INFORMATION SysTimeInfo;
            SYSTEM_BASIC_INFORMATION SysBaseInfo;
            double dbIdleTime;
            double dbSystemTime;
            LONG status;
            LARGE_INTEGER liOldIdleTime = {0,0};
            LARGE_INTEGER liOldSystemTime = {0,0};

            NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(
            GetModuleHandle("ntdll"),
            "NtQuerySystemInformation"
            );

            if (!NtQuerySystemInformation)
            return;

            // get number of processors in the system
            status = NtQuerySystemInformation(SystemBasicInformation,&SysBaseInfo,sizeof(SysBaseInfo),NULL);
            if (status != NO_ERROR)
            return;

            printf("\nCPU Usage (press any key to exit): ");
            while(!_kbhit())
            {
            // get new system time
            status = NtQuerySystemInformation(SystemTimeInformation,&SysTimeInfo,sizeof(SysTimeInfo),0);
            if (status!=NO_ERROR)
            return;

            // get new CPU's idle time
            status = NtQuerySystemInformation(SystemPerformanceInformation,&SysPerfInfo,sizeof(SysPerfInfo),NULL);
            if (status != NO_ERROR)
            return;

            // 本文轉(zhuǎn)自 C Builder研究 - http://www.ccrun.com/article.asp?i=424&d=7jw23a
            // if it's a first call - skip it
            if (liOldIdleTime.QuadPart != 0)
            {
            // CurrentValue = NewValue - OldValue
            dbIdleTime = Li2Double(SysPerfInfo.liIdleTime) - Li2Double(liOldIdleTime);
            dbSystemTime = Li2Double(SysTimeInfo.liKeSystemTime) - Li2Double(liOldSystemTime);

            // CurrentCpuIdle = IdleTime / SystemTime
            dbIdleTime = dbIdleTime / dbSystemTime;

            // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
            dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SysBaseInfo.bKeNumberProcessors 0.5;

            printf("\b\b\b\b=%%",(UINT)dbIdleTime);
            }

            // store new CPU's idle and system time
            liOldIdleTime = SysPerfInfo.liIdleTime;
            liOldSystemTime = SysTimeInfo.liKeSystemTime;

            // wait one second
            Sleep(1000);
            }
            printf("\n");
            }

            //-------------------------------------------------------------

            W9X:
            #include <windows.h>
            #include <conio.h>
            #include <stdio.h>

            void main(void)
            {
            HKEY hkey;
            DWORD dwDataSize;
            DWORD dwType;
            DWORD dwCpuUsage;

            // starting the counter
            if ( RegOpenKeyEx( HKEY_DYN_DATA,
            "PerfStats\\StartStat",
            0,KEY_ALL_ACCESS,
            &hkey ) != ERROR_SUCCESS)
            return;

            dwDataSize=sizeof(DWORD);
            RegQueryValueEx( hkey,
            "KERNEL\\CPUUsage",
            NULL,&dwType,
            (LPBYTE)&dwCpuUsage,
            &dwDataSize );

            RegCloseKey(hkey);

            // geting current counter's value
            if ( RegOpenKeyEx( HKEY_DYN_DATA,
            "PerfStats\\StatData",
            0,KEY_READ,
            &hkey ) != ERROR_SUCCESS)
            return;

            printf("\nCPU Usage (press any key to exit): ");
            while(!_kbhit())
            {
            dwDataSize=sizeof(DWORD);
            RegQueryValueEx( hkey,
            "KERNEL\\CPUUsage",
            NULL,&dwType,
            (LPBYTE)&dwCpuUsage,
            &dwDataSize );
            Sleep(500);
            printf("\b\b\b\b=%%",dwCpuUsage);
            }
            printf("\n");

            RegCloseKey(hkey);

            // stoping the counter
            if ( RegOpenKeyEx( HKEY_DYN_DATA,
            "PerfStats\\StopStat",
            0,KEY_ALL_ACCESS,
            &hkey ) != ERROR_SUCCESS)
            return;

            dwDataSize=sizeof(DWORD);
            RegQueryValueEx( hkey,
            "KERNEL\\CPUUsage",
            NULL,&dwType,
            (LPBYTE)&dwCpuUsage,
            &dwDataSize );

            RegCloseKey(hkey);
            }
            posted @ 2009-07-22 18:11 yanghaibao| 編輯 收藏

            Introduction

            One of the interesting features I found in C# is a ?Events and Delegates? concept. The idea is good but not new in Object Oriented Programming, it is one of the most frequently used concepts in programming, sometimes referred to as ?Observer? or ?Document/View? design pattern. Classical formulation of it could be found in ?Design Patterns, Elements of Reusable Object Oriented Software? by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (The Gang of Four).

            This concept is used when you want some information stored in one object, called ?model? (subject) to be watched by others, called ?views? (observers). Each time when information is changed in the ?model?, ?views? attached to the model should receive notification and update there states accordingly to the changed ?model?.

            Classical implementation described in ?Design Patterns?:

            As it is seen from the class diagram, concrete models should be derived from Subject class and views from Observer. Any time the state of Subject is changed, it calls notify method which notifies all observers attached to the Subject.

            Collapse Copy Code
            void Subject::notify()
            {
            for(int i=0; i<observes.size(); i++)
            observers[i]->update();
            }

            In many applications, this straightforward implementation is good enough, but things are getting ugly when you have different kinds of changes in the ?subject? and you want to pass different types of parameters to the ?views?.

            One of the examples for complex ?Model?/?View? relations is a GUI control attached to its processing function. Each time the control?s state is changed, process function is called with parameters indicating new state of the control.

            These kinds of problems are solved in C# by the introduction of ?Events and Delegates? concept. The resolution of the problem is easier in C#, because all classes are inherited from the same ?object? class.

            At the beginning, I thought why we do not have this nice ?Events and Delegates? thing in standard C++, but then I came to the conclusion that C++ does not need it.

            C++ language is powerful enough to express ?Events? and ?Delegates? concept in terms of already existing primitives. Proposed design makes it possible to "connect" different methods with different number of parameters belonging to unrelated classes to the ?model?.

            The keys for this solution are C++ templates (generic types) and pointes to member functions.

            Using Code

            Suppose we have a class MySubject that has internal information connected to different views, it produces three different types of events called int_event, double_event and triple_event with different types and numbers of parameters.

            Collapse Copy Code
            class MySubject
            {
            public:
            CppEvent1<bool,int> int_event;
            CppEvent2<bool,double,int> double_event;
            CppEvent3<bool,double,int,const char*> triple_event;
            void submit_int()
            {
            int_event.notify(1);
            }
            void submit_double()
            {
            double_event.notify(10.5,100);
            }
            void submit_triple()
            {
            triple_event.notify(10.5,100,"Oh ye");
            }
            };

            Views represented by MyListener1 and MyListener2 are unrelated. The only requirement is for callback (delegate) methods to have parameters signature similar to corresponding CppEvent.

            Collapse Copy Code
            class MyListener1
            {
            public:
            bool update_int(int p)
            {
            Console::WriteLine(S"int update listener 1");
            return true;
            }
            bool update_double(double p,int p1)
            {
            Console::WriteLine(S"double update listener 1");
            return true;
            }
            bool update_triple(double p,int p1,const char* str)
            {
            Console::WriteLine(S"triple update listener 1");
            return true;
            }
            };
            class MyListener2
            {
            public:
            bool fun(int p)
            {
            Console::WriteLine(S"int update listener 2");
            return true;
            }
            };

            The final step is to create viewers MyListener1 and MyListener2 and connect their member functions to corresponding events in MySubject model.

            Collapse Copy Code
            int main(void)
            {
            // create listeners (viewers)
                MyListener1* listener1 = new MyListener1;
            MyListener2* listener2 = new MyListener2;
            // create model
                MySubject subject;
            // connect different viewers to different events of the model
                CppEventHandler h1 = subject.int_event.attach(listener1,
            &MyListener1::update_int);
            CppEventHandler h2 = subject.int_event.attach(listener2,
            &MyListener2::fun);
            CppEventHandler h3 = subject.double_event.attach(listener1,
            &MyListener1::update_double);
            CppEventHandler h4 = subject.triple_event.attach(listener1,
            &MyListener1::update_triple);
            // generate events
                subject.submit_int();
            subject.submit_double();
            subject.submit_triple();
            // detach handlers
                subject.int_event.detach(h1);
            subject.int_event.detach(h2);
            subject.double_event.detach(h3);
            subject.triple_event.detach(h4);
            return 0;
            }

            Resulting output is:

            Collapse Copy Code
            > int update listener 1
            > int update listener 2
            > double update listener 1
            > triple update listener 1

            Implementation

            First of all, if we want to attach different types of event handles (member functions with same types of parameters from different classes) to the same event, we should provide common base for them. We use templates to make it generic for any combination of parameter types in ?delegate? or call back method. There are different event types for every number of arguments in callback function.

            Collapse Copy Code
            // Event handler base for delegate with 1 parameter
            template <typename ReturnT,typename ParamT>
            class EventHandlerBase1
            {
            public:
            virtual ReturnT notify(ParamT param) = 0;
            };

            Specific type of member function pointer within a pointer to the object is stored in the derived class.

            Collapse Copy Code
            template <typename ListenerT,typename ReturnT,typename ParamT>
            class EventHandler1 : public EventHandlerBase1<ReturnT,ParamT>
            {
            typedef ReturnT (ListenerT::*PtrMember)(ParamT);
            ListenerT* m_object;
            PtrMember m_member;
            public:
            EventHandler1(ListenerT* object, PtrMember member)
            : m_object(object), m_member(member)
            {}
            ReturnT notify(ParamT param)
            {
            return (m_object->*m_member)(param);
            }
            };

            Event class stores map of event handlers and notifies all of them when notify method is called. Detach method is used to release handler from the map.

            Collapse Copy Code
            template <typename ReturnT,typename ParamT>
            class CppEvent1
            {
            typedef std::map<int,EventHandlerBase1<ReturnT,ParamT> *> HandlersMap;
            HandlersMap m_handlers;
            int m_count;
            public:
            CppEvent1()
            : m_count(0) {}
            template <typename ListenerT>
            CppEventHandler attach(ListenerT* object,ReturnT (ListenerT::*member)(ParamT))
            {
            typedef ReturnT (ListenerT::*PtrMember)(ParamT);
            m_handlers[m_count] = (new EventHandler1<ListenerT,
            ReturnT,ParamT>(object,member));
            m_count++;
            return m_count-1;
            }
            bool detach(CppEventHandler id)
            {
            HandlersMap::iterator it = m_handlers.find(id);
            if(it == m_handlers.end())
            return false;
            delete it->second;
            m_handlers.erase(it);
            return true;
            }
            ReturnT notify(ParamT param)
            {
            HandlersMap::iterator it = m_handlers.begin();
            for(; it != m_handlers.end(); it++)
            {
            it->second->notify(param);
            }
            return true;
            }
            };

            Comments

            This implementation is quite similar to those in the article ?Emulating C# delegates in Standard C++?. I found out it after I already wrote the article. Actually, the fact that we have a similar way to deal with the problem means that it?s a very intuitive solution for this kind of problem in C++. An advantage of the current implementation is that it supports different number of arguments, so any member function of any class could be a callback (delegate). Probably to have this thing as a part of standard library is a good thing, but even if it?s not a part of the standard, you can use it as it is. This implementation is restricted to events up to 3 parameters, it can be easily extended to other numbers by just rewriting it with different number of parameters (see code for details).

            License

            posted @ 2009-07-22 14:51 yanghaibao| 編輯 收藏
            作用:

            定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。

            UML結(jié)構(gòu)圖:

             

            解析:

            Observer模式定義的是一種一對多的關(guān)系,這里的一就是圖中的Subject類,而多則是Obesrver類,當(dāng)Subject類的狀態(tài)發(fā)生變化的時候通知與之對應(yīng)的Obesrver類們也去相應(yīng)的更新狀態(tài),同時支持動態(tài)的添加和刪除Observer對象的功能。Obesrver模式的實現(xiàn)要點是,第一一般subject類都是采用鏈表等容器來存放Observer對象,第二抽取出Observer對象的一些公共的屬性形成Observer基類,而Subject中保存的則是Observer類對象的指針,這樣就使Subject和具體的Observer實現(xiàn)了解耦,也就是Subject不需要去關(guān)心到底是哪個Observer對放進了自己的容器中。生活中有很多例子可以看做是Observer模式的運用,比方說,一個班有一個班主任(Subject),他管理手下的一幫學(xué)生(Observer),當(dāng)班里有一些事情發(fā)生需要通知學(xué)生的時候,班主任要做的不是逐個學(xué)生挨個的通知而是把學(xué)生召集起來一起通知,實現(xiàn)了班主任和具體學(xué)生的關(guān)系解耦。

            實現(xiàn):

            1)Observer.h

            /**//********************************************************************
                created:    2006/07/20
                filename:     Observer.h
                author:        李創(chuàng)
                            http://www.shnenglu.com/converse/

                purpose:    Observer模式的演示代碼
            *********************************************************************/

            #ifndef OBSERVER_H
            #define OBSERVER_H

            #include <list>

            typedef int STATE;

            class Observer;

            // Subject抽象基類,只需要知道Observer基類的聲明就可以了
            class Subject
            {
            public:
                Subject() : m_nSubjectState(-1){}
                virtual ~Subject();

                void Notify();                            // 通知對象改變狀態(tài)
                void Attach(Observer *pObserver);        // 新增對象
                void Detach(Observer *pObserver);        // 刪除對象

                // 虛函數(shù),提供默認(rèn)的實現(xiàn),派生類可以自己實現(xiàn)來覆蓋基類的實現(xiàn)
                virtual void    SetState(STATE nState);    // 設(shè)置狀態(tài)
                virtual STATE    GetState();        // 得到狀態(tài)

            protected:
                STATE m_nSubjectState;                    // 模擬保存Subject狀態(tài)的變量
                std::list<Observer*>    m_ListObserver;    // 保存Observer指針的鏈表
            };

            // Observer抽象基類
            class Observer
            {
            public:
                Observer() : m_nObserverState(-1){}
                virtual ~Observer(){}

                // 純虛函數(shù),各個派生類可能有不同的實現(xiàn)
                // 通知Observer狀態(tài)發(fā)生了變化
                virtual void Update(Subject* pSubject) = 0;

            protected:
                STATE m_nObserverState;                    // 模擬保存Observer狀態(tài)的變量
            };

            // ConcreateSubject類,派生在Subject類
            class ConcreateSubject
                : public Subject
            {
            public:
                ConcreateSubject() : Subject(){}
                virtual ~ConcreateSubject(){}

                // 派生類自己實現(xiàn)來覆蓋基類的實現(xiàn)
                virtual void    SetState(STATE nState);    // 設(shè)置狀態(tài)
                virtual STATE    GetState();        // 得到狀態(tài)

            };

            // ConcreateObserver類派生自O(shè)bserver
            class ConcreateObserver
                : public Observer
            {
            public:
                ConcreateObserver() : Observer(){}
                virtual ~ConcreateObserver(){}

                // 虛函數(shù),實現(xiàn)基類提供的接口
                virtual void Update(Subject* pSubject);
            };

            #endif

             

            2)Observer.cpp

            /**//********************************************************************
                created:    2006/07/20
                filename:     Observer.cpp
                author:        李創(chuàng)
                            http://www.shnenglu.com/converse/

                purpose:    Observer模式的演示代碼
            *********************************************************************/

            #include "Observer.h"
            #include <iostream>
            #include <algorithm>

            /**//* --------------------------------------------------------------------
            |    Subject類成員函數(shù)的實現(xiàn)
            |
             ----------------------------------------------------------------------*/

            void Subject::Attach(Observer *pObserver)
            {
                std::cout << "Attach an Observern";

                m_ListObserver.push_back(pObserver);
            }

            void Subject::Detach(Observer *pObserver)
            {
                std::list<Observer*>::iterator iter;
                iter = std::find(m_ListObserver.begin(), m_ListObserver.end(), pObserver);

                if (m_ListObserver.end() != iter)
                {
                    m_ListObserver.erase(iter);
                }

                std::cout << "Detach an Observern";
            }

            void Subject::Notify()
            {
                std::cout << "Notify Observers''s Staten";

                std::list<Observer*>::iterator iter1, iter2;

                for (iter1 = m_ListObserver.begin(), iter2 = m_ListObserver.end();
                     iter1 != iter2;
                     ++iter1)
                {
                    (*iter1)->Update(this);
                }
            }

            void Subject::SetState(STATE nState)
            {
                std::cout << "SetState By Subjectn";
                m_nSubjectState = nState;
            }

            STATE Subject::GetState()
            {
                std::cout << "GetState By Subjectn";
                return m_nSubjectState;
            }

            Subject::~Subject()
            {
                std::list<Observer*>::iterator iter1, iter2, temp;

                for (iter1 = m_ListObserver.begin(), iter2 = m_ListObserver.end();
                    iter1 != iter2;
                    )
                {
                    temp = iter1;
                    ++iter1;
                    delete (*temp);
                }

                m_ListObserver.clear();
            }

            /**//* --------------------------------------------------------------------
            |    ConcreateSubject類成員函數(shù)的實現(xiàn)
            |
            ----------------------------------------------------------------------*/
            void ConcreateSubject::SetState(STATE nState)
            {
                std::cout << "SetState By ConcreateSubjectn";
                m_nSubjectState = nState;
            }

            STATE ConcreateSubject::GetState()
            {
                std::cout << "GetState By ConcreateSubjectn";
                return m_nSubjectState;
            }

            /**//* --------------------------------------------------------------------
            |    ConcreateObserver類成員函數(shù)的實現(xiàn)
            |
            ----------------------------------------------------------------------*/
            void ConcreateObserver::Update(Subject* pSubject)
            {
                if (NULL == pSubject)
                    return;

                m_nObserverState = pSubject->GetState();

                std::cout << "The ObeserverState is " << m_nObserverState << std::endl;
            }


            3)Main.cpp

            /**//********************************************************************
                created:    2006/07/21
                filename:     Main.cpp
                author:        李創(chuàng)
                            http://www.shnenglu.com/converse/

                purpose:    Observer模式的測試代碼
            *********************************************************************/

            #include "Observer.h"
            #include <iostream>

            int main()
            {
                Observer *p1 = new ConcreateObserver;
                Observer *p2 = new ConcreateObserver;

                Subject* p = new ConcreateSubject;
                p->Attach(p1);
                p->Attach(p2);
                p->SetState(4);
                p->Notify();

                p->Detach(p1);
                p->SetState(10);
                p->Notify();

                delete p;

                system("pause");

                return 0;
            }

            posted @ 2009-07-22 14:44 yanghaibao| 編輯 收藏
            http://www.codeproject.com/KB/cpp/CppEvents.aspx
            posted @ 2009-07-22 13:50 yanghaibao| 編輯 收藏

            2009年7月21日

            在許多廣泛應(yīng)用的程序庫,我們會看到類似 #pragma pack(push, 4) 等這樣的標(biāo)示。因為用戶會任意更改他們的結(jié)構(gòu)成員對齊選項,對于先于這些內(nèi)容創(chuàng)建的程序庫來說,不能確保一定的內(nèi)存布局將可能在預(yù)先書寫的一些數(shù)據(jù)訪問模塊上導(dǎo)致錯誤,或者根本不可能實現(xiàn)。

            我在實現(xiàn)一種 C++ 類的實例的序列化工具時,依賴了內(nèi)存布局。我知道市面上很多“序列化”工具允許更為廣泛的通信用途,但是它們也是用起來最麻煩的,有很多限制條件。我實現(xiàn)的序列化工具用意很明顯,為特定運行模塊提供便捷高效的持久化存儲能力。

            為了提供感性的認(rèn)識,提供了一個使用這個序列化工具的類型定義。

            class StorageDoc
            : public SerialOwner
            {
            public:
            Serializable(StorageDoc);

            char c;
            int i;
            SerialString str;
            };

            它繼承自 SerialOwner,它聲明了 Serializable,隱含著實現(xiàn)了一些接口,為基類訪問當(dāng)前類型信息提供幫助。這是較早書寫的一種方案,現(xiàn)在我會改用模板以便在編譯時建立類型信息,不過原理完全一樣。

            現(xiàn)在,StorageDoc 當(dāng)中的內(nèi)存布局需要可確定的,但是用戶會選擇不同的結(jié)構(gòu)成員對齊選項,為此需要設(shè)定一個結(jié)構(gòu)成員對齊的“子域”,完成這項能力的偽指令是 #pragma pack。

            #pragma pack( [ show ] | [ push | pop ] [, identifier ] , n )

            1)當(dāng)選用 show,則添加一條警告信息,指示當(dāng)前編譯域內(nèi)的對齊屬性
            2)僅僅設(shè)置 n,則重寫編譯器選項 /Zp,并影響到此聲明以下的同一個編譯單元內(nèi)的所有結(jié)構(gòu)定義
            3)push 以及 pop 管理了一組“子域”堆棧,可以不斷加深嵌套
            4)identifier 命名了堆棧上的對齊項,以便在特定需求中彈出合適的項目

            以下是使用的注意事項:

            1)不論何時,#pragma pack() 總是恢復(fù)到 /Zp 的預(yù)設(shè)值,即使處于 push 的“子域”
            2)#pragma pack(push) 未指定對齊值,則不改變
            3)#pragma pack(pop) 可指定對齊值出棧后的設(shè)置值,若不指定則按嵌套等級還原,直至 /Zp 預(yù)設(shè)值

            綜上,#pragma pack(pop) 總是能正確回退到上一個作用域,不管該作用域通過 #pragma pack(n) 聲明或者 #pragma pack(push, n)。而 #pragma pack() 總是取預(yù)設(shè)值。對于用戶事先指定了一個“子域”,并在其中引入了一個使用 #pragma pack(n) - #pragma pack() 對而非堆棧形式來聲明局部結(jié)構(gòu)成員對齊的頭文件,會使用戶非常困惑。 就是這樣做的。

            當(dāng)我們?yōu)槌绦驇炀幾g運行時,有一些類型要求嚴(yán)格地遵守內(nèi)存布局,比如一些硬件允許我們傳入的數(shù)據(jù)就需要這么做,就可以把它們限定起來:

            #pragma pack(push, 8)

            #include "Chain.h"
            #include "ByteQueue.h"
            #include "SerialOwner.h"
            #include "SerialUser.h"
            #include "SerialString.h"
            #include "SerialStream.h"

            #pragma pack(pop)

            事情再回到序列化上面,用戶會多次嘗試編譯他們的序列化應(yīng)用模塊,并指望前一次編譯之后運行所產(chǎn)生的文件仍然是可用的,所以還需要在用戶文件當(dāng)中明確所選用的對齊值,并一旦確定就不再更改:

            #pragma pack(push, 8)
            class StorageDoc
            : public SerialOwner
            {
            public:
            Serializable(StorageDoc);

            char c;
            int i;
            SerialString str;
            };
            #pragma pack(pop)

            并使用它們:

            StorageDoc doc;

            doc.Load(t("doc.bin"));
            std::cout << doc.str.Get() << std::endl;

            doc.str = ss.str();
            std::cout << doc.str.Get() << std::endl;
            doc.Save(t("doc.bin"));

            這就是全部了,但是正如以上提到的,不僅僅在序列化上,和硬件、鏈接庫的通信也可能存在嚴(yán)格的內(nèi)存布局的要求,如果你在項目設(shè)計上遭遇這些困惑,那么現(xiàn)在就可以立即動手解決它們。

            如果對本文提到的序列化能力感興趣的話,可以到以下鏈接了解詳情:

            http://code.google.com/p/los-lib/source/browse/

            目錄是:

            svn/trunk/Inc/Los/

            文件分別是:

            _ISerialUser.h
            ByteQueue.h
            Chain.h
            Serialization.h
            SerialOwner.h
            SerialStream.h
            SerialString.h
            SerialUser.h

            不過在本文發(fā)布之時,以上文件所處版本沒有針對結(jié)構(gòu)成員對齊選項進行修改,但并不影響閱讀。

            * 補充一(2009-1-18 02:41)

            聯(lián)合以及結(jié)構(gòu)的結(jié)構(gòu)成員對齊異常

            class Tick
            {
            static int _StaticID;

            __int64 _StartLI; // __alignof(LARGE_INTEGER) != __alignof(__int64)
            __int64 _CurrentLI;
            __int64 _Frequency;

            int _ID;
            clock_t _Start;
            clock_t _Current;

            bool _Stop;
            bool _HighPerformance;
            ...
            }

            LARGE_INTEGER 是分別對應(yīng)兩個 32bit 以及一個 64bit 類型的聯(lián)合,奇怪的是隨著全局對齊選項的修改,LARGE_INTEGER 類型本身的請求對齊 __alignof(LARGE_INTEGER) 將取聯(lián)合的成員的最大者同全局對齊選項的最小值,也就是說,當(dāng) /Zp 設(shè)置為 2,那么 LARGE_INTEGER 也將僅承諾在 2 字節(jié)邊界上對齊,多么不幸啊。當(dāng)然如果將這個類型納入 #pragma pack 的限定域那就什么問題都沒有了,不管聯(lián)合的對齊算法多么的古怪,只要保證不修改所需的對齊值那將總是能獲得確定的內(nèi)存布局。

            不過正如上面的代碼列出的,我使用了 __int64 代替了 LARGE_INTEGER 的工作,并在請求 Win32 API 的接口上強制指針轉(zhuǎn)型,使用的時候亦如此,但若訪問聯(lián)合成員剛好為 __int64 類型則直接使用便可。這種方式?jīng)]有獲得額外的好處,算是一種抗議的行為,并且讓后來的閱讀者有機會了解到這個見不得光的問題。

            _HighPerformance = ::QueryPerformanceFrequency((LARGE_INTEGER*)&_Frequency) != 0;

            當(dāng)然作為嚴(yán)肅的代碼寫作者,也許你將在不止一處使用到 LARGE_INTEGER,為此我也不拒絕使用如下格式:

            #pragma pack(push, 8)
            #include
            #pragma pack(pop)

            它可保證你萬無一失。

            作為對比,F(xiàn)ILETIME 有如下定義:

            typedef struct _FILETIME
            {
            DWORD dwLowDateTime;
            DWORD dwHighDateTime;
            } FILETIME;

            且不論它所需的可能的最大結(jié)構(gòu)成員對齊為 4,它也將伴隨著 /Zp 的更改而變動。因此,在不同的選項的影響下:

            __alignof(LARGE_INTEGER) != __alignof(FILETIME) != __alignof(__int64)

            有些人可能要指責(zé)會發(fā)生這樣的問題純粹是用戶在玩弄“結(jié)構(gòu)成員對齊選項”而導(dǎo)致的,我真希望他能夠讀一讀這篇文章。

            * 補充二(2009-1-18 02:41)

            D3D 與用戶定義結(jié)構(gòu)的協(xié)調(diào)

            class VertexXYZ_N_T1
            {
            public:
            float x, y, z;
            float normal_x, normal_y, normal_z;
            float u, v;
            DeviceBitmap* bitmap;
            Material* material;
            float temp_val;

            static const int FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
            };

            這是一個自定義頂點結(jié)構(gòu),它的最大成員字節(jié)數(shù)為 4,所有的成員也都是 4 字節(jié)邊界,不論作何選項,始終保持緊湊存儲,若其中一個成員擴展為 8 字節(jié),那么伴隨著選項的更改,VertexXYZ_N_T1 要求的對齊邊界可導(dǎo)致部分空洞,從而同硬件所需的頂點緩存數(shù)據(jù)布局存在出入,我不追究硬件是否使用 double 值,但是現(xiàn)在就應(yīng)當(dāng)使用

            #pragma pack(push, 4)
            ...
            #pragma pack(pop)

            加以限定。

            我還定義了 Matrix, Material, Vector3, Colorf 等類型,如果要使得這些數(shù)據(jù)同 D3D, D3DX 的相應(yīng)類型在內(nèi)存上兼容的,也是需要限定的。

            posted @ 2009-07-21 12:26 yanghaibao| 編輯 收藏

            本文主要包括二個部分,第一部分重點介紹在VC中,怎么樣采用sizeof來求結(jié)構(gòu)的大小,以及容易出現(xiàn)的問題,并給出解決問題的方法,第二部分總結(jié)出VC中sizeof的主要用法。

            1、 sizeof應(yīng)用在結(jié)構(gòu)上的情況

            請看下面的結(jié)構(gòu):

            struct MyStruct

            {

            double dda1;

            char dda;

            int type

            };

            對結(jié)構(gòu)MyStruct采用sizeof會出現(xiàn)什么結(jié)果呢?sizeof(MyStruct)為多少呢?也許你會這樣求:

            sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13

            但是當(dāng)在VC中測試上面結(jié)構(gòu)的大小時,你會發(fā)現(xiàn)sizeof(MyStruct)為16。你知道為什么在VC中會得出這樣一個結(jié)果嗎?

            其實,這是VC對變量存儲的一個特殊處理。為了提高CPU的存儲速度,VC對一些變量的起始地址做了“對齊”處理。在默認(rèn)情況下,VC規(guī)定各成員變量存放的起始地址相對于結(jié)構(gòu)的起始地址的偏移量必須為該變量的類型所占用的字節(jié)數(shù)的倍數(shù)。下面列出常用類型的對齊方式(vc6.0,32位系統(tǒng))。

            類型
            對齊方式(變量存放的起始地址相對于結(jié)構(gòu)的起始地址的偏移量)

            Char
            偏移量必須為sizeof(char)即1的倍數(shù)

            int
            偏移量必須為sizeof(int)即4的倍數(shù)

            float
            偏移量必須為sizeof(float)即4的倍數(shù)

            double
            偏移量必須為sizeof(double)即8的倍數(shù)

            Short
            偏移量必須為sizeof(short)即2的倍數(shù)

             

            各成員變量在存放的時候根據(jù)在結(jié)構(gòu)中出現(xiàn)的順序依次申請空間,同時按照上面的對齊方式調(diào)整位置,空缺的字節(jié)VC會自動填充。同時VC為了確保結(jié)構(gòu)的大小為結(jié)構(gòu)的字節(jié)邊界數(shù)(即該結(jié)構(gòu)中占用最大空間的類型所占用的字節(jié)數(shù))的倍數(shù),所以在為最后一個成員變量申請空間后,還會根據(jù)需要自動填充空缺的字節(jié)。

            下面用前面的例子來說明VC到底怎么樣來存放結(jié)構(gòu)的。

            struct MyStruct

            {

            double dda1;

            char dda;

            int type

            };

            為上面的結(jié)構(gòu)分配空間的時候,VC根據(jù)成員變量出現(xiàn)的順序和對齊方式,先為第一個成員dda1分配空間,其起始地址跟結(jié)構(gòu)的起始地址相同(剛好偏移量0剛好為sizeof(double)的倍數(shù)),該成員變量占用sizeof(double)=8個字節(jié);接下來為第二個成員dda分配空間,這時下一個可以分配的地址對于結(jié)構(gòu)的起始地址的偏移量為8,是sizeof(char)的倍數(shù),所以把dda存放在偏移量為8的地方滿足對齊方式,該成員變量占用sizeof(char)=1個字節(jié);接下來為第三個成員type分配空間,這時下一個可以分配的地址對于結(jié)構(gòu)的起始地址的偏移量為9,不是sizeof(int)=4的倍數(shù),為了滿足對齊方式對偏移量的約束問題,VC自動填充3個字節(jié)(這三個字節(jié)沒有放什么東西),這時下一個可以分配的地址對于結(jié)構(gòu)的起始地址的偏移量為12,剛好是sizeof(int)=4的倍數(shù),所以把type存放在偏移量為12的地方,該成員變量占用sizeof(int)=4個字節(jié);這時整個結(jié)構(gòu)的成員變量已經(jīng)都分配了空間,總的占用的空間大小為:8+1+3+4=16,剛好為結(jié)構(gòu)的字節(jié)邊界數(shù)(即結(jié)構(gòu)中占用最大空間的類型所占用的字節(jié)數(shù)sizeof(double)=8)的倍數(shù),所以沒有空缺的字節(jié)需要填充。所以整個結(jié)構(gòu)的大小為:sizeof(MyStruct)=8+1+3+4=16,其中有3個字節(jié)是VC自動填充的,沒有放任何有意義的東西。

            下面再舉個例子,交換一下上面的MyStruct的成員變量的位置,使它變成下面的情況:

            struct MyStruct

            {

            char dda;

            double dda1;

            int type

            };

            這個結(jié)構(gòu)占用的空間為多大呢?在VC6.0環(huán)境下,可以得到sizeof(MyStruc)為24。結(jié)合上面提到的分配空間的一些原則,分析下VC怎么樣為上面的結(jié)構(gòu)分配空間的。(簡單說明)

            struct MyStruct

            {

            char dda;//偏移量為0,滿足對齊方式,dda占用1個字節(jié);

            double dda1;//下一個可用的地址的偏移量為1,不是sizeof(double)=8

            //的倍數(shù),需要補足7個字節(jié)才能使偏移量變?yōu)?(滿足對齊

            //方式),因此VC自動填充7個字節(jié),dda1存放在偏移量為8

            //的地址上,它占用8個字節(jié)。

            int type;//下一個可用的地址的偏移量為16,是sizeof(int)=4的倍

            //數(shù),滿足int的對齊方式,所以不需要VC自動填充,type存

            //放在偏移量為16的地址上,它占用4個字節(jié)。

            };//所有成員變量都分配了空間,空間總的大小為1+7+8+4=20,不是結(jié)構(gòu)

            //的節(jié)邊界數(shù)(即結(jié)構(gòu)中占用最大空間的類型所占用的字節(jié)數(shù)sizeof

            //(double)=8)的倍數(shù),所以需要填充4個字節(jié),以滿足結(jié)構(gòu)的大小為

            //sizeof(double)=8的倍數(shù)。

             

            所以該結(jié)構(gòu)總的大小為:sizeof(MyStruc)為1+7+8+4+4=24。其中總的有7+4=11個字節(jié)是VC自動填充的,沒有放任何有意義的東西。

             

            VC對結(jié)構(gòu)的存儲的特殊處理確實提高CPU存儲變量的速度,但是有時候也帶來了一些麻煩,我們也屏蔽掉變量默認(rèn)的對齊方式,自己可以設(shè)定變量的對齊方式。

            VC中提供了#pragma pack(n)來設(shè)定變量以n字節(jié)對齊方式。n字節(jié)對齊就是說變量存放的起始地址的偏移量有兩種情況:第一、如果n大于等于該變量所占用的字節(jié)數(shù),那么偏移量必須滿足默認(rèn)的對齊方式,第二、如果n小于該變量的類型所占用的字節(jié)數(shù),那么偏移量為n的倍數(shù),不用滿足默認(rèn)的對齊方式。結(jié)構(gòu)的總大小也有個約束條件,分下面兩種情況:如果n大于所有成員變量類型所占用的字節(jié)數(shù),那么結(jié)構(gòu)的總大小必須為占用空間最大的變量占用的空間數(shù)的倍數(shù);

            否則必須為n的倍數(shù)。下面舉例說明其用法。

            #pragma pack(push) //保存對齊狀態(tài)

            #pragma pack(4)//設(shè)定為4字節(jié)對齊

            struct test

            {

            char m1;

            double m4;

            int m3;

            };

            #pragma pack(pop)//恢復(fù)對齊狀態(tài)

            以上結(jié)構(gòu)的大小為16,下面分析其存儲情況,首先為m1分配空間,其偏移量為0,滿足我們自己設(shè)定的對齊方式(4字節(jié)對齊),m1占用1個字節(jié)。接著開始為m4分配空間,這時其偏移量為1,需要補足3個字節(jié),這樣使偏移量滿足為n=4的倍數(shù)(因為sizeof(double)大于n),m4占用8個字節(jié)。接著為m3分配空間,這時其偏移量為12,滿足為4的倍數(shù),m3占用4個字節(jié)。這時已經(jīng)為所有成員變量分配了空間,共分配了16個字節(jié),滿足為n的倍數(shù)。如果把上面的#pragma pack(4)改為#pragma pack(16),那么我們可以得到結(jié)構(gòu)的大小為24。(請讀者自己分析)

            2、 sizeof用法總結(jié)

            在VC中,sizeof有著許多的用法,而且很容易引起一些錯誤。下面根據(jù)sizeof后面的參數(shù)對sizeof的用法做個總結(jié)。

            A. 參數(shù)為數(shù)據(jù)類型或者為一般變量。例如sizeof(int),sizeof(long)等等。這種情況要注意的是不同系統(tǒng)系統(tǒng)或者不同編譯器得到的結(jié)果可能是不同的。例如int類型在16位系統(tǒng)中占2個字節(jié),在32位系統(tǒng)中占4個字節(jié)。

            B. 參數(shù)為數(shù)組或指針。下面舉例說明.

            int a[50]; //sizeof(a)=4*50=200; 求數(shù)組所占的空間大小

            int *a=new int[50];// sizeof(a)=4; a為一個指針,sizeof(a)是求指針

            //的大小,在32位系統(tǒng)中,當(dāng)然是占4個字節(jié)。

            C. 參數(shù)為結(jié)構(gòu)或類。Sizeof應(yīng)用在類和結(jié)構(gòu)的處理情況是相同的。但有兩點需要注意,第一、結(jié)構(gòu)或者類中的靜態(tài)成員不對結(jié)構(gòu)或者類的大小產(chǎn)生影響,因為靜態(tài)變量的存儲位置與結(jié)構(gòu)或者類的實例地址無關(guān)。

            第二、沒有成員變量的結(jié)構(gòu)或類的大小為1,因為必須保證結(jié)構(gòu)或類的每一

            個實例在內(nèi)存中都有唯一的地址。

            下面舉例說明,

            Class Test{int a;static double c};//sizeof(Test)=4.

            Test *s;//sizeof(s)=4,s為一個指針。

            Class test1{ };//sizeof(test1)=1;

            D. 參數(shù)為其他。下面舉例說明。

            int func(char s[5]);

            {

            cout<< sizeof(s) << endl;
            //數(shù)組參數(shù)在傳遞的時候系統(tǒng)處理為一個指針,所

            //以sizeof(s)實際上為求指針的大小。

            return 1;

            }

            sizeof(func(“1234”))=4//因為func的返回類型為int,所以相當(dāng)于

            求sizeof(int).

             

            以上為sizeof的基本用法,在實際的使用中要注意分析VC的分配變量的分配策略,這樣的話可以避免一些錯誤。

            posted @ 2009-07-21 10:56 yanghaibao| 編輯 收藏
            僅列出標(biāo)題  下一頁

            導(dǎo)航

            <2025年7月>
            293012345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            統(tǒng)計

            常用鏈接

            留言簿

            隨筆分類

            隨筆檔案

            文章檔案

            收藏夾

            Good blogs

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            亚洲国产精久久久久久久| 亚洲国产精品无码久久| 精品久久香蕉国产线看观看亚洲| 99久久久国产精品免费无卡顿| 久久精品国产亚洲av麻豆色欲| 国产高清国内精品福利99久久| 狠狠色狠狠色综合久久| 26uuu久久五月天| 亚洲国产另类久久久精品黑人| 91精品国产91热久久久久福利 | 国产成人无码精品久久久免费 | 香港aa三级久久三级| 久久精品视频一| 99久久精品国产一区二区三区 | 18岁日韩内射颜射午夜久久成人 | 日韩AV无码久久一区二区| 久久777国产线看观看精品| 香蕉99久久国产综合精品宅男自 | 久久精品一区二区国产| 久久精品卫校国产小美女| 久久成人精品| 亚洲国产二区三区久久| 久久国产精品一国产精品金尊| 99久久综合国产精品免费| 久久久久久久综合日本| 91精品免费久久久久久久久| 久久99精品久久只有精品| 精品国产乱码久久久久久人妻| 色偷偷88欧美精品久久久| 久久99国产一区二区三区| 情人伊人久久综合亚洲| 国产精品一久久香蕉产线看| 久久夜色精品国产噜噜麻豆| 无码精品久久久久久人妻中字| 亚洲精品无码专区久久同性男| 久久久久人妻一区精品| 欧美日韩精品久久久久| 开心久久婷婷综合中文字幕| 亚洲国产精品综合久久网络| 伊人久久大香线蕉av一区| 久久综合狠狠综合久久综合88|