在c语言中,我们经常用到宏定义,但是我们使用时并没有考虑太多的因素。
其实宏定义有时候不能被视为程序的一部分,当在使用 #define PI 3.14 时,其实编译器是看不到的,宏定义只会被预处理器看到,
什么意思呢,在编译器开始处理源码之前,宏可能就已经被预处理器移走了,所以这就话可以更改为“宁可使用编译器取代预处理器”;
为什么是这样的呢,比如你定义的PI在程序运行时出了问题,这是错误提示可能会让你困惑,因为编译器没有获得PI记号,也许错误提到的是3.14,也就是说,你无法找到错误位置,再或者,你用的是别人写的头文件,那这时候,你就对这个3.14更没有概念了,于是就会因为追踪它浪费很多时间。
那么怎么解决这个问题呢,我们可以使用 const double PI = 3.14;这样做就没问题了,而且,这样的话,PI在内存中也只有一份,不会像宏替换那样出现很多份,节省内存。
在使用const时有两种特殊情况,一种是定义常量指针,因为我们在编写程序时,一般会把常量值放在头文件中,便于共享,因此要把指针声明为常量,而不是其内容,例如: const char * const name=“jack”; 这里第一个const修饰的是指针内容,第二个是指针的值,所以这个时候,指针的内容与值都不可以被改变。
在c++中使用string会更好些:const std::string name("hehe");
第二种值得注意的是:可以用const将常量限制在一个class内,这时需要将常量设置为类的私有成员而且设置成静态成员,这样确保了常量成员只有一份为所有类对象所共享;
class Student
{
//常量声明
static const int num = 5;
//常量使用
int scores[num];
};
需要注意我们这里是声明而非定义,一般而言,我们是必须为使用的东西提供定义的,比如一个变量或者一个函数,但是class专属常量又是static,我们只要不取地址,可以声明不定义,但是如果取地址的话,就必须提供定义,这样:const int Student::num;
记住定义式要放在实现文件而不是头文件中。因为class常量在声明时已经获得了初始值,所以不能再设置初值。
宏定义是无法创建一个class专属常量的,因为它不重视作用域,他也不能被封装。
如果是在c语言中,我们想要宏定义函数,我们似乎除了使用普通函数没有别的方法去取代,但是在c++中,我们可以使用内联函数去取代宏。
用a,b中的较大值去调用函数f;
#define Call_max(a,b) f((a)>(b))?(a):(b))
我什么也不说的话,这种东西写出来也会让人痛苦不堪,宏函数有太多的弊端,代码冗长不说,还要给所有的实参加上小括号,即便加上了小括号也会出现一些尴尬的情况,这里我要吐槽一下,宏被作为很多人炫耀的手段,用来当作考察别人的手段,我其实并不这么认为,优秀的程序是不应该出现这些模棱两可的东西,编译器的版本不应该成为一种炫耀手段,这仅是我的个人观点,不喜勿喷。
在c++中,我们可以使用安全的内联函数来替代宏
template<typename T>
inline void Callmax(const T& a,const T& b)
{
f(a>b?a:b);
}
由于不知道T是什么所以才用了模板,这样可以避免很多尴尬的情况,而且这是一个真正的函数,它遵守普通函数的作用域和访问规则,例如完全可以在类中写出private inline函数,而宏是做不到的。
总结:
有了const ,enum,inline 我们不在像c语言那样需要预处理器,降低了对预处理器的需求,但是预处理器还是十分有用,很多预处理指令使我们所必需的。