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

            Error

            C++博客 首頁 新隨筆 聯系 聚合 管理
              217 Posts :: 61 Stories :: 32 Comments :: 0 Trackbacks

            #

            grant 權限 on 對象 to 用戶

            一、grant 普通數據用戶,查詢、插入、更新、刪除 數據庫中所有表數據的權利。 
            grant select on testdb.* to 
            grant insert on testdb.* to 
            grant update on testdb.* to 
            grant delete on testdb.* to

            或者,用一條 MySQL 命令來替代: 
            grant select, insert, update, delete on testdb.* to

            二、grant 數據庫開發人員,創建表、索引、視圖、存儲過程、函數。。。等權限。 
            grant 創建、修改、刪除 MySQL 數據表結構權限。 
            grant create on testdb.* to ;
            grant alter  on testdb.* to ;
            grant drop   on testdb.* to ;

            grant 操作 MySQL 外鍵權限。 
            grant references on testdb.* to ;

            grant 操作 MySQL 臨時表權限。 
            grant create temporary tables on testdb.* to ;

            grant 操作 MySQL 索引權限。 
            grant index on  testdb.* to ;

            grant 操作 MySQL 視圖、查看視圖源代碼 權限。 
            grant create view on testdb.* to ;
            grant show   view on testdb.* to ;

            grant 操作 MySQL 存儲過程、函數 權限。 
            grant create routine on testdb.* to ;  -- now, can show procedure status
            grant alter  routine on testdb.* to ;  -- now, you can drop a procedure
            grant execute        on testdb.* to ;

            三、grant 普通 DBA 管理某個 MySQL 數據庫的權限。 
            grant all privileges on testdb to

            其中,關鍵字 “privileges” 可以省略。 
            四、grant 高級 DBA 管理 MySQL 中所有數據庫的權限。 
            grant all on *.* to

            五、MySQL grant 權限,分別可以作用在多個層次上。 
            1. grant 作用在整個 MySQL 服務器上: 
            grant select on *.* to ; -- dba 可以查詢 MySQL 中所有數據庫中的表。
            grant all    on *.* to ; -- dba 可以管理 MySQL 中的所有數據庫

            2. grant 作用在單個數據庫上: 
            grant select on testdb.* to ; -- dba 可以查詢 testdb 中的表。

            3. grant 作用在單個數據表上: 
            grant select, insert, update, delete on testdb.orders to ;

            4. grant 作用在表中的列上: 
            grant select(id, se, rank) on testdb.apache_log to ;

            5. grant 作用在存儲過程、函數上: 
            grant execute on procedure testdb.pr_add to 
            grant execute on function  testdb.fn_add to

            六、查看 MySQL 用戶權限 
            查看當前用戶(自己)權限: 
            show grants;

            查看其他 MySQL 用戶權限: 
            show grants for ;

            七、撤銷已經賦予給 MySQL 用戶權限的權限。 
            revoke 跟 grant 的語法差不多,只需要把關鍵字 “to” 換成 “from” 即可: 
            grant  all on *.* to   ;
            revoke all on *.* from ;

            八、MySQL grant、revoke 用戶權限注意事項 
            1. grant, revoke 用戶權限后,該用戶只有重新連接 MySQL 數據庫,權限才能生效。 2. 如果想讓授權的用戶,也可以將這些權限 grant 給其他用戶,需要選項 “grant option“ 
            grant select on testdb.* to with grant option;

            這個特性一般用不到。實際中,數據庫權限最好由 DBA 來統一管理。

            posted @ 2014-11-14 19:29 Enic 閱讀(234) | 評論 (0)編輯 收藏

            string傳值方式效率肯定是有問題的,如果使用引用方式,則必須提供原生指針接口,否則會有異常

            void Test(const std::string& strParam)
            {
            strParam.c_str();
            }
            void Test(const char* szParam)
            {
            }
            void Test()
            {
            Test(nullptr);
            int i = 0;
            i++;
            std::string strA = "---";
            }
            posted @ 2014-11-07 14:47 Enic 閱讀(1275) | 評論 (0)編輯 收藏

            Jsp如何轉換為Servlet
            jsp的底層技術是servlet,他們的生命周期是相同的。服務器負責實例化jsp/servlet,激活init()方法,準備處理客戶端請求。可以通過編寫service()方法處理自己的事物邏輯,或者自己編寫doGet()、doPost()方法。服務器激活destroy()方法時,jsp/servlet被銷毀,啟動gc使用finalize()方法清理內存。
            jsp會被轉換到適當的servlet代碼,即一個.java文件。許多應用服務器保存生成的.java文件,一旦他轉換到.java文件被編譯為字節碼.class。.class文件被支持輸出成html文檔返回給客戶端。
            HttpServlet基本結構
            編寫一個HttpServlet時,通常需要許該的方法:
            Void init(ServletConfig sc) throws ServletException;
            Void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
            Void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
            Void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
            html表單(form)擁有一種成為“method”屬性,用于定義如何想服務器發送數據。Get用于將數據追加入url的方式向服務器發送數據。Post用于將數據打包發送給服務器。
            理解jsp應用模型
            什么是應用模型:對jsp如何相互交互的一種描述。
            1.簡單模型:單一的jsp構成。
            2.N層模型:添加了類似JavaBean的服務端資源。
            3.松散耦合模型:允許位于遠程系統的jsp做對等交互,或者擁有一種C/S關系。
            通過html或者xml的http通訊,每個jsp應用都與其他jsp應用保存隔離。
            4.包含請求模型:一個jsp可以負責請求和響應,但是包含其他jsp的輸出。通過include行為實現。
            5.轉發請求模型:重定向,通過forward行為實現。
            理解Jsp的布局
            標簽可以是html標簽,也可以是jsp標簽。
            jsp標簽可以有作用域 <jsp: useBean name="myClass" class="SimpleClass" scope="page" />
            注釋:
            1.html:<!-- -->
            2.jsp: <%-- --%>
            3.java: // or /**/
            jsp解剖:
            html代碼----負責頁面整個布局和外觀
            jsp標簽----負責scriptlet、表達式、聲明、行為和指令
            jsp隱式對象----例如:request對象、response對象、session對象、config對象
            JavaBean----實現事物邏輯
            理解jsp標簽元素
            jsp有兩種形式:<%%>、<jsp:tagid/>
            1.jsp聲明標簽用法:(定義變量)
            <%!
            declaration_statement(s)
            %>
            <jsp:declaration>
            </jsp:declaration>
            2.jsp表達式標簽用法:(引用變量)
            <%=expression%>
            <jsp: expression> expression </jsp: expression>
            3.jsp scriptlet標簽用法:(編寫java代碼,差不多就是用標簽把java代碼包起來,標簽會截斷標簽)
            <% 
            boolean isPasswordAnyGood(String password)
            {
            return false;
            }
            %>
            <%
            if(isPasswordAnyGood("123"))
            {
            %>
            <H1>Welcome<H1/>
            <%
            }
            else
            {
            %>
            <H1>Press Login</H1>
            <%
            }
            %>
            4.jsp指令標簽:(預處理命令之類)
            <%@ page import="com.macmilan.jspln24.*" %>
            5.jsp行為標簽(允許jsp使用java創建對象,包含jsp,轉發jsp,java交互)
            六種:useBean setProperty getProperty include forward plugin
            Jsp內置對象用法:
            內置對象提供來自瀏覽器的請求并且動態響應的功能:
            request out response pageContext session application config page
            JavaBean規范(這是個大議題,據說有兩種用法,一種可以直接無視,把javabean當不同java類在jsp中導入來用。一種是遵循javabean規范使用標簽來訪問)
            7
            posted @ 2014-11-04 16:25 Enic 閱讀(161) | 評論 (0)編輯 收藏

            首先一個原則是這樣:被用來delete的指針,一定是new出來的。
            在設計智能指針的時候發現,如果采用delete this機制,到最后可能會有嚴重的問題。
            假設有這樣的多重繼承:
            class CA{};
            class CB{};
            class CC{};
            class CABC : public CA, public CB, public CC
            {};
            CABC* pAbc = new CABC;
            CA* pA = pAbc;
            CB* pB = pAbc;
            CC* pC = pAbc;
            A B C指針的值都是不一樣的,如果是簡單的使用delete 來刪除,問題就大條了,pB pC的地址和new出來的pAbc不一樣,這樣就出現堆錯誤。
            如果設計入侵式RefObj問題就大條了:
            class Ref
            {
            public:
            virtual void Test()
            {
            std::cout << this << std::endl;
            }
            int m_i;
            };
            class CA : public virtual Ref{};
            class CB : public virtual Ref{};
            class CC : public virtual Ref{};
            class CABC : public CA, public CB, public CC{};
                     C* pAbc = new CABC;
            CA* pA = pAbc;
            CB* pB = pAbc;
            CC* pC = pAbc;
            通過打印可以看到,雖然ABC 在基類Test打印出來的this地址一致,但是和實際正確的delete指針(new出來的)是不一致的,這就準備堆錯誤了,,,
            這基本意味著使用入侵式樣Ref的類,使用了多重繼承就是作死。除非和com的思想一樣,Ref不是作為基類處理,而是作為純虛接口,但是又會使得編碼復雜化,基本上全部要用組合來替代繼承
            class IXXX;
            class CXXX;
            class IYYY;
            class CYYYXXX : public CXXX, public IYYY;
            這樣的用法無法實現,應為沒有實現IXXX接口,是一個虛基類,實現了在調用的時候也是各種基類未指定的錯誤。

            奇怪的是shared_ptr沒有這個限制,猜測是在第一次從裸指針構造出來的時候,保留了原始地址,生成了一個析構函數,會在shared_ptr之間共享,直到最后一個對象析構的時候調用.
            實際觀察std::shared_ptr的實現,發現其構造函數不是控制一定要轉換成T指定的類型,而是給入參數的實際類型,后面的管理也分兩個部分,一層得到根據T轉換后的值,實際的生命周期管理得到仍然是實際類型,所以他delete的時候還是當初給定的值。
            所以,感覺智能指針不是什么好東西,背后的細節太多,一個不小心就死翹翹



            class RefObj
            {
            public:
            RefObj() : m_i(0){}
            public:
            void Increate(){m_i++;}
            void Decreate()
            {
            if(0 == --m_i)
            {
            delete this;
            }
            }
            private: 
            int m_i;
            };
            class BaseA : public virtual RefObj {public: int m_iA;};
            class BaseB : public virtual RefObj {public: int m_iB;};
            class BaseC : public virtual RefObj {public: int m_iC;};
            class CBaseABC : public BaseA,
            public BaseB,
            public BaseC
            {
            };
            void Test(BaseB* pB)
            {
            pB->Decreate();
            }
            int _tmain(int argc, _TCHAR* argv[])
            {
            ETcpSocket tcpSocket;
            CBaseABC* pABC = new CBaseABC;
            pABC->Increate();
            BaseB* pB = pABC;
            Test(pB);
            return 0;
            }



            始終還是覺得相比shared_ptr,RefObj這種,還是有部分場景更加格式,至少不用擔心類型轉換變得異常麻煩,,,
            后發現如下實現方式:
            class IRefObj
            {
            public:
            virtual void __DecRef() = 0;
            };
            template<typename TType>
            class TRefObj : public IRefObj
            {
            public:
            void __DecRef()
            {
            delete reinterpret_cast<TType*>(this);
            }
            };
            class CRefTest : public TRefObj<CRefTest>
            {
            };
            class CRefTestA : public CRefTest
            {
            };
            class CTestA {int i;};
            class CTestB {int j;};
            class CTestC : public CTestB, public CRefTestA, public  CTestA
            {
            };
            int _tmain(int argc, _TCHAR* argv[])
            {
            CTestC* pTC = new CTestC;
            CTestB* pTB = pTC;
            CRefTestA* pRTA = pTC;
            CTestA* pTA = pTC;
            pRTA->__DecRef();

            這個時候CRefTest是預期額值,懷疑可能和編譯器有關,VS測試OK,gcc沒去實驗。
            分析了一下原因:C++保證單根繼承的時候基類和派生類地址是一樣的,如果是多重繼承,那么也保證和最深的根父類地址樣,順便的,從最深的根節點,選一路下來是安全的。
            class CRefTest : public TRefObj<CRefTest>所以約定TRefObj<CRefTest>這玩意和永遠和接口在一個級別,可以保證IRefObj永遠是最深的根,就安全了?
            目前只在控件設計的時候用RefObj吧,,,坑的比較深,,,
            posted @ 2014-10-16 16:29 Enic 閱讀(756) | 評論 (2)編輯 收藏

            技術路線的選擇重要但不具有決定性(轉載)

            轉自 http://blog.csdn.net/myan/article/details/3247071  
             
            孟巖 2008 年的文章,現在看來還是挺有啟發, 送給大家,也送給自己。

            最近微軟在技術上連續有大動作,在PDC上發布了Windows Azure云計算平臺,預告了Visual Studio 2010、.NET 4.0和C# 4.0。如果放在幾年前,我相信微軟粉絲們一定是歡聲雷動,不過這次情況有點不太一樣,在網上看到有人在抱怨微軟技術更新速度太快而且四面出擊,還有人揚言要改弦更張,投奔Linux或者Java陣營。我本人也收到一封來信,寫信人大意是說自己大學時選擇.NET路線,一路跟下來很辛苦,2.0還沒學好,人家已經4.0預覽了,感到很困惑,問我該怎么辦。老實講,這樣的問題我無法回答,每個人具體情況不同,所應該采取的態度和解決方案也不同。從我自己來講,其實技術路線問題也曾長時間地困擾我,所以我想把我現在的一些想法攤出來跟大家分享一下。羅列如下:

            1. 根據我長期的觀察,做開發技術的人按照其人生路線設計,可以分成幾類。第一類是把自己的命運寄托在一項事業上。這樣的人知道自己想干什么,而且有能力把技術當工具來實現自己的想法和事業。這里所說的事業是廣義的,并不是說你非要自己開公司當老板,而是說你認可一件事情,比如促進人們交流和言論自由,帶給大家更多娛樂,提升大眾身心健康水平,增強國家國防實力,或者提升某個行業的信息化水平,然后你能夠以技術為手段,在這個事情上做出成績。這種人做著自己認為值得一生投入的事情,愿意領略這一追求帶來的人生起伏并且無怨無悔,我認為這是做技術的最高層次。第二類是把自己的命運寄托在組織和團隊上。這種人雖然不知道自己到底想干什么,但是技術水平出色,而且綜合素質突出,勇于變化,能夠把技術當敲門磚進入某個優秀的團隊,以團隊的目標為目標,依據團隊的需求而轉型或者堅持,跟團隊一起干出一番成績。這類人有令人羨慕的職業背景,在大公司里高薪厚祿,生活比較安定舒適,但是中年以后會經常自問到底做了什么自己想做的事情為自己過于風平浪靜的人生感到惆悵。但總的來說,這個層次也是比較高的。第三種是把命運寄托在技術上。這種人有能力成為技術的專家,然后就希望奇貨可居,待價而沽,把技能當商品出售謀求富足人生。這種人沒有大的人生目標,不想把自己的命運跟企業和組織綁定在一起,也不愿意做什么改變,只是滿足于技術高手的層面,寄希望于其技術專長能夠長期值錢,有點投機主義者的意思。第四種是還處于出賣勞動力的階段,在這里就不多說了。

            我想說的是,在過去很多年里,很多技術人實際上是把自己定位在第三種人里。而實際上,只有成為第一類和第二類人,才算達到了比較成功的狀態。第三類人實際上最危險,因為技術的變遷不但是可能的,而且是一定的。他們要么馬上被淘汰,要么追得老了累了追不動了以后被淘汰,被淘汰只是時間問題。 因此,如果你認為某個軟件技術的興起或者衰落對你個人的職業生涯構成了決定性的影響,那么你可能正走在錯誤的路線上,應當盡快改弦更張。

            2. 對個體軟件人來說,什么是核心競爭力?不是時間差,不是技術,不是基本功,不是什么思想,也不是聰明腦瓜,而是你獨特的個性知識經驗組合。

            有人看到新技術出來了,急急忙忙趕上去嘗鮮,以為自己快人一步,就能如何如何,實際上這種想法根本不靠譜,最多在論壇博客上風光兩天,等這項技術投入實際應用以后一點便宜也占不到。

            有人把某個技術、框架、平臺研究得里外通透,以為這樣就能奇貨可居。實踐中,這種人能紅火一時,但很難超過5年。這是現階段技術發展生命周期所決定的。此外,現在越來越多人意識到了,能夠靠讀書看文章讀代碼做練習學會的東西沒什么門檻,智力正常的人只要愿意花功夫,都不難達到你的程度。有的人認為,自己有能力駕馭技術潮流,哪個紅學哪個。我在技術行業里不敢說閱人無數,見過的高手上百是有的,說句不怕得罪人的話,能夠連續抓住兩個以上的潮流并且始終處于領先位置的人及其少見,一只手就數的過來。更常見的情況是,上一個階段的成功會成為下一個階段的障礙,所謂隨機應變屹立潮頭之說,往往只不過是當紅小生給自己壯膽的狂言,時過境遷之后,他就只能聽著新一代當紅小生的豪言壯語而默默苦笑。

            有人強調基本功,這是對的。在任何技術性行業里都一樣,基礎打得多深,上面就能造得多高。現實中,基本功扎實的人很少見,這跟中國教育的弊病有關,所以基本功好的人,一般應變能力強,學習速度快,比較受歡迎。但說基本功是核心競爭力,還是沒有抓住本質。我們經常能看到基本功差不多的兩個人,一個發展的很好,一個發展平平,這表明基本功成功職業生涯的是重要條件,但不是決定因素。

            有人強調這個那個思想,實際上軟件行業里的偉大的思想就那么屈指可數的幾個,窗戶紙一點就破,其他衍生出來的思想,就跟技術風潮一樣,各領風騷三五年,成不了你核心競爭力。

            還有人強調自己的智商,聰明腦瓜,覺得自己比別人聰明,自己的聰明是核心競爭力。大學生、剛畢業的人持這個觀點的比較多,然而有過人生閱歷以后,自然會對這種觀點不以為然。本質上這是因為社會對于“聰明”的定義與學校不同,一個解題高手在學校里可能是受人仰慕的聰明腦瓜,但在職業人生中則可能是個大傻蛋。我們身邊很多人走了一條不盡如意的人生道路,往往不是因為他們不夠“聰明”,而是因為他們太“聰明”了,聰明反被聰明誤。我想這也是為什么人們要發明“智慧”這個詞以區別于“聰明”的原因。另外,關于這種觀點,還有一點不得不指出,那就是在軟件這個行當里,一般聰明就可以了,絕頂聰明占不到多少便宜。

            那么核心競爭力是什么?我觀察圈子里很多成功和不成功的技術人,提出一個觀點,那就是個人的核心競爭力是是他獨特的個性知識經驗組合。這個行業里擁擠著上百萬聰明人,彼此之間真正的不同在哪里?不在于你學的是什么技術,學得多深,IQ多少,而在于你身上有別人沒有的獨特的個性、背景、知識和經驗的組合。如果這種組合,1,絕無僅有;2,在實踐中有價值,3,具有可持續發展性,那你就具備核心競爭力。因此,當設計自己的發展路線時,應當最大限度地加強和發揮自己獨特的組合,而不是尋求單項的超越。而構建自己獨特組合的方式,主要是通過實踐,其次是要有意識地構造。關于這個觀點,話題太大,我不打算贅述。

            3. 雖然技術路線的選擇不是核心競爭力,也不應該具有決定性,但對于個人職業路線還是具有比較重要的影響力。但這并不是說,我們應該煞有介事地把自己歸于Java或者.NET技術陣營,整天捧本書吭哧吭哧啃。正確的態度應該是著重于你要干的事情,然后認真把這件事情做好,通過必要的學習將所需的知識體系構筑完整,在整個過程中及時更新知識體系。只有心理沒譜的人,才會為新技術的推出感到惶恐,因為他不知道自己要干什么,也就不知道自己要學什么,看到什么東西出來了都以為如果不學就會落伍,才會覺得是個壓力,日積月累,才會痛苦彷徨嚷嚷怎么辦。相反,如果你很清楚地知道自己要做什么,就會發現,其實必須及時更新的知識變化并不頻繁,大多數新鮮玩意根本不在自己關注范圍內,任他三仙落地,五佛升天,與我何干?因此完全可以安步當車,穩扎穩打。

            4. 幾年前我剛加入CSDN的時候,.NET和Java之爭是最熱門的話題。現在回過頭看,其實當時無論你選擇那條路,如果認真做下去,搞些實事,別玩虛活的話,現在都應該有成就了。當然,客觀上來說,這幾年微軟技術變化是比較快,彎彎繞得比較多,相比之下,如果當時你選擇的是Java,可能這幾年過的比較幸福一些,這是事實。我對此并不是沒有自己的看法,但是這畢竟不是多么大的問題,實際上Java這幾年折騰得也夠猛,只不過作為一個比較開放的領域,Java為其追隨者保留了更多的自由度,而微軟的追隨者大多數有一種被驅趕的感覺而已。話說回來, 微軟的技術變革并不是沒有章法的,其今天的技術架構,早在2003年就已經明明白白地公諸于眾,只不過因為某些微妙的原因,一些微軟跟隨者這幾年被帶著兜了一些圈子,浪費了一些精力,比較辛苦。不過,現在.NET技術體系的塵埃基本落定,從體系結構上看,相對穩定的時期已經到來,投資微軟技術可以放心。

            5. 不過我相信未來不同技術流的應用領域會出現一些明顯的分化。在中國,涉及國防、國家安全、命脈產業和關鍵行業的服務端要害系統,國產化改造是阻擋不住的潮流,長期來看,開源和Java將在這個領域占據主導低位。其他的領域,隨著微軟技術變革的的大勢確定,相信微軟的優勢不可小覷。這里沒有考慮中國政府可能做出的產業調整政策。這次微軟黑屏事件,無論是否出于微軟本意,其最大的效果在于向有關部門展示了一下其信息戰力,中國政軍內部有關機構對此不可能不加以警惕,這是否會引起中國國內IT產業政策的調整,現在還不得而知。但我相信,微軟系統恐怕將在不長的時間里與中國關鍵要害領域的核心系統徹底說拜拜。未來中國IT系統的格局,很有可能是居廟堂之高則清一色Linux/Java,處江湖之遠則Windows占主導。

            6. 幾年前還有一個熱烈爭論,就是Java和C#之爭。現在實際上塵埃已經落定,兩個語言的定位已經分道揚鑣。Java實際上已經落實了成為系統語言的諾言,在現在的計算機體系結構上,Java與15年前的C一樣,可以成為構造基礎設施的利器,而且其性能相當不錯,完全突破了之前人們對虛擬機語言的認識局限性。我相信在未來,Java將有效地侵蝕C語言的一些曾經以為千秋萬代永不變色的地盤。雖然同時Java也在向上發展,但是其力度與C#不可同日而語。相反,C#主要是在往上發展,即將成為超級瑞士軍刀,微軟版十全大補膏,所有于應用開發有意義的特性都要加上,從編程語言發展來看,它將成為一株奇葩。作為一個編程語言的愛好者,我正饒有興致地注視著史上特性最豐富語言C#的發展動向。但是,不得不指出,C#的弱點在腳跟。自從用它開發Longhorn Avalon失敗以后,微軟暫時放棄了讓C#成為系統語言的努力,專心專意讓C#變成應用開發領域的超級無敵霸王3000,而在核心領域,仍然是C++、COM當關。這就出現了有趣的局面,在可見的未來,微軟體系內真正的核心軟件基礎設施,還是將由微軟自己用C++來構造,而組合裝配的應用開發,則由C#完成。VB和CLR平臺上的其他動態語言都不會有太多機會,因為C#將窮盡神智正常者一切關于語言的幻想。

            以上幾點,如果有人現在要選擇技術路線,可以參考一下。但切記,技術路線的選擇重要,但不具有決定意義。

            posted @ 2014-10-14 10:16 Enic 閱讀(215) | 評論 (0)編輯 收藏

            基于WinDbg的內存泄漏分析
            在前面C++中基于Crt的內存泄漏檢測一文中提到的方法已經可以解決我們的大部分內存泄露問題了,但是該方法是有前提的,那就是一定要有源代碼,而且還只能是Debug版本調試模式下。實際上很多時候我們的程序會用到第三方沒有源代碼的模塊,有些情況下我們甚至懷疑系統模塊有內存泄露,但是有沒有證據,我們該怎么辦? 這時我們就要依靠無所不能的WinDbg了。

            WinDbg的!heap命令非常強大,結合AppVerifier可以對堆(heap)內存進行詳細的跟蹤和分析, 我們接下來對下面的代碼進行內存泄漏的分析:
            // MemLeakTest.cpp : Defines the entry point for the console application.
            //

            #include "stdafx.h"
            #include <Windows.h>
            #include <stdio.h>

            int _tmain(int argc, _TCHAR* argv[])
            {
                char* p1 = new char;
                printf("%p\n", p1);

                char* pLargeMem = new char[40000];

                for(int i=0; i<1000; ++i)
                {
                    char* p = new char[20];
                }
                
                system("pause");

                return 0;
            }

            首先下載安裝AppVerifier, 可到這里下載, 把我們需要測試的程序添加到AppVerifier的檢測列表中, 然后保存。

            注: 我們這里用AppVerifier主要是為了打開頁堆(page heap)調試功能,你也可以用系統工具 gflags.exe 來做同樣的事。 

            雙擊運行我們要調試的MemLeakTest.exe, 效果如下:


            然后將WinDbg Attach上去, 輸入命令 !heap -p -a 0x02FC1FF8,結果如下:
            0:001> !heap -p -a 0x02FC1FF8
                address 02fc1ff8 found in
                _DPH_HEAP_ROOT @ 2f01000
                in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                             2f02548:          2fc1ff8                1 -          2fc1000             2000
                5a8c8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
                77485c4e ntdll!RtlDebugAllocateHeap+0x00000030
                77447e5e ntdll!RtlpAllocateHeap+0x000000c4
                774134df ntdll!RtlAllocateHeap+0x0000023a
                5b06a65d vrfcore!VfCoreRtlAllocateHeap+0x00000016
                5a92f9ea vfbasics!AVrfpRtlAllocateHeap+0x000000e2
                72893db8 MSVCR90!malloc+0x00000079
                72893eb8 MSVCR90!operator new+0x0000001f
                012c1008 MemLeakTest!wmain+0x00000008 [f:\test\memleaktest\memleaktest\memleaktest.cpp @ 11]
                77331114 kernel32!BaseThreadInitThunk+0x0000000e
                7741b429 ntdll!__RtlUserThreadStart+0x00000070
                7741b3fc ntdll!_RtlUserThreadStart+0x0000001b

            怎么樣, 神奇吧?我們當分配該地址內存時的堆棧(stack)被完整地打印了出來。

            當然有人很快會說:這是你知道內存地址的情況, 很多情況下我們是不知道該地址的,該如何分析?

            對于這種情況, 我們首先需要明確一些概念, 我們new出來的內存是分配在堆上, 那一個進程里究竟有多少個堆, 每個模塊都有自己單獨的堆嗎?實際上一個進程可以有任意多個堆,我們可以通過CreateHeap創建自己單獨的堆, 然后通過HeapAlloc分配內存。 我們new出來的內存是crt(C運行庫)分配的, 那就涉及到crt究竟有多少個堆了? crt有多少個堆由你編譯每個模塊(Dll/Exe)時的編譯選項決定, 如果你運行庫選項用的是/MD, 那就和其他模塊共享一個堆; 如果用/MT, 那就是自己單獨的堆。大部分情況下我們會用/MD,這樣我們在一個模塊里new內存, 另一個模塊里delete不會有問題, 因為大家共享一個堆。

            明確這些概念之后, 我們看看我們的測試程序有多少個堆, 輸入!heap -p
            0:001> !heap -p

                Active GlobalFlag bits:
                    vrf - Enable application verifier
                    hpa - Place heap allocations at ends of pages

                StackTraceDataBase @ 00160000 of size 01000000 with 00000034 traces

                PageHeap enabled with options:
                    ENABLE_PAGE_HEAP
                    COLLECT_STACK_TRACES

                active heaps:

                + 1160000
                    ENABLE_PAGE_HEAP COLLECT_STACK_TRACES 
                  NormalHeap - 1300000
                      HEAP_GROWABLE 
                + 1400000
                    ENABLE_PAGE_HEAP COLLECT_STACK_TRACES 
                  NormalHeap - 16b0000
                      HEAP_GROWABLE HEAP_CLASS_1 
                + 2360000
                    ENABLE_PAGE_HEAP COLLECT_STACK_TRACES 
                  NormalHeap - 1280000
                      HEAP_GROWABLE HEAP_CLASS_1 
                + 2f00000
                    ENABLE_PAGE_HEAP COLLECT_STACK_TRACES 
                  NormalHeap - 31d0000
                      HEAP_GROWABLE HEAP_CLASS_1 
            可以看到我們的測試程序一共有4 個堆。

            接下來我們的問題就是確定哪個是我們的crt堆, 也就是我們需要分析每個堆創建時的堆棧(stack)情況.

            我們接下來分析最后一個堆, handle是2f00000, 輸入!heap -p -h 02f00000 分析該堆的內存分配情況
            0:001> !heap -p -h 02f00000
                _DPH_HEAP_ROOT @ 2f01000
                Freed and decommitted blocks
                  DPH_HEAP_BLOCK : VirtAddr VirtSize
                    02f01f04 : 02f09000 00002000
                    02f02e38 : 02f69000 00002000
                    037e2548 : 03892000 00002000
                    037e2514 : 03894000 00002000
                Busy allocations
                  DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize
                    02f01f6c : 02f05de8 00000214 - 02f05000 00002000
                    02f01f38 : 02f07800 00000800 - 02f07000 00002000
                    02f01ed0 : 02f0bde0 00000220 - 02f0b000 00002000
                    02f01e9c : 02f0df50 000000ac - 02f0d000 00002000
                    02f01e68 : 02f0ffe0 0000001f - 02f0f000 00002000
                    02f01e34 : 02f11fd8 00000028 - 02f11000 00002000
                    02f01e00 : 02f13fe0 0000001d - 02f13000 00002000
                    02f01dcc : 02f15fc0 0000003a - 02f15000 00002000
                    ....

            可以看到該堆 _DPH_HEAP_ROOT 結構的地址是 2f01000,通過dt命令打印該結構地址
            0:001> dt ntdll!_DPH_HEAP_ROOT CreateStackTrace 2f01000
               +0x0b8 CreateStackTrace : 0x0017cbe4 _RTL_TRACE_BLOCK

            可以看到StackTrace的地址是 0x0017cbe4, 通過dds命令打印該地址內的符號
            0:001> dds 0x0017cbe4 
            0017cbe4  00178714
            0017cbe8  00007001
            0017cbec  000f0000
            0017cbf0  5a8c8969 verifier!AVrfDebugPageHeapCreate+0x439
            0017cbf4  7743a9e8 ntdll!RtlCreateHeap+0x41
            0017cbf8  5a930109 vfbasics!AVrfpRtlCreateHeap+0x56
            0017cbfc  755fdda2 KERNELBASE!HeapCreate+0x55
            0017cc00  72893a4a MSVCR90!_heap_init+0x1b
            0017cc04  72852bb4 MSVCR90!__p__tzname+0x2a
            0017cc08  72852d5e MSVCR90!_CRTDLL_INIT+0x1e
            0017cc0c  5a8dc66d verifier!AVrfpStandardDllEntryPointRoutine+0x99
            0017cc10  5b069164 vrfcore!VfCoreStandardDllEntryPointRoutine+0x121
            0017cc14  5a92689c vfbasics!AVrfpStandardDllEntryPointRoutine+0x9f
            0017cc18  7741af58 ntdll!LdrpCallInitRoutine+0x14
            0017cc1c  7741fd6f ntdll!LdrpRunInitializeRoutines+0x26f
            0017cc20  774290c6 ntdll!LdrpInitializeProcess+0x137e
            0017cc24  77428fc8 ntdll!_LdrpInitialize+0x78
            0017cc28  7741b2f9 ntdll!LdrInitializeThunk+0x10
            0017cc2c  00000000
            0017cc30  00009001

            現在我們可以看到該堆被Create時的完整堆棧了, 通過堆棧,我們可以看到該堆正是由crt創建的, 也就是說我們new的內存都分配在該堆內。

            如果你覺得上面跟蹤堆創建的過程太復雜,可以先忽略, 下面我們分析堆狀態, 輸入!heap -stat -h 0,它會分析所有堆的當前使用狀態, 我們著重關注我們的crt堆02f00000:
            Allocations statistics for
             heap @ 02f00000
            group-by: TOTSIZE max-display: 20
                size     #blocks     total     ( %) (percent of total busy bytes)
                9c40 1 - 9c40  (52.66)
                14 3ea - 4e48  (26.38)
                1000 1 - 1000  (5.39)
                800 2 - 1000  (5.39)
                490 1 - 490  (1.54)
                248 1 - 248  (0.77)
                220 1 - 220  (0.72)
                214 1 - 214  (0.70)
                ac 2 - 158  (0.45)
                82 2 - 104  (0.34)
                6a 2 - d4  (0.28)
                50 2 - a0  (0.21)
                28 4 - a0  (0.21)
                98 1 - 98  (0.20)
                94 1 - 94  (0.19)
                8a 1 - 8a  (0.18)
                2e 3 - 8a  (0.18)
                41 2 - 82  (0.17)
                80 1 - 80  (0.17)
                7c 1 - 7c  (0.16)

            我們可以看到排在第一位的是大小為0x9c40 (0n40000)的內存,分配了1次, 第二位的是大小為 0x14 (0n20) 的內存,分配了3ea (0n1002)次.
             回頭再看我們的測試程序,怎么樣? 是不是感覺很熟悉了。

            輸入!heap -flt s 0x9c40, 讓WinDbg列出所有大小為0x9c40的內存:
            0:001> !heap -flt s 0x9c40
                _DPH_HEAP_ROOT @ 1161000
                Freed and decommitted blocks
                  DPH_HEAP_BLOCK : VirtAddr VirtSize
                Busy allocations
                  DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize
                _HEAP @ 1300000
                _DPH_HEAP_ROOT @ 1401000
                Freed and decommitted blocks
                  DPH_HEAP_BLOCK : VirtAddr VirtSize
                Busy allocations
                  DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize
                _HEAP @ 16b0000
                _DPH_HEAP_ROOT @ 2361000
                Freed and decommitted blocks
                  DPH_HEAP_BLOCK : VirtAddr VirtSize
                Busy allocations
                  DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize
                _HEAP @ 1280000
                _DPH_HEAP_ROOT @ 2f01000
                Freed and decommitted blocks
                  DPH_HEAP_BLOCK : VirtAddr VirtSize
                Busy allocations
                  DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize
                    02f024e0 : 02fc63c0 00009c40 - 02fc6000 0000b000
                _HEAP @ 31d0000

            可以看到, WinDbg幫我們找到了一個符合要求的分配, 它的UserAddr是02fc63c0, 該地址實際上就是代碼char* pLargeMem = new char[40000]分配的地址, 按照開頭的方法, 輸入!heap -p -a 02fc63c0 
            0:001> !heap -p -a 02fc63c0
                address 02fc63c0 found in
                _DPH_HEAP_ROOT @ 2f01000
                in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                             2f024e0:          2fc63c0             9c40 -          2fc6000             b000
                5a8c8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
                77485c4e ntdll!RtlDebugAllocateHeap+0x00000030
                77447e5e ntdll!RtlpAllocateHeap+0x000000c4
                774134df ntdll!RtlAllocateHeap+0x0000023a
                5b06a65d vrfcore!VfCoreRtlAllocateHeap+0x00000016
                5a92f9ea vfbasics!AVrfpRtlAllocateHeap+0x000000e2
                72893db8 MSVCR90!malloc+0x00000079
                72893eb8 MSVCR90!operator new+0x0000001f
                012c101e MemLeakTest!wmain+0x0000001e [f:\test\memleaktest\memleaktest\memleaktest.cpp @ 13]
                77331114 kernel32!BaseThreadInitThunk+0x0000000e
                7741b429 ntdll!__RtlUserThreadStart+0x00000070
                7741b3fc ntdll!_RtlUserThreadStart+0x0000001b

            可以看到該堆棧就是我們
            new char[40000]的堆棧, 用同樣的方法, 我們可以分析出上面代碼for循環中的1000次內存泄漏。

            最后, 總結一下, 通過WinDbg結合AppVerifier, 我們可以詳細的跟蹤堆中new出來的每一塊內存。 很多時候在沒有源代碼的Release版本中,在程序運行一段時間后,如果我們發現有大塊內存或是大量同樣大小的小內存一直沒有釋放,  我們就可以用上面的方法進行分析。有些情況下,我們甚至可以將 _CrtDumpMemoryLeaks()和WinDbg的!heap -p -a [address]命令結合起來使用, 由前者打印泄漏地址,后者分析調用堆棧,以便快速的定位問題。

            posted @ 2014-10-14 09:57 Enic 閱讀(263) | 評論 (0)編輯 收藏

            客戶端架構設計的簡單總結
            我們知道,客戶端是相對服務端而言的,客戶端程序相對普通應用程序,主要是增加了網絡通訊功能。在這個移動和云存儲的年代,大部分終端應用程序都有網絡通訊功能, 所以都可以稱為客戶端。常見的客戶端如瀏覽器,IM客戶端, 網絡會議客戶端,郵件客戶端,微博和微信客戶端等...

            通過觀察,我們會發現所有的客戶端基本是大同小異,都會包括一些相同的功能組件, 下面簡單例舉下:
            通訊協議層

            既然客戶端都有網絡功能,就會涉及到通訊方式和數據格式以及協議, 這三者不是完全獨立,而是有機統一的。

            首先說通訊方式,常見的通訊方式包括TCP,UDP, P2P和http(s), 很多時候我們不會用單一的通訊方式,而是多種通訊方式的結合。比如說TCP端口被封,走不通時,我們會轉成嘗試http(s)。IM中聊天文本走的是TCP, 由服務器轉發,但是2個客戶端之間的文件傳輸我們可能走的又是P2P了, 多個人之間的語音聊天, 我們走的又是UDP了。

            其次說數據格式,常見的數據格式包括二進制編碼,開源序列化協議和文本格式。
            二進制一般是自定義的私有格式,通常對數值,我們會轉成大頭端,對字符串我們會用UTF8 編碼,因為沒有冗余數據,它的優點是不會浪費帶寬;主要缺點是有硬編碼的味道,不好擴充。
            開源序列化協議這里主要是指google的protocal buffer,  現在很多公司都在用, 很多人基于它開發了自己的RPC框架。主要優點是數據小,使用簡單而高效。
            文本格式主要是指xml和json. 相對來說xml比較清晰和容易擴充,但是冗余數據比較多。json借助javascript對它語言層次的支持,感覺主要是前端人員使用的比較多。

            最后再說協議,  協議和我們的應用相關聯。比如郵件客戶端,當然是走SMTP和POP3了; IM客戶端的話,一般走XMPP了;  網絡會議的話,可以走ITU的T.120協議, 也可以RFC 6501定義的XCON, 信令走SIP, 數據走RTP等。

            通信協議層是整個客戶端網絡事件驅動的引擎,它可能會比較簡單,也可能會很復雜。如果是基于XMPP的IM, 它可能會比較簡單,因為基本上只需要一層文本協議的封包和解包就可以了。 當如果是基于T.120網絡會議客戶端,就會比較復雜,它數據包走的自定義的二機制格式,按照T.120協議的建議, 在通訊協議層又分了3層:TP, MCS和GCC。TP層主要封裝數據傳輸的方式, 可以讓上層無差別的區分TCP和http(s)。 MCS層主要提供多點傳輸功能, 它抽象出通道(channel)這個概念, 讓不同session的數據進行邏輯隔離, 上層用戶可以同時加入不同的通道來進行一對一和一對多的數據收發,并且通道中的數據有不同的優先級, 還有令牌這個機制。我們也可以在MCS層對數據進行加密和壓縮, 還可以對上層的大數據包進行切包等。 GCC層主要封裝會議的最基本邏輯,比如創建會議和加入session數據包的格式封裝等, 讓上層可以通過API調用而不用關心協議要求的數據包格式。不同的數據包會工作在不同的層次, 比如心跳包可能在底層TP層就被攔截了,它不要再往上層發,因為上面不用關心這個; 而有些數據包,則需要從底層往上層按照整個協議棧層層轉發,當然每層都會剝離掉自己的協議頭, 直至上層用戶數據到達它的最終用戶。

            總之,通訊協議層封裝了客戶端和服務端的通訊方式及協議格式, 讓上層用戶不用關心底層的通信機制, 而只關注應用的接口事件。理論上我們可以在上層應用不做大調整的前提下,直接將網絡會議客戶端中的T.120協議成基于SIP的XCON。
            功能組件

            一個客戶端程序通常是由很多功能模塊組成,模塊按功能來說可以分為基礎組件和應用組件。

            基礎組件為應用組件提供的基礎設施,基礎組件是可以在不同的項目中重復使用的(比如界面控件庫,2D渲染引擎Skia, 跨平臺的網絡和線程庫等)。 
            應用組件通常和我們當前的特定應用程序相關,比如我們的網絡會議客戶端包含的桌面共享模塊, 文檔共享模塊,視頻音頻模塊,文本聊天模塊等。

            應用模塊本身分為帶界面和無界面兩種情況, 帶界面的情況下我們通常會給組件提供一個容器窗口的句柄, 讓組件自己在內部組織自己的界面。這種帶界面的組件通常會為邏輯和界面的分離帶來麻煩,在Window上實現一些半透明和動畫效果也很難。 比如我們想提供跨平臺的SDK來封裝邏輯,這時我們會更傾向讓應用組件采用無界面的模式,組件在跨平臺層只封裝邏輯和提供數據, 而把數據發到最上層界面層后再統一處理。

            對功能組件我們的設計原則是盡量保持獨立和可復用,最好能以仿COM方式動態升級而不用重新編譯, 另外組件之間要保持層次性,避免雙向或是循環依賴。
            數據存儲

            客戶端本身是處理和收發網絡數據, 這里就涉及到對這些數據如何組織和存儲的問題。這個通常會根據客戶端的類型采用不同的處處理方式:
            對于永久存儲的數據,當然是保存成文件或是存入數據庫,文件如xml, 數據庫如ACCESS,  SQL server, mysql等。
            臨時數據當然是存入內存了,根據需要采用不同的數據結構, 組織格式如array,list, map, hashmap等。
            還有一種是常見的數據保存方式是內存數據庫,最常見是SQLite了, 內存數據庫既能高效的分類保存大量數據, 又可以直接用基于SQL語句進行查詢和處理, 比如foxmail客戶端就是用SQLite存儲的郵件信息。
            還有一種是跨進程共享的數據,我們一般當然是內存映射文件了。比如我們有一個實時顯示log的工具, 我們通常會在對方應用程序里分配共享內存的內存映射文件,所有的log都寫到里面,然后在log工具程序里讀取和顯示。
            當然還有一些數據可能存到網上去的, 常見的比如我們的云筆記, 這時數據分服務端數據和本地cache。

            對于數據存儲我們的設計原則是根據需要,選擇簡單高效的方式。比如我們在設計網絡會議客戶端時曾討論要不要引入SQLite, 理想情況是引入內存數據庫后各個組件和上層應用的數據都可以統一存儲,采用數據驅動的方式,可以很方便的跟蹤整個客戶端的運行情況。但后來發現這種設計會把本來各自獨立的組件通過數據庫耦合在了一起,而且各組件的數據格式本身都很不要一樣, 很難統一存儲, 另外很多數據實際也只是臨時數據, 沒必要把簡單的時間做復雜了。
            客戶端框架

            客戶端框架一般有兩個作用:一是把所有的功能組件組織起來,進行統一的管理和展現; 二是實現整個客戶端的主界面。客戶端框架在協調各個組件時, 要注意避免讓組件之間產生雙向依賴, 而是應該讓組件把事件通知給框架后由框架統一協調和處理, 所以客戶端框架通常是整個客戶端邏輯最復雜的部分。 一個好的客戶端框架,通常會采用插件方式設計,可以動態插拔需要的組件。最好是可以根據服務端的配置, 動態下載和更新所需要的插件。

            對于客戶端框架, 我們的設計原則是低耦合和可擴展。基本上所有下層組件的改動都會影響到我們的客戶端框架,客戶的很多新需求和新組件也會導致框架產生壞味道, 這里我們設計時就要考慮如何讓客戶端框架能及時的適應這些變化。
            界面庫

            客戶端肯定會有界面,在Windows平臺上,現在的界面大致分為以下幾類:
            基于Windows原始窗口控件句柄的C++ native 客戶端, 基于.net的winform客戶端,基于.net的WPF客戶端,基于C++的DirectUI客戶端, 以嵌入瀏覽器(如webkit)方式實現的客戶端, 還有一類是基于Xaml的Metro客戶端。

            對于企業級大型安裝應用,主要考慮的是開發效率,所以客戶端還是以.net為主; 但是對于互聯網企業, 更多的是要求客戶端精簡而高效, 所以還是以C++為主。 這里就設及到C++的兩種界面庫, 一種是基于windows窗口句柄和控件自繪機制的界面庫,還有一中是基于DirectUI的界面庫, 現在大一點的公司基本上都有自己的DirectUI界面庫。基于修改Webkit方式實現的界面庫可以通過Canvas和SVG動畫等方式實現一些很炫的效果, 但是它和標準程序的用戶體驗還是有一定差距:一來web實現的控件跟Windows標準控件有很大不同, 二來web的流式布局和應用程序的網格布局也不一樣。

            對于界面,個人覺得這個東西變化實在太快了,所以我覺得應該盡量用界面和邏輯相分離的方式組織代碼。
            總結

            對于客戶端架構設計,個人覺得最大的原則就分層設計, 每層都封裝一個概念并保持獨立, 同時根據依賴倒置的原則, 站在上層客戶的角度提供接口。軟件工程里面的一條黃金定律:“任何問題都可以通過增加一個間接層來解決。

             


            http://m.shnenglu.com/weiym
            posted @ 2014-10-14 09:46 Enic 閱讀(255) | 評論 (0)編輯 收藏

            最近有機會看號稱是公司最核心的代碼, 因為這個代碼以前一直是美國那邊保密的, 這么重要的代碼會是啥樣子?
            真正拿到手大致看了一下后卻挺失望的,因為該代碼風格基本上是我剛畢業時的C++風格----帶類的C,單從代碼上看寫的挺濫,里面沒啥設計模式, 也沒有用模板, 代碼里面甚至一個函數可以寫上近千行。
            這么重要的代碼, 竟然是這種風格,挺郁悶, 由此思考好的C++程序應該是什么風格?

            C++因為本身支持多種范型設計(面向過程, 基于對象,面向對象,普通泛型,模板元編程等), 使得C++的程序風格和其他語言相比更加多種多樣。所以有人評價C++像一把瑞士軍刀, 什么功能都有, 你想拿它當什么刀使,它就能成為什么刀, 所以它很強大,強大的同時也意味著復雜。其他語言,比如Java/C#主要只支持面向對象,這樣他們的風格就很統一, 無論是標準庫,框架還是應用,都是以對象,接口和模式為主導。 但是C++程序就不一樣了, 可以說C++程序風格沒有固定的標準, 每個人根據他的經歷和使用的框架,會有完全不一樣的風格, 網上別人總結了一些C++程序風格:

            1. 經典C++流:類是核心,例程多用C Runtime的,很少用模版,一般是正統教育的結果。
            2. 古典C流:基本上當C用,偶爾用用對象,不使用異常,喜歡懷舊。
            3. MFC流:秉承MFC的風格,主要使用MFC/ATL對象和Win32 API,不喜歡STL,用很多的宏把IDE的語法提示模塊折磨到崩潰。
            4. Portable流:以C Runtime和STL為主要工具,使用類和模版,不跨平臺毋寧死。
            5. Functional流:以模版和STL為主要武器,大量使用函數式語言的設計方法,并號稱這才是真正的C++。
            6. Win32流:多使用全局函數,偏愛Win32 API,但不排斥C Runtime,通常喜歡輕量級的程序,所以身材也比較苗條。
            7. Java流:全面使用Java的風格,不能容許任何全局成員,但允許使用STL的集合類,寫很多叫Factory的類。
            8. COM流:喜歡AddRef()和Release(),大量使用接口,隱藏一切可以隱藏的東西,誦經的時候要把上帝替換成COM。
            9. 戒律流:追求完美的C++程序,計較每一個const和throw(),極力避免不安全的cast,隨身一定要帶一本ISO C++手冊。
            10. 混沌流:其程序無常形,無恒道,變幻莫測,吾不知其名。 

            上面確實總結了我們常見的一些C++程序風格,相信大部分C++程序員都可以再里面找到自己曾經或現在的影子。另外每個人C++程序風格不是一成不變的,隨著他的項目經歷會不斷的變化。比如一般人剛畢業時的風格都是帶類的C,代碼風格偏向面向過程; 后來隨著對面向對象的深入, 慢慢地會使用模式和接口來設計,此時代碼風格偏向面向對象;  再后面可能會深入STL和泛型,甚至模板元編程, 此時代碼風格使用模板泛型; 最后有些人可能會覺得過度的關注面向對象的設計模式和模板的泛型設計, 會讓人偏離對要解決的問題本身的關注, 最后他的風格又回到了原始的C或是剛畢業時帶類的C的風格。

            從上面可以看到,對于C++程序風格,我們很難定出一個比較統一的標準,但是我想我們可以根據我們要解決的問題不同而使用不同的風格。下面是我個人的一些看法:

            (1)C++底層語言基礎庫(STL, Boost)以泛型為主導, 以高效和通用為設計原則, 這方面我想大家已經達成共識

            (2)C++應用基礎庫和框架以面向對象和泛型為主導。基礎框架一般對擴展性和性能都有一定要求,對于框架一般我們是大量實踐經驗的總結,所以我們基本上已經知道它的所有可變情況, 所以理論上我們可以進行精致的設計,然后通過模板參數的Traits和Policy來分離所有可能的情況,框架本身也有一定的復雜性,需要面向對象來封裝和解耦, ATL是這方面作為COM組件開發基礎庫的成功例子。基礎框架以高效,專用和擴展性為設計原則。

            (3)C++應用層以面向對象為主導。應用層邏輯是多變的, 理論上你也可以采用模板參數的方式來應對變化, 但是應用層的變化非常復雜, 很多事不可預測的, 所以你不可能以模板參數的方式預測到所有可能的情況。另外C++現在還沒有對泛型Concepts的描述機制, 導致模板代碼比較難懂。在多變的應用層大量采用模板顯然不是一個好的選擇。 另外模板在應用層的大量使用也沒有比較成熟的經驗, 而面向對象和模式已經是非常成熟。應用層以低耦合,靈活應對變化為設計原則。

            (4)C++模塊(DLL)間的交互則以C方式API或是仿COM(Interface+Factory)為主導, 模塊接口和交互以簡潔和二進制兼容為設計原則。

            總之, 我們應該靈活應用C++各種風格和范型的特點, 采用 ”多范型“ 程序設計的思路來解決問題, 而不是采用單一風格。

            最后,回到我最初的公司核心代碼, 該代碼是用來解決某個特定問題, 顯然與通用性和可擴展性關系都不大, 也就不需要所謂的模式和模板了, 實際上你越往操作系統底層, 你離這些抽象的東西就越遠, 所以Linux之父才會給C++差評。


            http://m.shnenglu.com/weiym
            posted @ 2014-10-14 09:45 Enic 閱讀(343) | 評論 (0)編輯 收藏

            SQL連接可以分為內連接、外連接、交叉連接。

             

            數據庫數據:

                       

            book表                                          stu表

             

            1.內連接

            1.1.等值連接:在連接條件中使用等于號(=)運算符比較被連接列的列值,其查詢結果中列出被連接表中的所有列,包括其中的重復列。

            1.2.不等值連接:在連接條件使用除等于運算符以外的其它比較運算符比較被連接的列的列值。這些運算符包括>、>=、<=、<、!>、!<和<>。

            1.3.自然連接:在連接條件中使用等于(=)運算符比較被連接列的列值,但它使用選擇列表指出查詢結果集合中所包括的列,并刪除連接表中的重復列。

            內連接:內連接查詢操作列出與連接條件匹配的數據行,它使用比較運算符比較被連接列的列值。

            select * from book as a,stu as b where a.sutid = b.stuid  select * from book as a inner join stu as b on a.sutid = b.stuid

            內連接可以使用上面兩種方式,其中第二種方式的inner可以省略。

            其連接結果如上圖,是按照a.stuid = b.stuid進行連接。

             

            2.外連接

            2.1.左聯接:是以左表為基準,將a.stuid = b.stuid的數據進行連接,然后將左表沒有的對應項顯示,右表的列為NULL

            select * from book as a left join stu as b on a.sutid = b.stuid

            2.2.右連接:是以右表為基準,將a.stuid = b.stuid的數據進行連接,然以將右表沒有的對應項顯示,左表的列為NULL

            select * from book as a right join stu as b on a.sutid = b.stuid

            2.3.全連接:完整外部聯接返回左表和右表中的所有行。當某行在另一個表中沒有匹配行時,則另一個表的選擇列表列包含空值。如果表之間有匹配行,則整個結果集行包含基表的數據值。

            select * from book as a full outer join stu as b on a.sutid = b.stuid

             

            3.交叉連接

            交叉連接:交叉聯接返回左表中的所有行,左表中的每一行與右表中的所有行組合。交叉聯接也稱作笛卡爾積。

            select * from book as a cross join stu as b order by a.id

            posted @ 2014-10-05 11:09 Enic 閱讀(149) | 評論 (0)編輯 收藏

                 摘要: C++11與Unicode及使用標準庫進行UTF-8、UTF-16、UCS2、UCS4/UTF-32編碼轉換作者: 破曉 日期: 2014年2月28日發表評論 (0)查看評論UnicodeUnicode是計算機領域的一項行業標準,它對世界上絕大部分的文字的進行整理和統一編碼,Unicode的編碼空間可以劃分為17個平面(plane),每個平面包含2的16次方(655...  閱讀全文
            posted @ 2014-09-25 14:53 Enic 閱讀(10935) | 評論 (0)編輯 收藏

            僅列出標題
            共22頁: First 5 6 7 8 9 10 11 12 13 Last 
            久久国产免费观看精品| 国产精品日韩欧美久久综合| 久久久久亚洲AV无码专区首JN| 久久久久久久精品妇女99| 2020久久精品国产免费| 久久一本综合| 国产成人久久AV免费| 久久人人爽人人爽AV片| 国产欧美久久久精品| 亚洲第一永久AV网站久久精品男人的天堂AV | 久久九九兔免费精品6| 精品九九久久国内精品| 久久亚洲日韩看片无码| 久久久中文字幕| 国内精品人妻无码久久久影院| 欧美久久一区二区三区| 色综合久久中文色婷婷| 久久午夜羞羞影院免费观看| 亚洲国产香蕉人人爽成AV片久久| 久久精品www人人爽人人| 久久天天躁狠狠躁夜夜不卡| 久久久久无码中| 国产精品视频久久久| 精品国产乱码久久久久软件| 欧美色综合久久久久久| 久久久久国产精品麻豆AR影院 | 2020最新久久久视精品爱 | 久久久久婷婷| 国产午夜精品久久久久九九| 久久亚洲国产午夜精品理论片 | 性做久久久久久久久浪潮| 久久国产免费| 精品无码久久久久久久久久| 91久久精品电影| 色综合久久精品中文字幕首页| 久久精品国产亚洲欧美| 免费观看成人久久网免费观看| 久久精品9988| 国产精品综合久久第一页| 欧美亚洲另类久久综合婷婷| 久久久久亚洲AV无码去区首|