盡量不要使用#define,而是用const、enum、inline替換。


 為什么這么說呢?或許很多程序員已經習慣在文件開始使用大量的#define語句。例如:這里程序文件開頭有如下#define語句

        #define     N                     10
        #define     PI                     3.14
        #define     MAX                10000
        #define     Heigth               6.65
        ...
        ...
        假設這里程序運行出錯誤,而且就是在我們使用這些常量有錯誤,此時編輯器應該會拋出錯誤信息。如果該信息提示6.65這里有錯誤,Ok如果你運氣好你正好記得或者程序簡單一眼能找到6.65表示什么,如果程序很復雜,而且報出6.65的文件是引用該文件,不記得,那么你會困惑這是什么?或許會花大量時間去追蹤6.65是什么?進而定位問題。
        為什么錯誤信息是6.65呢?而不是Heith呢?因為在預編譯階段,已經用5.65來代替Heigth,Height沒有進入記號表(system table)內。解決之道是可以使用下面語句進行替換
         const double treeHeight=5.68;
    作為一個語言常量,treeHeight肯定會被編譯器獲知,並且進入記號表內。報出的錯誤不在是數字而是變量名稱,這樣有利於定位問題。
        這里特別說明一下常量替換#define有兩種特殊情況。
    第一個是定義常量指針。這里要將指針定義為常量指針,同時該指針也是指向一個常量,所以是下面的形式:
          const char * const HZ="Hang Zhou";
       在C++中最好使用string對象來替換掉char*形式:
          const std::string  HZ ("Hang Zhou");
       第二個值得注意的就是class專屬常量。首先將作用於限制到類內,必須將其聲明為其成員。其次確保此常量至多只有一份實體,必須讓它稱為static成員。例如:
1         class People
2         {
3             private:
4                     static const int Number=10;
5                     int phoneNumbers[Number];
6             ......
7         }
         這看到的是聲明式,而非定義式。通常C++要求你對使用的任何東西提供一個定義式。 或者使用enum,對於形式函數的宏,盡可能用inline或者template來代替。但是如果它是個class專屬常量又是static且為整數類型(int,char,bool)則需特殊處理。只要不娶它們地址,則只用聲明而不用提供定義式子。但是如果取class專屬常量地址,縱使不取其地址編譯器就要你提供定義式子。
         static const int People::Number
        這里定義不設初始值,是因為聲明的時候已經獲取了初值。
        這里可以使用enum完成類似的功能
        class People
        {
            private:
                    enum { Number = 10 };
                    int phoneNumbers[Number];
            ....
        }
        enum比較像#define而不像const。因為取const的地址是合法的,取一個enum的地址就不合法,取#define地址通常就不合法。所以可以通過enum來實現不讓他人取得某個常量的地址。
        下面介紹一道筆試題目
           #define PRODUCT(a,b) a*b
           ....
           int a=5,b=3,c;
          c=PRODUCT(a+3,b+4);
        那么c的值為多少?c=5+3*3+4=18而不是程序員預想的56,如果想達到預期的結果必須這樣寫
          #define PRODUCT(a,b) ((a)*(b))
        或許這樣你還會堅持會寫宏函數,因為你想說只用寫一個宏函數就能完成int,flaot,double等類型的乘積運算。那么在看看下面例子
         #define MAX(a,b) ((a)>(b)?(a):(b))
         int a=5,b=3
         MAX(++a,b);   //a被加了兩次
         MAX(++a,b+4);  //a被加了一次
        a被加的結果可能不是預期的,完全可以用template inline函數達到宏的預期效果,並且效率與宏差不多。
1         template<typename T>
2         inline void Max(const T& a,const T& b)
3         {
4             f(a>b?a:b);
5         }
 
        inline函數是一種編譯機制,有點從代碼上是看不出來的,但是從程序的執行效率上有差別,通常編譯器對函數調用的處理是一種類似中斷的方式,即當執行到函數調用語句時,會將當前所有信息存儲到寄存器中,在去執行函數的代碼,執行完后取回寄存器值,恢復到調用函數開始的狀態,繼續執行代碼。聲明為   inline 函數后,編譯器不把函數編譯成調用函數而是將代碼拷貝到被調用的地方。所以效率上要比普通函數高一些,少了存寄存器與取寄存器信息的步驟。
        另外需要注意的是inline函數最好寫到.h文件中去,或者直接寫到類中去。
        const允許程序員指定一個語義約束,即不能被改動,編譯器會強制實施這項約束。它表示被它修飾的值是不變的。const可以在classes外部修飾global或namespace作用域中的常量,或修飾文件、函數或者static對象以及指針。在const應用在指針中要注意關鍵字const出現在“*”的什么地方,如果在左邊表示被指向的值是常量,如果在右邊表示指針自身是常量。出現兩邊表示兩者的功能的並集。這里特別說以下幾點:
        (1)迭代器中的cosnt
        const std::vector<int>::iterator iter=vec.begin();  //相當於iter不能改變
        std::vector<int>::const_iterator citer=vec.begin();  //iter所指向的內容無法改變
        (2) 將函數返回值聲明為常量,不僅可以降低因程序員錯誤造成的不可預料的情況,並且不用放棄安全性和高效性。例如:
         const operater *(const &lhs,const &rhs);
         if((a * b  = c);//本意是if(a*b==c)因程序員馬虎而寫成這樣
        如果a和b都是內置類型,此代碼就不合理,但是是我們自定義類型,就可能行得通,如果聲明返回值是cosnt,就可以預防這么做了。
       (3)const成員函數,它是為了確認成員函數可作用於const對象。而且兩個成員函數如果只是常量性不同,是可以重載的。成員函數后面跟const,表示該函數不能更改類的成員變量(下面有代碼驗證,如果嘗試對其成員賦值,編譯器會爆出錯誤)。原理就是編譯器將其認為是只讀變量。並且大多數const對象用於引用傳遞或者指針傳遞。
 1 #include <iostream>
 2 #include <string>
 3 
 4 class People
 5 {
 6     public:
 7          People():m_sName(""),m_iAge(0){}
 8          People(std::string name,int age):m_sName(name),m_iAge(age){}
 9          void set(int age)
10          {
11             this->m_iAge=age;
12          }
13          
14          void set2(int age) const
15          {
16             this->m_iAge=age;
17          }
18          
19          int get()
20          {
21             return this->m_iAge;
22          }
23     private:
24          std::string m_sName;
25          int m_iAge;
26 };
27 
28 int main(int argc,char **argv)
29 {
30     People* p=new People("sky",8);
31     p->set(10);
32     std::cout<<p->get()<<std::endl;
33     p->set2(12);
34     std::cout<<p->get()<<std::endl;
35     delete p;
36     return 0;
37 }
編譯該文件會報以下錯誤信息
const_test.cpp: In member function `void People::set2(int) const':
const_test.cpp:16: error: assignment of data-member `People::m_iAge' in read-only structure
const_test.cpp:36:2: warning: no newline at end of file
cosnt重載(注意:僅當形參是引用或者指針時候,形參是否為const才會有影響)。可以試一試我們將下面代碼的&去掉,傳入const_int其實調用的是void set(int age)函數,說明形參的const是沒有起作用的下面是驗證代碼
 1 #include <iostream>
 2 #include <string>
 3 
 4 class People
 5 {
 6     public:
 7          People():m_sName(""),m_iAge(0){}
 8          People(std::string name,int age):m_sName(name),m_iAge(age){}
 9          void set(const int& age) const
10          {
11              std::cout<<"this is const"<<std::endl;
12          }
13          
14          void test(int& age)
15          {
16              std::cout<<"this is non-const"<<std::endl;
17          }
18          
19          void test(short age)
20          {
21              std::cout<<"this is non-const"<<std::endl;
22          }
23          
24          int get()
25          {
26             return this->m_iAge;
27          }
28     private:
29          std::string m_sName;
30          int m_iAge;
31 };
32 
33 int main(int argc,char **argv)
34 {
35     People* p=new People("sky",8);
36     const int const_int=12;
37     p->test(const_int);
38     std::cout<<p->get()<<std::endl;
39     delete p;
40 }

(4)關於重載函數代碼重復的問題。由經驗可以得出我們通過const重載的函數往往有大量的代碼是重復,甚至是一樣的。如果大部分重復代碼,我們可以將這些重復的代碼寫成一個函數,在由它們分別調用。如果是一樣的,如下代碼,我們就可以在non-const函數中調用const函數,來解決代碼重復。

 1 class People
 2 {
 3     public:
 4          People():m_sName(""),m_iAge(0){}
 5          People(std::string name,int age):m_sName(name),m_iAge(age){}
 6          void eat(const People & Person) const
 7          {
 8              std::cout<<"this person info is:{age ="<<Person.m_iAge()<<",name ="<<Person.m_sName()<<std::endl;
 9              std::cout<<"eating"<<std::endl;
10              std::cout<<"end"<<std::endl;
11          }
12          
13          void  eat ( People & Person)
14          {
15              std::cout<<"this person info is:{age ="<<Person.m_iAge()<<",name ="<<Person.m_sName()<<std::endl;
16              std::cout<<"eating"<<std::endl;
17              std::cout<<"end"<<std::endl;
18          }
19     private:
20          std::string m_sName;
21          int m_iAge;
22 };

然后在non-const eat函數中首先將*this類型由People&顯示的轉化為const People&讓其調用const函數,即函數重載

 1 #include <iostream>
 2 #include <string>
 3 
 4 class People
 5 {
 6     public:
 7          People():m_sName(""),m_iAge(0){}
 8          People(std::string name,int age):m_sName(name),m_iAge(age){}
 9          void eat(const People & Person) const
10          {
11              std::cout<<"this person info is:{age ="<<Person.m_iAge<<",name ="<<Person.m_sName<<"}"<<std::endl;
12              std::cout<<"eating"<<std::endl;
13              std::cout<<"end"<<std::endl;
14          }
15          
16          void eat(People & Person)
17          {
18              static_cast<const People&>(*this).eat(Person);
19          }
20     private:
21          std::string m_sName;
22          int m_iAge;
23 };
24 
25 int main(int argc,char **argv)
26 {
27     People Person("sky",8);
28     Person.eat(Person);
29 }

運行的結果為

this person info is:{age =8,name =sky
eating
end

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM