鱼C论坛

 找回密码
 立即注册
查看: 9651|回复: 36

[技术交流] #define 和 const 之辩

   关闭 [复制链接]
回帖奖励 460 鱼币 回复本帖可获得 10 鱼币奖励! 每人限 1 次(中奖概率 10%)
发表于 2011-6-25 21:59:17 | 显示全部楼层 |阅读模式
“尽量用const和inline而不用#define”,对于很多高手和经典书籍的“警告”,有木有朋友提出不同的观点呢??有木有??

人云亦云那不是云,除却巫山还有云!

有奖辩论,回答精彩的朋友均有奖励!



结束时间: 2011-9-23 21:52 裁判: 仰望天上的光

正方观点 (28)

#define 比 const 优越

反方观点 (12)

const 比 #define 优越

辩手:3 ( 加入 )
 
辩手:2 ( 加入 )
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-6-25 22:55:27 | 显示全部楼层
我习惯性的右键百度了,骚瑞。

#define ASPECT_RATIO 1.653

编译器会永远也看不到ASPECT_RATIO这个符号名,因为在源码进入编译器之前,它会被预处理程序去掉,于是ASPECT_RATIO不会加入到符号列表中。如果涉及到这个常量的代码在编译时报错,就会很令人费解,因为报错信息指的是1.653,而不是ASPECT_RATIO。如果ASPECT_RATIO不是在你自己写的头文件中定义的,你就会奇怪1.653是从哪里来的,甚至会花时间跟踪下去。这个问题也会出现在符号调试器中,因为同样地,你所写的符号名不会出现在符号列表中。
解决这个问题的方案很简单:不用预处理宏,定义一个常量:
const double ASPECT_RATIO = 1.653;

这种方法很有效。但有两个特殊情况要注意。
首先,定义指针常量时会有点不同。因为常量定义一般是放在头文件中(许多源文件会包含它),除了指针所指的类型要定义成const外,重要的是指针也经常要定义成const。例如,要在头文件中定义一个基于char*的字符串常量,你要写两次const:
const char * const authorName = "Scott Meyers";

关于const的含义和用法,特别是和指针相关联的问题,参见条款21。
另外,定义某个类(class)的常量一般也很方便,只有一点点不同。要把常量限制在类中,首先要使它成为类的成员;为了保证常量最多只有一份拷贝,还要把它定义为静态成员:
   

class GamePlayer {
private:
        static const int NUM_TURNS = 5; // constant eclaration
        int scores[NUM_TURNS];                // use of constant
        ...
};

还有一点,正如你看到的,上面的语句是NUM_TURNS的声明,而不是定义,所以你还必须在类的实现代码文件中定义类的静态成员:
const int GamePlayer::NUM_TURNS;        // mandatory definition;
                // goes in class impl.file
你不必过于担心这种小事。如果你忘了定义,链接器会提醒你。

旧一点的编译器会不接受这种语法,因为它认为类的静态成员在声明时定义初始值是非法的;而且,类内只允许初始化整数类型(如:int, bool, char 等),还只能是常量。
在上面的语法不能使用的情况下,可以在定义时赋初值:

class EngineeringConstants { // this goes in the class
private:                // header file
        static const double FUDGE_FACTOR;
        ...
};
        // this goes in the class implementation file
        const double EngineeringConstants::FUDGE_FACTOR = 1.35;
大多数情况下你只要做这么多。唯一例外的是当你的类在编译时需要用到这个类的常量的情况,例如上面GamePlayer::scores数组的声明(编译过程中编译器一定要知道数组的大小)。所以,为了弥补那些(不正确地)禁止类内进行整型类常量初始化的编译器的不足,可以采用称之为“借用enum”的方法来解决。这种技术很好地利用了当需要int类型时可以使用枚举类型的原则,所以GamePlayer也可以象这样来定义:

class GamePlayer {
private:
        enum { NUM_TURNS = 5 }        // "the enum hack" — makes
        // NUM_TURNS a symbolic name
        // for 5
        int scores[NUM_TURNS];// fine
};
除非你正在用老的编译器(即写于1995年之前),你不必借用enum。当然,知道有这种方法还是值得的,因为这种可以追溯到很久以前的时代的代码可是不常见的哟。

回到预处理的话题上来。另一个普遍的#define指令的用法是用它来实现那些看起来象函数而又不会导致函数调用的宏。典型的例子是计算两个对象的最大值:

#define max(a,b) ((a) > (b) ? (a) : (b))

这个语句有很多缺陷,光想想都让人头疼,甚至比在高峰时间到高速公路去开车还让人痛苦。
无论什么时候你写了象这样的宏,你必须记住在写宏体时对每个参数都要加上括号;否则,别人调用你的宏时如果用了表达式就会造成很大的麻烦。但是即使你象这样做了,还会有象下面这样奇怪的事发生:
int a = 5, b = 0;
max(++a, b);// a 的值增加了2次
max(++a, b+10); // a 的值只增加了1次
这种情况下,max内部发生些什么取决于它比较的是什么值!
幸运的是你不必再忍受这样愚笨的语句了。你可以用普通函数实现宏的效率,再加上可预计的行为和类型安全,这就是内联函数(见条款33):

inline int max(int a, int b) { return a > b ? a : b; }
不过这和上面的宏不大一样,因为这个版本的max只能处理int类型。但模板可以很轻巧地解决这个问题:

template<class T>
inline const T& max(const T& a, const T& b)
{ return a > b ? a : b; }
这个模板产生了一整套函数,每个函数拿两个可以转换成同种类型的对象进行比较然后返回较大的(常量)对象的引用。因为不知道T的类型,返回时传递引用可以提高效率(见条款22)。

顺便说一句,在你打算用模板写象max这样有用的通用函数时,先检查一下标准库(见条款49),看看他们是不是已经存在。比如说上面说的max,你会惊喜地发现你可以后人乘凉:max是C++标准库的一部分。
有了const和inline,你对预处理的需要减少了,但也不能完全没有它。抛弃#include的日子还很远,#ifdef/#ifndef在控制编译的过程中还扮演重要角色。预处理还不能退休,但你一定要计划给它经常放长假。;P
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-6-25 23:39:14 | 显示全部楼层
感觉纠结这个问题那个更有用没啥意义啊~~~~看个人喜好吧。爱用啥就用啥~~~~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-6-26 06:18:15 | 显示全部楼层
c专家编程 有个相关的主题
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-6-26 09:55:48 | 显示全部楼层
http://topic.csdn.net/u/20090516/03/33da7287-64c6-4605-b428-a72741c1ffec.html
上面链接是const详细的使用方法和优缺点,个人认为const更加灵活,不易出错,使用方法更多,但不能定义常量只能定义只读变量,#define只是简单的替换,编译器并不对它检查,出错几率大,但使用方便,可以定义常量,总之存在就有他存在的价值,所以挑选程序需要的最合适。{:1_1:}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-6-26 15:31:42 | 显示全部楼层
我也不太懂
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-6-26 21:48:23 | 显示全部楼层
个人觉得const更加规范,更加安全、、、
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-6-27 21:12:07 | 显示全部楼层
呵呵  我是个菜鸟 只知道const是常量的定义  他的格式是 const 数据类型  变量名    而#define是预编译指令  呵呵 其实嘛 我感觉他们都各有千秋 正所谓条条大路通罗马 如果你习惯用const编译器也不能反对你的做法 呵呵 当然#define  也是 不过他们的区别在于编译器先是别的就是#define  这点是不容置疑的 编程提供了这么多的定义方法 自然丰富了编程的知识 我觉得这个问题能引起讨论 我非常荣幸参与这次辩论  我虽然支持反方 那也是无能为力的  我想选择中方也没有啊  所以你们选择哪种不要紧 呵呵不过希望有人能够给出最好的答案 我只是一个参与者 发表自己的见解 让大家展开激烈的辩轮  我的观点认为 条条大路通罗马  一个功能不知一个方法实现 呵呵 与其在这里争辩这个 不如去静下心去自己试试 呵呵 每次用到 都定义两种 呵呵 分别作对比 呵呵或许你在偶然的一个测验中会得到令你满意结果呢? 呵呵 别拿香蕉皮砸我啊!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

头像被屏蔽
发表于 2011-6-30 19:50:03 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-1 03:17:13 | 显示全部楼层
我是来拿分的{:7_182:}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-1 17:15:01 | 显示全部楼层
我认为#define的普通用法完全可以用const常量替代,但#define中使用诸如#和##可以完一些高级的文字游戏。完全放弃该用法的话,有时候不得不自己写很长的代码。具体例子可以参见《深入浅出MFC》里关于DECLARE_DYNAMIC宏和IMPLEMENT_DYNAMIC。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-2 10:16:30 | 显示全部楼层
我想应该是看在什么程序里,那个能让程序简练就用那个
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-5 00:07:13 | 显示全部楼层
在我的理解里面,#define是不占用实体内存的,而const则需要分配一个实体内存来给它,但是他是一个受保护的内存实体罢了,受保护后此内存实体的数据就不能被改写,   而对于运用来讲,我绝对#define更容易被人所理解,因为const这东西放在语句的不同地方则表示不同的概念,即它#define复杂更多,所以在一般两者都可以兼用的时候,我会选择#define!

评分

参与人数 1鱼币 +2 收起 理由
灯下幽魂 + 2

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-5 14:41:01 | 显示全部楼层
对于C语言来讲,const 修饰是只读的变量,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。
const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。
定义const 只读变量,具有不可变性。
   例如:
   const int Max=100;
   int Array[Max];
  这里请在Visual C++6.0 里分别创建.c 文件和.cpp 文件测试一下。在.c 文件中,
编译器会提示出错,而在.cpp 文件中则顺利运行。我们知道定义一个数组必须指
定其元素的个数。这也从侧面证实在C 语言中,const 修饰的Max 仍然是变量,只不过是只
读属性罢了;而在C++里,扩展了const 的含义。

    编译器通常不为普通const 只读变量分配存储空间,而是将它们保存在符号表中,这使
得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。
例如:
    #define M 3    //宏常量
    const int N=5; //此时并未将N 放入内存中
    ......
    int i=N;      //此时为N 分配内存,以后不再分配!
    int I=M;      //预编译期间进行宏替换,分配内存
    int j=N;      //没有内存分配
    int J=M;      //再进行宏替换,又一次分配内存!
    const 定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define
一样给出的是立即数,所以,const 定义的只读变量在程序运行过程中只有一份拷贝(因为
它是全局的只读变量,存放在静态区),而#define 定义的宏常量在内存中有若干个拷贝。
#define 宏是在预编译阶段进行替换,而const 修饰的只读变量是在编译的时候确定其值。
#define 宏没有类型,而const 修饰的只读变量具有特定的类型。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-6 15:50:50 | 显示全部楼层
初学者,对于#define和const还真没怎么区分过,期待高手们的精解!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-10 16:06:04 | 显示全部楼层

回帖奖励 +10 鱼币

事在人为嘛 看自己了 没有谁更好的说法
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-10 21:47:26 | 显示全部楼层
我还是个菜鸟,帮不了你哎
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-11 00:09:15 | 显示全部楼层
这个。。。真把我看纠结了有木有:loveliness:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-16 20:14:42 | 显示全部楼层
该怎么说呢,我两个都用的不少。相比我const用的比较多,define用来定义宏的,const常常用来做一下变量,函数等的**,功能更为强大
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2011-7-20 08:03:32 | 显示全部楼层
经常会用#define,尤其是在windows中做message crack。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-19 15:58

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表