使用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.