使用const
,enum
替換#define
定義常量
C
語言中常用#define
來定義具有某種特殊意義的常量。但是,使用#define
宏定義定義的符號會在編譯前被替換掉,當因爲該宏定義出現問題時,在錯誤信息中無法獲得有關該宏的任何提示,這對錯誤的發現帶來困難,,儘管可以通過查看預編譯輸出的方式嘗試定位問題。同時預處理也會在程序中產生多份副本,造成代碼量較大。
爲了解決這些問題,可以使用const
常量替代宏定義的常量,這樣定義的常量會被加入記號表內,而被編譯器看到,同時所有使用該常量的地方都是對同一個常量的引用,不會出現多份的情況。
/****************************************
* const_value.cpp *
* *
* C++高效原則之一用const代替#define *
****************************************/
#include <iostream>
int main()
{
const int PRICE = 10.0;
std::cout<<"輸入數量: "<<std::endl;
int x;
std::cin>>x;
std::cout<<"總價爲: "<<PRICE * x<<std::endl;
return 0;
}
在這個例子中,發現一個問題,就是C++
中cin
輸入回顯是要換行的,而C語言中的scanf
卻不用。上例中,前一個是C
程序,使用printf
輸出,scanf
輸入回顯不換行,第二個是C++
程序,使用cout
輸出,cin
輸入。
在使用const
與指針結合時,有兩種不同的形式,一種是指針常量,使用類似const char *
的定義,說明該指針指向的內存地址裏的內容不可改變,另一種是常量指針,使用類似char* const
定義,說明指針本身是個常量,它不能指向當前所指地址外的其他地址。
指針常量可以改變指向的地址,但不可以通過指針改變地址內的值。
/****************************************
* pointer_const.cpp *
* *
* C++指向常量的指針(指針常量) *
****************************************/
#include <iostream>
int main()
{
int a = 10;
int b = 20;
const int *p = &a;
p = &b;
*p = 30;
return 0;
}
常量指針可以改變地址內的值,但不可以改變指針指向
/*****************************************
* const_pointer.cpp *
* *
* 常量指針 *
*****************************************/
#include <iostream>
int main()
{
int a = 20;
int* const p = &a;
*p = 30;
int b = 50;
p = &b;
return 0;
}
const
類型常量可以用以定義類常量,類常量是類中的一個static const
成員,它的作用域爲類內,且在各個對象中共享一份。當類常量是基本類型時,只要不取它們的地址,可以聲明並使用它們而無需定義式。如果需要取它們的地址,或者編譯器不支持以上原則,則需要在定義文件中額外提供類常量的定義式。如果在聲明式子中已設置初值(有些編譯器不支持,必須將初始化放於定義式中),則在定義式中就不能在設置初值。而在這一方面,宏定義無法來定義類常量,它缺乏作用域的限定,不具有封裝性。
//-*-C++-*-
class GamePlayer
{
private:
static const int NumTurns = 5;
public:
void PrintNumTurns();
};
#include "GamePlayer.h"
#include <iostream>
void GamePlayer::PrintNumTurns()
{
std::cout<<"NumTurns = "<<NumTurns<<std::endl;
}
int main()
{
GamePlayer a;
a.PrintNumTurns();
}
若只在類中提供類常量聲明式,當試圖引用指針時,就會報錯:
//GamePlayer.cpp
#include "GamePlayer.h"
#include <iostream>
void GamePlayer::PrintNumTurns()
{
std::cout<<"NumTurns = "<<NumTurns<<std::endl;
const int *p = &NumTurns;
std::cout<<"NumTurns = "<<*p<<std::endl;
}
int main()
{
GamePlayer a;
a.PrintNumTurns();
}
在定義文件中添加定義式即可。
//GamePlayer.cpp
#include "GamePlayer.h"
#include <iostream>
const int GamePlayer::NumTurns;
void GamePlayer::PrintNumTurns()
{
std::cout<<"NumTurns = "<<NumTurns<<std::endl;
const int *p = &NumTurns;
std::cout<<"NumTurns = "<<*p<<std::endl;
}
int main()
{
GamePlayer a;
a.PrintNumTurns();
}
當對定義的整型常量 需要某些類似宏的行爲時,例如不能取地址,不會導致額外的存儲空間,可以使用enum hack
。enum hack
利用枚舉類型的數值來充當整型使用。
//GamePlayer.h
//-*-C++-*-
class GamePlayer
{
private:
enum {NumTurns = 5};
public:
void PrintNumTurns();
};
//GamePlayer.cpp
#include "GamePlayer.h"
#include <iostream>
void GamePlayer::PrintNumTurns()
{
std::cout<<"NumTurns = "<<NumTurns<<std::endl;
}
int main()
{
GamePlayer a;
a.PrintNumTurns();
}
使用inline函數替換形似函數的宏
在C
語言中常定義類似於函數的宏,儘管這樣的宏有不帶來函數調用的額外開銷,但這樣定義的宏很容易出現問題。在C++
中,可以使用template inline
函數獲得宏帶來的效率以及一般函數的所有預料行爲和類型安全性。
//-*-C++-*-
//GamePlayer.h
#define MAX(a, b) a > b ? a : b
class GamePlayer
{
private:
template<typename T>
inline T max(const T& a, const T& b);
public:
void PrintMaxUsingMacro();
void PrintMaxUsingInlineFunction();
};
//GamePlayer.cpp
#include "GamePlayer.h"
#include <iostream>
template<typename T>
T GamePlayer::max(const T& a, const T& b)
{
return a > b ? a : b;
}
void GamePlayer::PrintMaxUsingMacro()
{
int a = 20;
int b = 30;
std::cout<<"a和b中最大的值是"<<(MAX(a,b))<<std::endl;
}
void GamePlayer::PrintMaxUsingInlineFunction()
{
int a = 20;
int b = 30;
std::cout<<"a和b中最大的值是"<<max(a,b)<<std::endl;
}
int main()
{
GamePlayer a;
a.PrintMaxUsingMacro();
a.PrintMaxUsingInlineFunction();
}
參考文獻
- Scott Meyers著,侯捷譯. Effective C++中文版. 電子工業出版社. 2012.