C++基礎——多語言支持 Singleton模式

来源:http://www.cnblogs.com/wlsandwho/archive/2016/04/09/5371102.html
-Advertisement-
Play Games

版權聲明 版權聲明:原創文章 禁止轉載 請通過右側公告中的“聯繫郵箱([email protected])”聯繫我 勿用於學術性引用。 勿用於商業出版、商業印刷、商業引用以及其他商業用途。 本文不定期修正完善。 本文鏈接:http://www.cnblogs.com/wlsandwho/p/ ...


=================================版權聲明=================================

版權聲明:原創文章 禁止轉載 

請通過右側公告中的“聯繫郵箱([email protected])”聯繫我

勿用於學術性引用。

勿用於商業出版、商業印刷、商業引用以及其他商業用途。                   

 

本文不定期修正完善。

本文鏈接:http://www.cnblogs.com/wlsandwho/p/5371102.html

恥辱牆:http://www.cnblogs.com/wlsandwho/p/4206472.html

=======================================================================

其實Singleton模式,很多人都寫過類似的博客。

雖然閱讀量很多很多,可能超過了我所有博客的閱讀量,但是沒幾個的代碼是能用的。

而且也沒有說為什麼要用這種東西。

  也沒說在什麼情況下要用這種東西。

 

本文不講解原理,詳細請參見四人幫的經典書籍。我相信原理的東西,我和大師還是有差距的。

這裡只講點自己的看法。

 

有什麼不對的地方歡迎指正。不喜歡來咬我呀。

=======================================================================

下麵王林森舉兩個最常見的例子,以說明為什麼要用Singleton模式。

=======================================================================

1 一個在各個文件里都需要用到的變數

作為一隻MFCer,以對話框程式為例,我們知道有一個叫theApp的實例。(什麼你不是?那這個例子和這篇文章的代碼不適合你。:)

這個東西在WLSApp.h中用extern聲明,

    在WLSApp.cpp中定義。

theApp實例可以在CWLSDlg中使用。(因為.h文件有聲明,.cpp文件里有定義,實在想不通可以把.h和.cpp合成一個.hpp)

 

現在假如王林森在該對話框程式里又添加了另一個對話框CWLSDlg2,(用作其他彈出式界面),

那麼如果CWLSDlg2要使用theApp,

就必須在CWLSDlg2中引用WLSApp.h文件。

 

現在再假如王林森在程式初始化的時候讀取了一些配置用於稍後所有的界面初始化,比方說CMultiLanguage的一個實例oML用作多語言支持,

那麼如果想要所有的界面都使用同一個oML,

就需要像theApp一樣,在WLSApp.h中引用MultiLanguage.h,

            在WLSApp.h中用extern聲明,

            在CWLSApp.cpp中定義,

            在所有打算使用oML的文件里,引用WLSApp.h

(講道理,其實不是必須在WLSApp.h文件里,之所以要這麼做,是因為跟theApp的定義聲明放在一起比較好。)

 

這是一個很繁瑣的過程,而且你不能保證在所有的地方使用的是同一個。(一旦繁瑣,就可能出錯。)

=======================================================================

2 一個在多線程環境中初始化並使用的變數

假如我們要處理一些數據,對記錄中間結果那個變數的初始化工作是其中一個線程做的,但如果其餘線程也空閑著,那麼可以幫助這個線程一起處理數據。

(這個例子可能不恰當。)

 

顯然我們需要保證大家用的是一個同一個變數,既不能對同一份數據生成多個變數,也不能用不同的變數處理同一份數據。

=======================================================================

上面是可能用到Singleton的情況。

=======================================================================

有一種情況,記住不要用Singleton,那就是:

  需要使用同一個變數,

  在一個文件里定義,(可以在其頭文件里聲明)

  並且只在這個文件里使用。

因為:

  這種情況就應該用全局變數啊。(在這個文件的開始定義一個全局變數。)

 

(什麼?老師說“XXXXX”?老師還說“XXXXX”?好,你贏了。)

=======================================================================

搞清楚了這些,我們再繼續進行。

=======================================================================

以下以多語言支持CMultiLanguage為例。

(用多語言支持來舉例,是因為我想把我的設計模式系列寫成有實際能用例子的系列,而不是那些做燒烤做遙控器之類的很奇怪的例子。)

常見的Singleton實現,有多種情況,這裡舉兩個:

1 在GetInstance里返回一個static CMultiLanguage oML。(這個要在.cpp文件里定義並初始化。屏蔽拷貝構造和賦值運算符等。)

2 在GetInstance里,通過判斷指針是否為空,返回一個通過new CMultiLanguage的poML。

 

坊間傳聞C++11可以在第1種的代碼里不加鎖,我不是很清楚,對此沒有探討過。(下麵討論加鎖。)

作為VS2010SP1用戶,我通常使用第2種。

=======================================================================

對於加鎖,好多博客里只寫了個Lock和UnLock,然後一個註釋“這裡加鎖”、“這裡解鎖”。

這種代碼你肯定跑不了的好嗎?!

 

還有的博客是用了內部類,利用構造函數加鎖,析構函數解鎖。這個想法很不錯,但是大多數的代碼也用不了。

因為你傳參了但是沒用到啊,你沒用到同一個臨界區怎麼知道是加了鎖了?

你怎麼保證你外部傳進來的作為參數的臨界區是同一個?怎麼保證這個臨界區的生命期?

另外個人用個人的鎖,那不叫加鎖啊。大家用同一把鎖那才叫加鎖啊。

 

所以,同時作為VS用戶和MFC用戶,我會使用一個CCriticalSection,把它typedef為CLock。

                    聲明一個static的CLock成員對象,在.cpp文件里定義。(這裡註意要有類名進行限定修飾,不然會不識別。)

                    判斷指針是否為空,加鎖,判斷指針是否為空,分配,解鎖。(坊間傳聞這個叫雙重加鎖?我覺得名字起的不好。)

搞定!

=======================================================================

再來說下垃圾回收。

可能有人會說這個變數的使用貫穿始終,不需要啊。

 

是的,如果能做到只在主程式/主界面里使用,還真的不需要,在整個程式結束的時候,任何資源都會被回收。

 

可是,如果這個變數是用在一個經常創建並銷毀的地方,那麼就要當心了。

比方說,變數定義在一個彈出的子對話框上,這個對話框有三個選項卡,它們共用這個變數。

不停的關閉和打開這個字對話框,會不斷的創建和銷毀變數。如果不回收記憶體,就會不斷的造成記憶體泄漏。直至記憶體耗盡然後一步一卡。

 

在垃圾回收上,比較贊同用內部類的方式處理。

使用一個內部類的static修飾的變數,例如static CAutoDeleter m_oAD,然後再在.cpp文件中定義並初始化。(因為大家都知道的原因,static是類擁有而不是變數擁有的。)

=======================================================================

大體的需要註意的地方就這些。

下麵來具體的舉一個實際的例子。

=======================================================================

多語言支持,是一個不可避免的話題。我們的征途是星辰大海,必然要走出國門,黑白頭鷹、駱駝、漢斯貓以及其他動物的。

然而在我們沒有走出國門前,經常會忽視掉/忘記這個功能模塊。

 

那麼問題來了:

後期如何添加?

怎麼能支持擴展語言?

把要做的發給外貿的同事,他們不會編程,能改嗎?(即一定要等外貿的同事翻譯完,我們工程師再去修改成能夠用的形式嗎?)

 

直擊靈魂的三個問題!

 

王林森這文章的後半部分,將使用Singleton模式,使用簡單易行的方法,解決這個問題。

=======================================================================

在開始我的方法之前,先看看現在都有哪些做法。

1 資源DLL。很顯然,這需要編程能力。

2 .ini配置文件。這個網上大多的例子,在後期界面修改引入新的ID的時候,比較麻煩。

 

值得一提的是,微軟在VS的示例里,給出了一個些多語言支持的例子。(我64位預設安裝路徑,好像在打SP1補丁包後,位於C:\Program Files (x86)\Microsoft Visual Studio 10.0\Samples\2052)

在VC2010Samples\C++\International里有三個例子。可以看下。

其中,

IME跟顯示多語言支持無關。

unires用了String Table,調用LoadString來顯示多種文字。寫死了,不好改。

satdll這個Solusion有問題。

 

坑啊!

=======================================================================

好了,忘掉剛纔黑暗的小路,王林森即將帶領你走向光明的,呃,小道。

 

我們應當有一個解決方案,這個解決方案能夠支持動態的多語言切換——這個不太重要,

                    支持語言集的拓展——這個重要,因為後期可能要加其他語言,

                    支持支持界面的和非界面的多語言文字——這個重要,因為除了界面,你可能還要生成報表,報表也得改啊。

順便說下,多語言的本質是“根據用戶的選擇,提供不同的字元串”。

=======================================================================

配置文件分兩種,

第一種,預覽文件。在這個文件里,列出當前選用的是哪個語言,

                  一共有多少種語言,

                  每種語言的ID,

                  每種語言的名字,

                  每種語言的文件名。

可以按照這樣的形式:

 1 [CurrentSetting]
 2 CurrentLanguageID=2
 3 
 4 [LanguageSum]
 5 Sum=2
 6 
 7 [Language1]
 8 LanguageID=1
 9 LanguageName=中文
10 LanguageFileName=Chinese.ini
11 
12 [Language2]
13 LanguageID=2
14 LanguageName=English
15 LanguageFileName=English.ini

 

配置文件的第二種,語言數據文件。

借鑒resource.h文件,王林森把每一個用到的字元串都分配一個字元串ID。

考慮到既有界面和控制項是有資源ID的,

把資源ID和字元串ID都用

1 ID_MAIN_DLG_CAPTION    王林森的多語言支持示常式序
2 ID_STRING_HELLO        你好

的形式寫在配置文件里。

這裡註意一點。必須保證所有的ID_XXX_XXX不重名。

題外話,MFC程式里,不同類型控制項的ID重名不影響,是因為.rc文件里指明瞭控制項的類型。這裡我們只是一個配置文件,為了簡化並和諧統一,採用不重名的方式。

可以參照下麵的形式:

中文

1 [LanguageText]
2 IDC_STATIC_PROVERB=當蝴蝶在南半球拍了兩下翅膀,它就會稍微飛高一些。
3 IDC_BUTTON1=禁止轉載
4 IDC_STATIC1=王林森
5 IDC_RADIO1=半瓶以上
6 IDC_RADIO2=一瓶未滿

英文

1 [LanguageText]
2 IDC_STATIC_PROVERB=When the butterfly in the southern hemisphere beat the wings, it will fly a little higher.
3 IDC_BUTTON1=No reproduction without permission.
4 IDC_STATIC1=wlsandwho
5 IDC_RADIO1=Oooops
6 IDC_RADIO2=I can't translate this sentence into English.

 

那麼問題來了,類如何知道在配置文件里有哪些ID的?

王林森在往配置文件寫ID的時候,順便用SetKey的方式,向一個容器註冊一個ID名。

即在從無到有的創建一個語言數據文件時,

每寫一行

1 ID_XXXXXX=XXX XXX

就在VS里寫一行代碼

1 m_oML.SetKey(TEXT("ID_XXXXXX"));

 

當然可以讀取每一個配置文件,用解析文本文件的每一行的方式,得到“=”左邊的鍵的文本,然後添加到一個set里,代替手工註冊。這樣就更簡單了。

在王林森的示例代碼中,會綜合使用這兩種方法。

=======================================================================

讀寫配置文件的函數參見MSDN。函數有很多。我只用三個。

GetPrivateProfileInt

GetPrivateProfileString

WritePrivateProfileString

 

前兩個讀數字和字元串,最後一個寫字元數據。

 

現在要做的就是,讀取預覽文件,然後根據預覽文件數據去讀取語言數據文件。

是的,在一次初始化中,讀取所有的配置文件。反正初始化也要時間,再多這半秒也無所謂。這方便後面動態切換語言。

=======================================================================

實際上,王林森使用了map生成了兩個字典。

第一個是預覽字典,存放的是預覽文件的數據。

第二個是語言數據字典,存放的是眾多的語言數據文件的數據。

=======================================================================

這樣通過

  使用ID的文本作為Key查找,得到目標文本

  在界面處使用操作ID的API來使用文本/在非界面處,直接使用文本

的方式完成了多語言支持。

=======================================================================

看一下效果

切換前

切換後

=======================================================================

部分代碼。

頭文件:

  1 #pragma once
  2 //wlsandwho
  3 
  4 #include <atlstr.h>
  5 #include <vector>
  6 #include <map>
  7 #include <set>
  8 class CMultiLanguage
  9 {
 10 //////////////////////////////////////////////////////////////////////////
 11 public:
 12     typedef struct _LanguagePreviewData
 13     {
 14         int nID;
 15         CString strName;
 16         CString strFileName;
 17 
 18         bool operator <(const _LanguagePreviewData &oDR) const
 19         {
 20             return nID<oDR.nID;
 21         }
 22 
 23         _LanguagePreviewData& operator = (const _LanguagePreviewData & oDR)
 24         {
 25             nID=oDR.nID;
 26             strName=oDR.strName;
 27             strFileName=oDR.strFileName;
 28 
 29             return *this;
 30         }
 31 
 32     }LanguagePreviewData,*PLanguagePreviewData;
 33 
 34     typedef std::vector<LanguagePreviewData> VctLanguagePreview;
 35 
 36     typedef std::vector<CString> VctStrKey;
 37 
 38     typedef std::map<CString,CString> MapStrId2Text;
 39     typedef std::map<CString,CString>::iterator ItMapStrId2Text;
 40 
 41     typedef std::map<int,MapStrId2Text> MapLanguageId2Text;
 42     typedef std::map<int,MapStrId2Text>::iterator ItMapLanguageId2Text;
 43     typedef std::set<CString> SetKeys;
 44 
 45 //////////////////////////////////////////////////////////////////////////
 46 public:    
 47     void RegisterIDasKey(CString strKey);
 48     bool InitialMultiLanguageSupport(CString strPath);
 49 
 50     int GetCurrentLanguageID();
 51     bool ChangeLanguage(int nLanguageID);
 52 
 53     CString GetStringbyID( CString strStrID );
 54 
 55     VctLanguagePreview GetLanguagePreview();
 56     bool IsOnSupport();
 57 
 58 protected:
 59     CString GetFilePath();
 60     bool AutoRegisterIDasKey(CString strFileName);
 61 
 62     bool ReadLanguagePreview(CString strFileName);
 63     bool ReadLanguageData();
 64 
 65 protected:
 66     SetKeys m_setKeys;
 67     VctStrKey m_vctStrKey;
 68 
 69     bool m_bSupport;
 70     int m_nCurrentLanguageID;
 71 
 72     CString m_strFileName;
 73 
 74     VctLanguagePreview m_vctLanguagePreview;
 75     MapLanguageId2Text m_mapLanguageId2Text;
 76 
 77 //////////////////////////////////////////////////////////////////////////
 78 public:
 79     ~CMultiLanguage(void);
 80 
 81 protected:
 82     CMultiLanguage(void);
 83     CMultiLanguage(const CMultiLanguage&);
 84      CMultiLanguage& operator=(const CMultiLanguage&);
 85 
 86 //////////////////////////////////////////////////////////////////////////
 87 public:
 88     static CMultiLanguage* GetInstance();
 89 
 90 protected:
 91     static CMultiLanguage* m_pInstance;
 92 
 93 //////////////////////////////////////////////////////////////////////////
 94 private:
 95     class CAutoDeleter
 96     {
 97     public:
 98         ~CAutoDeleter()
 99         {
100             if (CMultiLanguage::m_pInstance)
101             {
102                 delete CMultiLanguage::m_pInstance;
103                 CMultiLanguage::m_pInstance=NULL;
104             }
105         }
106     };
107 //////////////////////////////////////////////////////////////////////////
108 private:
109     typedef CCriticalSection CLock;
110 //////////////////////////////////////////////////////////////////////////
111 private:
112     static CLock m_oLock;
113     static CAutoDeleter m_oAutoDeleter;
114 };

源文件:

  1 #include "StdAfx.h"
  2 
  3 #include "Multilanguage.h"
  4 #include "afx.h"
  5 
  6 CMultiLanguage::CMultiLanguage(void)
  7 {
  8     m_nCurrentLanguageID=1;
  9     m_bSupport=false;
 10 
 11     m_strFileName=TEXT("");
 12 
 13     m_vctLanguagePreview.clear();
 14     m_setKeys.clear();
 15     m_vctStrKey.clear();
 16     m_mapLanguageId2Text.clear();
 17 }
 18 
 19 CMultiLanguage::~CMultiLanguage(void)
 20 {
 21     m_vctLanguagePreview.clear();
 22     m_setKeys.clear();
 23     m_vctStrKey.clear();
 24     m_mapLanguageId2Text.clear();
 25 }
 26 
 27 CString CMultiLanguage::GetStringbyID( CString strStrID )
 28 {
 29     CString strRes=TEXT("wlsandwho");
 30 
 31     ItMapLanguageId2Text itLId2Text=m_mapLanguageId2Text.find(m_nCurrentLanguageID);
 32     if (itLId2Text!=m_mapLanguageId2Text.end())
 33     {
 34         ItMapStrId2Text itStrId2Text=itLId2Text->second.find(strStrID);
 35         if (itStrId2Text!=itLId2Text->second.end())
 36         {
 37             strRes=itStrId2Text->second;
 38         }
 39     }
 40 
 41     return strRes;
 42 }
 43 
 44 bool CMultiLanguage::ChangeLanguage( int nLanguageID )
 45 {
 46     m_oLock.Lock();
 47     //Change the current language text.
 48     m_nCurrentLanguageID=nLanguageID;
 49 
 50     //Save the configure.
 51     CString strCurrentLanguageID;
 52     strCurrentLanguageID.Format(TEXT("%d"),m_nCurrentLanguageID);
 53 
 54     WritePrivateProfileString(TEXT("CurrentSetting"),TEXT("CurrentLanguageID"),strCurrentLanguageID,m_strFileName);
 55 
 56     m_oLock.Unlock();
 57 
 58     return true;
 59 }
 60 
 61 bool CMultiLanguage::InitialMultiLanguageSupport(CString strFileName)
 62 {
 63     bool bMade = true;
 64 
 65     m_oLock.Lock();
 66     //bMade = ReadLanguagePreview(strFileName) ? AutoRegisterIDasKey(m_vctLanguagePreview[0].strFileName) && ReadLanguageData() && (m_bSupport = true) : m_bSupport = false;
 67 
 68     if (ReadLanguagePreview(strFileName))
 69     {
 70         int nFilesNumber = m_vctLanguagePreview.size();
 71 
 72         for (int i = 0; i < nFilesNumber; i++)
 73         {
 74             if (!AutoRegisterIDasKey(m_vctLanguagePreview[i].strFileName))
 75             {
 76                 bMade = false;
 77                 break;
 78             }
 79         }
 80 
 81         bMade=ReadLanguageData();
 82     }
 83     else
 84     {
 85         bMade = false;
 86     }
 87     
 88     m_bSupport = bMade;
 89 
 90     m_oLock.Unlock();
 91 
 92     return bMade;
 93 }
 94 
 95 bool CMultiLanguage::ReadLanguagePreview( CString strFileName )
 96 {
 97     bool bOK=true;
 98     
 99     m_nCurrentLanguageID=GetPrivateProfileInt(TEXT("CurrentSetting"),TEXT("CurrentLanguageID"),0,strFileName);
100     if (m_nCurrentLanguageID==0)
101     {
102         bOK=false;
103     }
104 
105     m_strFileName=strFileName;
106 
107     int nLanguageSum=0;
108     nLanguageSum=GetPrivateProfileInt(TEXT("LanguageSum"),TEXT("Sum"),0,strFileName);
109 
110     CString strLanguageNo=TEXT("Language");
111     CString strTempLanguageNo;
112 
113     TCHAR szReturnString[MAX_PATH];
114     
115     for (int i=1;i<=nLanguageSum;i++)
116     {
117         strTempLanguageNo.Format(TEXT("%d"),i);
118         strTempLanguageNo=strLanguageNo+strTempLanguageNo;
119 
120         LanguagePreviewData stLPD;
121         stLPD.nID=GetPrivateProfileInt(strTempLanguageNo,TEXT("LanguageID"),0,strFileName);
122 
123         memset(szReturnString,0,MAX_PATH);
124         GetPrivateProfileString(strTempLanguageNo,TEXT("LanguageName"),TEXT("wlsandwho"),szReturnString,MAX_PATH,strFileName);
125         stLPD.strName=CString(szReturnString);
126 
127         memset(szReturnString,0,MAX_PATH);
128         GetPrivateProfileString(strTempLanguageNo,TEXT("LanguageFileName"),TEXT("wlsandwho"),szReturnString,MAX_PATH,strFileName);
129         stLPD.strFileName=CString(szReturnString);
130 
131         m_vctLanguagePreview.push_back(stLPD);
132     }
133 
134     if (m_vctLanguagePreview.size()<1)
135     {
136         bOK = false;
137     }
138 
139     return bOK;
140 }
141 
142 CString CMultiLanguage::GetFilePath()
143 {
144     TCHAR szPath[MAX_PATH];
145     GetCurrentDirectory(MAX_PATH, szPath);
146     CString strPath = szPath + CString(TEXT("\\"));
147 
148     return    strPath;
149 }
150 
151 bool CMultiLanguage::ReadLanguageData()
152 {
153     int nSize=m_vctLanguagePreview.size();
154     if (nSize<1)
155     {
156         return false;
157     }
158 
159     TCHAR szReturnString[MAX_PATH];
160 
161     CString strPath=GetFilePath();
162 
163     for (int i=0;i<nSize;i++)
164     {
165         m_vctLanguagePreview[i].strFileName;
166         
167         memset(szReturnString,0,MAX_PATH);
168 
169         int nKeySize=m_vctStrKey.size();
170 
171         MapStrId2Text mapTempStrId2Text;
172         for (int j=0;j<nKeySize;j++)
173         {
174             GetPrivateProfileString(TEXT("LanguageText"),m_vctStrKey[j],TEXT("wlsandwho"),szReturnString,MAX_PATH,strPath+m_vctLanguagePreview[i].strFileName);
175             mapTempStrId2Text.insert(std::make_pair(m_vctStrKey[j],CString(szReturnString)));
176         }
177 
178         m_mapLanguageId2Text.insert(std::make_pair(m_vctLanguagePreview[i].nID,mapTempStrId2Text));
179     }
180 
181     return true;
182 }
183 
184 bool CMultiLanguage::AutoRegisterIDasKey(CString strFileName)
185 {
186     CStdioFile oStdFile;
187 
188     int nPos = -1;
189     CString strTemp;
190     BOOL bFile=oStdFile.Open(GetFilePath()+TEXT("\\")+strFileName, CFile::modeRead);
191     if (bFile==FALSE)
192     {
193         return false;
194     }
195 
196     while (oStdFile.ReadString(strTemp))
197     {
198         nPos=strTemp.Find(TEXT("="));
199         if (nPos!=-1)
200         {
201             strTemp=strTemp.Left(nPos);
202             m_setKeys.insert(strTemp);
203         }
204     }
205 
206     oStdFile.Close();
207     
208     VctStrKey vctTempStrKey(m_setKeys.begin(), m_setKeys.end());
209     
210     m_vctStrKey = vctTempStrKey;
211 
212     return true;
213 }
214 
215 void CMultiLanguage::RegisterIDasKey( CString strKey )
216 {
217     m_vctStrKey.push_back(strKey);
218 }
219 
220 int CMultiLanguage::GetCurrentLanguageID()
	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 下載地址【回覆可見】: http://www.fu83.cn/thread-256-1-1.html ...
  • 代理模式是常用的設計模式,其特征是代理類與委托類具有相同的介面,在具體實現上,有靜態代理和動態代理之分。代理類與委托類之間通常會存在關聯關係,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務,也就是說代理類主要負責為委托類 ...
  • 偶然知道原來有些網站的驗證碼圖片都是隨機生成的,後來聽人講了一下,就做了這個小例子 生成圖片,繪製背景,數字,干擾線用到了java.awt包,主要使用BufferedImage來生成圖片,然後使用Graphics對象來繪製內容。 代碼如下: 這是生成圖片,在網頁顯示中,用img標簽就可以,通過ser ...
  • 在C語言學習005:不能修改的字元串中我們知道字元串是存儲在常量區域的,將它賦值給數組實際是將常量區的字元串副本拷貝到棧記憶體中,如果將這個數組賦值給指針,我們可以改變數組中的元素,就像下麵那樣 但是現在我們不想讓指針可以修改字元串數組的中的元素,而又可以得到字元串中的元素,那麼我麽需要再拷貝一份字元 ...
  • 一.關鍵字和標示符 1.關鍵字:c規定的有特殊含義的單詞(也就是系統起的名字),全部是小寫,有32個; 由關鍵字引出數據類型和流程類型 1.分類: (1)數據類型:整型用int標示,字元型用char表示,實型(浮點型)用float或者double表示 (2)流程類型:順序結構,選擇結構,迴圈結構 其 ...
  • malloc和free都包含在<stdlib.h>頭文件中 局部變數由於存儲在棧中,一旦離開函數,變數就會被釋放,當我們需要將數據持久使用,就需要將數據保存到堆中,而在堆中申請記憶體空間就需要malloc方法;malloc方法在堆中建立一片記憶體空間,然後返回一個指針,這個指針是void*類型,保存了這 ...
  • 題目1111:單詞替換 輸入一個字元串,以回車結束(字元串長度<=100)。該字元串由若幹個單片語成,單詞之間用一個空格隔開,所有單詞區分大小寫。現需要將其中的某個單詞替換成另一個單詞,並輸出替換之後的字元串。 多組數據。每組數據輸入包括3行, 第1行是包含多個單詞的字元串 s, 第2行是待替換的單 ...
  • 非Lazy版本的普通單例實現: Lazy版本的單例實現: 對比分析: 使用Lazy<T>來初始化,使得代碼看起來更為簡潔易懂。其實非Lazy<T>版本的單例實現從本質上說就是一個簡單的對象Lazy的實現。 一般對於一些占用大的記憶體的對象,常常使用Lazy方式來初始化達到優化的目的。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...