const 的使用(100分)

  • 主题发起人 主题发起人 浪里白条
  • 开始时间 开始时间

浪里白条

Unregistered / Unconfirmed
GUEST, unregistred user!
现有一段C的代码如下:
#define CXCNS_csmsos 0X1056
可转换成pascal代码:
const CXCNS_csmsos $1056

可如果C语言中这样写#define CXCNS_csmsos EP_TCcnst->cnst_csmsos
pascal就真的没办法了吗?
有兴趣的再看看我的上一个问题,pascal真的不如C方便的,简直无法对地址操作
请高手指教
 
Delphi不支持宏.
>>#define CXCNS_csmsos EP_TCcnst->cnst_csmsos
为了节省输入时间用:
with EP_TCcnst->cnst_csmsos do
begin
//...
end;
 
我知道在delphi 中只要直接使用
with EP_TCcnst->cnst_csmsos do
begin
//...
end;
可以实现,现在的问题是有一个非常大的系统,由于有好多的参数,这样写不太好控制
对程序的结构化,可读性都有损害,所以想用const把一些参数写在一个单元文中,供团队
开发时使用
如要实现C里的#define CXCNS_csmsos EP_TCcnst->cnst_csmsos
还有别的办法吗?
 
把EP_TCcnst->cnst_csmsos作为参数?
可以贴出一部分你想要做成const unit的代码吗?这样也直观一些。[:D]
 
#define CXS000A ES_sndbf.LUopt.fnc_f000a /* 送信用オプションID 000a */
#define CXS000C ES_sndbf.LUopt.fnc_f000c /* 送信用オプションID 000c */
#define CXS000E ES_sndbf.LUopt.fnc_f000e /* 送信用オプションID 000e */
#define CXS000F ES_sndbf.LUopt.fnc_f000f /* 送信用オプションID 000f */
#define CXS0010 ES_sndbf.LUopt.fnc_f0010 /* 送信用オプションID 0010 */
#define CXS0011 ES_sndbf.LUopt.fnc_f0011 /* 送信用オプションID 0011 */
#define CXS0013 ES_sndbf.LUopt.fnc_f0013 /* 送信用オプションID 0013 */
#define CXS0014 ES_sndbf.LUopt.fnc_f0014 /* 送信用オプションID 0014 */
#define CXS001B ES_sndbf.LUopt.fnc_f001b /* 送信用オプションID 001b */
当然代码还很多,全贴上来不太可能,就是现在有一套系统是unix下C语言编的,现在呢
客户要求我们在linux下重做出来,没有详细的需求分析,他们提供了源程序,
时间呢只有两个月,如重新设计不太可能,涉及的内容太多,而且原的C语言下的结构都
分得比较好,所以就想照着原来的结构改一下,是用的kylix编的
请问怎么简决
 
以我现在这个破水平看来,C语言中的#define CXCNS_csmsos EP_TCcnst->cnst_csmsos 在Delphi中确实
很难实现,这不仅仅是因为Object Pascal语言中没有预处理功能,最主要的是上面的“EP_TCcnst->cnst_csmsos”
是没有数据类型的。
Pascal和C、以及Object Pascal和C++之间,本来就是各有优缺点的。但就功能强弱而言,后者比前者稍微
强一些,这是不争的事实。
我们平时总是对Delphi奇快无比的编译速度倍加称赞。其实,这不仅仅是因为Borland的工程师们把Delphi
这个产品做得非常优秀,最根本的原因是Pascal没有预处理这样的东东,所以,Object Pascal注定就比C++
编译得快!(当然,还有其他原因)

下面是林锐博士在《高质量C++编程指南》中关于const的两段论述

--------------------------------------------------------------------------------------------
const 与 #define的比较
C++ 语言可以用const来定义常量,也可以用 #define来定义常量。但是前者比后者有更多的优点:
(1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进
行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
【规则5-2-1】在C++ 程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。

--------------------------------------------------------------------------------------------

11.1 使用const提高函数的健壮性
看到const关键字,C++程序员首先想到的可能是const常量。这可不是良好的条件反射。如果只知道用const
定义常量,那么相当于把火药仅用于制作鞭炮。const更大的魅力是它可以修饰函数的参数、返回值,甚至
函数的定义体。

const是constant的缩写,“恒定不变”的意思。被const修饰的东西都受到强制保护,可以预防意外的变
动,能提高程序的健壮性。所以很多C++程序设计书籍建议:“Use const whenever you need”。

11.1.1 用const修饰函数的参数

如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加
const修饰,否则该参数将失去输出功能。

const只能修饰输入参数:
如果输入参数采用“指针传递”,那么加const修饰可以防止意外地改动该指针,起到保护作用。

例如StringCopy函数:

void StringCopy(char *strDestination, const char *strSource);

其中strSource是输入参数,strDestination是输出参数。给strSource加上const修饰后,如果函数体内
的语句试图改动strSource的内容,编译器将指出错误。

如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保
护,所以不要加const修饰。

例如不要将函数void Func1(int x) 写成void Func1(const int x)。同理不要将函数void Func2(A a) 写
成void Func2(const A a)。其中A为用户自定义的数据类型。

对于非内部数据类型的参数而言,象void Func(A a) 这样声明的函数注定效率比较底。因为函数体内将产
生A类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。

为了提高效率,可以将函数声明改为void Func(A &a),因为“引用传递”仅借用一下参数的别名而已,不
需要产生临时对象。但是函数void Func(A &a) 存在一个缺点:“引用传递”有可能改变参数a,这是我们
不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为void Func(const A &a)。

以此类推,是否应将void Func(int x) 改写为void Func(const int &x),以便提高效率?完全没有必要,
因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率
几乎相当。

问题是如此的缠绵,我只好将“const &”修饰输入参数的用法总结一下,如表11-1-1所示。

对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。
例如将void Func(A a) 改为void Func(const A &a)。

对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效
率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。

表11-1-1 “const &”修饰输入参数的规则

11.1.2 用const修饰函数的返回值

如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,
该返回值只能被赋给加const修饰的同类型指针。

例如函数

const char * GetString(void);

如下语句将出现编译错误:

char *str = GetString();

正确的用法是

const char *str = GetString();

如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰
没有任何价值。

例如不要把函数int GetInt(void) 写成const int GetInt(void)。

同理不要把函数A GetA(void) 写成const A GetA(void),其中A为用户自定义的数据类型。

如果返回值不是内部数据类型,将函数A GetA(void) 改写为const A & GetA(void)的确能提高效率。
但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就
可以了,否则程序会出错。见6.2节“返回值的规则”。

函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链
式表达。

例如

class A

{…

A & operate = (const A &other); // 赋值函数

};

A a, b, c; // a, b, c 为A的对象



a = b = c; // 正常的链式赋值

(a = b) = c; // 不正常的链式赋值,但合法

如果将赋值函数的返回值加const修饰,那么该返回值的内容不允许被改动。上例中,语句 a = b = c仍然
正确,但是语句 (a = b) = c 则是非法的。



11.1.3 const成员函数

任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成
员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。

以下程序中,类stack的成员函数GetCount仅用于计数,从逻辑上讲GetCount应当为const函数。编译器将
指出GetCount函数中的错误。

class Stack

{

public:

void Push(int elem);

int Pop(void);

int GetCount(void) const; // const成员函数

private:

int m_num;

int m_data[100];

};



int Stack::GetCount(void) const

{
++ m_num; // 编译错误,企图修改数据成员m_num

Pop(); // 编译错误,企图调用非const函数

return m_num;

}

const成员函数的声明看起来怪怪的:const关键字只能放在函数声明的尾部,大概是因为其它地方都已经
被占用了。
 
写得漂亮!
 
自己写一个预编译器。如果不想涉及语法分析,可以考虑把标识符前面加上$之类的字符,
然后自己的预编译器执行替换。
 
多人接受答案了。
 
后退
顶部