C++程序中尽量避免#define
2015-09-12 13:56
537 查看
使用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.相关文章推荐
- C++中栈区 堆区 常量区
- C/C++常用头文件及函数汇总
- c++11——列表初始化
- C、C++: 引用、指针、实例、内存模型、namespace
- 一个c语言写的文件系统
- Effective C++ 条款47 请使用traits classes表现类型信息
- 【反思】一个价值两天的BUG,无论工作还是学习C语言的朋友都看看吧!
- 为什么C++编译器不支持模板头文件和实现代码分离的编译
- 一:c语言(数据类型和运算符)
- C 语言漫谈(一)
- 第二周项目1-C/C++语言中函数参数传递的三种方式
- C++于public、protected和private说明(From MSDN)
- C++ 容器Vector简介
- C++ NAN 异常处理
- c++11——模板的细节改进
- C/C++中extern关键字详解
- jvm里面有两个存储区,一个是暂存区,另一个是变量区。而C++只有一个
- c语言
- Item 26:为什么要推迟变量的定义? Effective C++笔记
- C语言中strlen()函数和sizeof()函数区别[关于字符串长度]