我最近的 C++ 编码风格

182 views
Skip to first unread message

Atry

unread,
Dec 12, 2007, 2:41:03 AM12/12/07
to pongba
本文最早是在 Linus 炮轰 C++ 事件以后写下的。当时痛定思痛,对 C++ 一下子有了一个不同的认识。当时写出来这些文字但没有公开,这两个月对这种务实编码风格有了一些实践经验,可以把这篇文章修改修改公布出来了。

自从我接触 boost 以来,我的编码习惯就模仿 boost ,其实那样并不是最好的。 boost 对 C++ 的语言特性的使用没有限制,支持大量的不同的编译器,并且为了这些编译器能在一起工作做了许多别的事情。我没必要支持所有的 C++ 语言特性,也没必要支持所有的编译器。所以我应该有一个不同的编码规范。

1 原则
1.1 封装性
这对我来说不是问题,我早已养成了这个习惯。但是,我必须得把这一条原则写在前面,否则,光看到第二条的读者就会被我误导。
1.2 大胆的硬编码
在模块内部,只要有利于减少代码量,什么手段都可以用!把 C++ 看成比 C 更低级的语言,这样,使用 C++ 的奇技淫巧就不会有心理负担。我认为,增加代码可读性的最有效方法就是减少代码量。
2 C++ 的特性使用
C++ 的特性,大概可以分成4类。低级特性、高级特性、语法糖、没用的特性。
2.1 低级特性
模板属于低级特性。模板以及 boost 中相关的支持,都属于低级特性,比 C 语言更低级的特性。
模板的使用有两种情况:
a) 小的实用工具库,这种实用工具库应该是相对独立的,与其他数据结构不应该有什么关系。要求模板的代码必须尽量简洁。
b) 在 cpp 文件内部用模板来提高效率和减少重复代码。这种方式使用的时候,只是一种复用小规模代码的语法糖,不应暴露在头文件中。
2.2 高级特性
虚函数就是这样一个高级特性。纯虚类用来做 handler 很好。此外,如果真的需要多态的话,也可以用虚函数。
要指出的是,只有真正需要多态才用虚函数,而这种需求其实并不普遍。滥用虚函数最常见的一个借口就是灵活性。大多数时候灵活性都不是需求,而仅仅是因为虚函数能够多态因而那个程序员幻想出来有这样一个需求。
2.3 语法糖
继承、static_cast、reinterpret_cast、运算符重载、函数重载、命名空间、成员函数、类静态成员,这些都是语法糖。我对语法糖没什么意见,能用就用。当然我们知道语法糖很容易被滥用,但我觉得其实只要接口简洁,滥用也滥不到哪里去。这些语法糖的使用应该主要是实现层次的,尽量不要出现在头文件中。
2.4 没用的特性
2.4.1 引用
我受够了!左值/右值、复制引用/复制值 这些问题已经折磨了我很长时间了。我想,如果要用指针就用指针,不用遮遮掩掩说那是一个引用。如果 boost 不需要支持引用的话,我估计 boost 里面跟模板相关的代码可以减少三分之一。我今天决定了,以后除非调用别人的函数要求传引用,否则我绝对不在自己的代码里面用引用。
2.4.2 private 、 public 和 class
任何暴露在头文件中的东西,都理所当然是 public 的。而需要隐藏的实现,如果有办法隐藏,自然可以放到 cpp 文件中。用 C 的那些办法来隐藏实现,比用自欺欺人的 private 好多了。我们知道 class 和 struct 在 C++ 中的唯一区别就是 class 默认成员为 private ,由于 private 是自欺欺人的,所以我们在一切地方都用 struct 代替 class
2.4.3 dynamic_cast和typeid
使用多态的唯一正确方式就是用虚函数,正确编码的代码一定可以避免 dynamic_cast 。
3 关于模块
3.1 一个模块就是一个 cpp 文件,最小几十行代码,最大不超过1000行。函数分为两类。一类是出现在头文件中的接口,另一类是模块内部用的,仅仅用来避免重复代码。
3.2 头文件。直接暴露数据结构就是编写良好接口的捷径!试图隐藏实现是愚蠢的。此外,接口函数应该以自由函数为主,但也可以使用成员函数。对于需要严格分离的接口,可以使用纯虚类,纯虚函数来定义。
3.3 组织模块内部编码时,唯一的目标就是代码最短,避免任何包装开销。内部不必是良好定义的,不必是正交的,为了减少代码,甚至可以大量的使用宏。同时,把模板当成一种低级特性来用,只要有利于简短代码量,一切奇技淫巧都可以用。
4 模块接口的参数传递
4.1 缓冲区和字符串
对于接受字符串的函数,接受的参数是首地址指针和长度,或者首末地址也可以。当然,字符串和缓冲区的具体存储字段,可以用 std::vector<char>
4.2 时间
不使用任何 C++ 类,一律传整数,比如 time_t 或者其他自定义的整数 。强大的 Boost.Date_Time 可以拜拜了。
4.3 指针
一切想要传引用的地方,都明白无误的传指针。
4.4 模板参数
因为已经规定了不使用引用,模板参数就可以不必考虑引用,每一个参数都可以传值。类似这样:template<typename T0, typename T1> void func(T0 t0, T1 t1);

王磊

unread,
Dec 12, 2007, 2:55:29 AM12/12/07
to pon...@googlegroups.com
增加代码可读性的最有效方法就是减少代码量。
这一点。
 
 
我有疑问。
我觉得代码不光是为了实现功能还是给人读的。
既然如此对于代码量而言不一定要也别精简,都写的晦涩不明。
多写几行能表意十分明确也好。
至于特性可以不用的就不要用。
我C++水平很差,一个很一般的程序员而已。
因为做外包,发现代码的可读性至关重要。其中一点就是别把代码写的跟诗一样。
最好都是 1+1。。。要么就写大量的注释。不然代码就没有升级扩展的空间了。
最好的诗,不是只有诗人能看懂,是所有认识字的人都明白 :)

 
在07-12-12,Atry <pop....@gmail.com> 写道:

Atry

unread,
Dec 12, 2007, 3:02:03 AM12/12/07
to pon...@googlegroups.com
我说得不清楚。这么说吧,减少代码量是指的"在模块内部重用代码减少代码量",而不是缩短变量命名之类的办法。

重用代码不一定能减少代码量。因为有可能额外的封装代码比其减少的代码还要多。当重用代码能减少代码量的时候,这个重用代码通常也有利于提高代码可读性。

在模块内部重用代码的方式:提取公共部分的函数(可能带模板参数)、宏

在07-12-12, 王磊 <wangle...@gmail.com> 写道:

王磊

unread,
Dec 12, 2007, 3:10:59 AM12/12/07
to pon...@googlegroups.com
汗,我激动了hoho
 
之前被客户的各种东西闹的,不加注释,写得跟天书似的。。。

 
在07-12-12,Atry <pop....@gmail.com> 写道:

Googol Lee

unread,
Dec 12, 2007, 3:25:46 AM12/12/07
to pon...@googlegroups.com
对于private和public的看法很赞同,不过c++目前还不能很好的分离头和实现,如果强行分离会损失些东西。

另外,我现在有个原则,完全不用参数默认值。所有需要参数默认值的地方都可以用重载解决,而使用参数默认值又会有一些很隐晦的问题(尤其和继承联系起来),干脆不用。

在 07-12-12,王磊<wangle...@gmail.com> 写道:


--
新的理论从少数人的主张到一统天下,并不是因为这个理论说服了别人抛弃旧观点,而是因为一代人的逝去。

My blog: http://googollee.blog.163.com

pi1ot

unread,
Dec 12, 2007, 3:37:36 AM12/12/07
to TopLanguage
我也搞不懂引用是用来干吗的,貌似这几年就没使过这玩意。
关于time_t,如果你经常需要输出datetime字符串,或者以day、week、month、year为单位进行比较和计算,岂不是每次都要再
调用一次localtime_r(),更麻烦。

On 12月12日, 下午3时41分, Atry <pop.a...@gmail.com> wrote:
> 本文最早是在 Linus 炮轰 C++ 事件以后写下的。当时痛定思痛,对 C++
> 一下子有了一个不同的认识。当时写出来这些文字但没有公开,这两个月对这种务实编码风格有了一些实践经验,可以把这篇文章修改修改公布出来了。
>
> 自从我接触 boost 以来,我的编码习惯就模仿 boost ,其实那样并不是最好的。 boost 对 C++
> 的语言特性的使用没有限制,支持大量的不同的编译器,并且为了这些编译器能在一起工作做了许多别的事情。我没必要支持所有的 C++
> 语言特性,也没必要支持所有的编译器。所以我应该有一个不同的编码规范。
>
> 1 原则
> 1.1 封装性
> 这对我来说不是问题,我早已养成了这个习惯。但是,我必须得把这一条原则写在前面,否则,光看到第二条的读者就会被我误导。
> 1.2 大胆的硬编码
> 在模块内部,只要有利于减少代码量,什么手段都可以用!把 C++ 看成比 C 更低级的语言,这样,使用 C++
> 的奇技淫巧就不会有心理负担。我认为,增加代码可读性的最有效方法就是减少代码量。
> 2 C++ 的特性使用
> C++ 的特性,大概可以分成4类。低级特性、高级特性、语法糖、没用的特性。
> 2.1 低级特性
> 模板属于低级特性。模板以及 boost 中相关的支持,都属于低级特性,比 C 语言更低级的特性。
> 模板的使用有两种情况:
> a) 小的实用工具库,这种实用工具库应该是相对独立的,与其他数据结构不应该有什么关系。要求模板的代码必须尽量简洁。
> b) 在 cpp 文件内部用模板来提高效率和减少重复代码。这种方式使用的时候,只是一种复用小规模代码的语法糖,不应暴露在头文件中。
> 2.2 高级特性
> 虚函数就是这样一个高级特性。纯虚类用来做 handler 很好。此外,如果真的需要多态的话,也可以用虚函数。
> 要指出的是,只有真正需要多态才用虚函数,而这种需求其实并不普遍。滥用虚函数最常见的一个借口就是灵活性。大多数时候灵活性都不是需求,而仅仅是因为虚函数能-够多态因而那个程序员幻想出来有这样一个需求。
> 2.3 语法糖
> 继承、static_cast、reinterpret_cast、运算符重载、函数重载、命名空间、成员函数、类静态成员,这些都是语法糖。我对语法糖没什么-意见,能用就用。当然我们知道语法糖很容易被滥用,但我觉得其实只要接口简洁,滥用也滥不到哪里去。这些语法糖的使用应该主要是实现层次的,尽量不要出现在头文-件中。
> 2.4 没用的特性
> 2.4.1 引用
> 我受够了!左值/右值、复制引用/复制值 这些问题已经折磨了我很长时间了。我想,如果要用指针就用指针,不用遮遮掩掩说那是一个引用。如果 boost
> 不需要支持引用的话,我估计 boost
> 里面跟模板相关的代码可以减少三分之一。我今天决定了,以后除非调用别人的函数要求传引用,否则我绝对不在自己的代码里面用引用。
> 2.4.2 private 、 public 和 class
> 任何暴露在头文件中的东西,都理所当然是 public 的。而需要隐藏的实现,如果有办法隐藏,自然可以放到 cpp 文件中。用 C
> 的那些办法来隐藏实现,比用自欺欺人的 private 好多了。我们知道 class 和 struct 在 C++ 中的唯一区别就是 class
> 默认成员为 private ,由于 private 是自欺欺人的,所以我们在一切地方都用 struct 代替 class
> 2.4.3 dynamic_cast和typeid
> 使用多态的唯一正确方式就是用虚函数,正确编码的代码一定可以避免 dynamic_cast 。
> 3 关于模块
> 3.1 一个模块就是一个 cpp
> 文件,最小几十行代码,最大不超过1000行。函数分为两类。一类是出现在头文件中的接口,另一类是模块内部用的,仅仅用来避免重复代码。
> 3.2头文件。直接暴露数据结构就是编写良好接口的捷径!试图隐藏实现是愚蠢的。此外,接口函数应该以自由函数为主,但也可以使用成员函数。对于需要严格分离的-接口,可以使用纯虚类,纯虚函数来定义。
> 3.3组织模块内部编码时,唯一的目标就是代码最短,避免任何包装开销。内部不必是良好定义的,不必是正交的,为了减少代码,甚至可以大量的使用宏。同时,把模-板当成一种低级特性来用,只要有利于简短代码量,一切奇技淫巧都可以用。

徐梓鼎

unread,
Dec 12, 2007, 3:47:59 AM12/12/07
to pon...@googlegroups.com
使用默认参数有什么隐晦的问题?

在2007-12-12来自goog...@gmail.com的邮件[[TopLanguage] Re: 我最近的 C++ 编码风格]中写到:

> 另外,我现在有个原则,完全不用参数默认值。所有需要参数默认值的地方都可以用重载解决,而使用
>参数默认值又会有一些很隐晦的问题(尤其和继承联系起来),干脆不用。
>

pongba

unread,
Dec 12, 2007, 3:48:43 AM12/12/07
to pon...@googlegroups.com
On Dec 12, 2007 3:41 PM, Atry <pop....@gmail.com> wrote:
2.4.1 引用
我受够了!左值/右值、复制引用/复制值 这些问题已经折磨了我很长时间了。我想,如果要用指针就用指针,不用遮遮掩掩说那是一个引用。如果 boost 不需要支持引用的话,我估计 boost 里面跟模板相关的代码可以减少三分之一。我今天决定了,以后除非调用别人的函数要求传引用,否则我绝对不在自己的代码里面用引用。

const&也不用吗?那用什么来接受临时对象呢?
 
2.4.2 private 、 public 和 class
任何暴露在头文件中的东西,都理所当然是 public 的。而需要隐藏的实现,如果有办法隐藏,自然可以放到 cpp 文件中。用 C 的那些办法来隐藏实现,比用自欺欺人的 private 好多了。我们知道 class 和 struct 在 C++ 中的唯一区别就是 class 默认成员为 private ,由于 private 是自欺欺人的,所以我们在一切地方都用 struct 代替 class

这么做的动机需要更多解释。是为了解二进制依赖吗?如果是像std::complex这样的值类型又如何呢?

--
刘未鹏(pongba)|C++的罗浮宫
http://blog.csdn.net/pongba
TopLanguage
http://groups.google.com/group/pongba

pi1ot

unread,
Dec 12, 2007, 3:49:41 AM12/12/07
to TopLanguage
维护性方面,项目大了代码多了之后的问题,

On 12月12日, 下午4时47分, 徐梓鼎 <xuzid...@gmail.com> wrote:
> 使用默认参数有什么隐晦的问题?
>
> 在2007-12-12来自googol...@gmail.com的邮件[[TopLanguage] Re: 我最近的 C++ 编码风格]中写到:
>
>
>
> > 另外,我现在有个原则,完全不用参数默认值。所有需要参数默认值的地方都可以用重载解决,而使用
> >参数默认值又会有一些很隐晦的问题(尤其和继承联系起来),干脆不用。- 隐藏被引用文字 -
>
> - 显示引用的文字 -

redsea

unread,
Dec 12, 2007, 3:52:02 AM12/12/07
to TopLanguage
> 2.4.1 引用
> 我受够了!左值/右值、复制引用/复制值 这些问题已经折磨了我很长时间了。我想,如果要用指针就用指针,不用遮遮掩掩说那是一个引用。如果 boost
你比我更激进.
我也很讨厌调用一个函数, 要去查原型才知道我的变量会不会被修改, 所以函数参数中我不用引用.
不过我还会在一些地方用引用, 就是类的成员变量, 用引用, 表明了态度--不能为空 & 只能设置一次.

> 4.2 时间
> 不使用任何 C++ 类,一律传整数,比如 time_t 或者其他自定义的整数 。强大的 Boost.Date_Time 可以拜拜了。
时间啊, 时间啊, c/c++, delphi 里面的时间相关的函数, 都让人头痛无比, 好多个类型, 好多个调用, 让我从来都记不
住, 每次要查找的时候, 都感到很悲愤.
python 里面也不是很简单.
我讨厌时间.

redsea

unread,
Dec 12, 2007, 4:00:50 AM12/12/07
to TopLanguage
> const&也不用吗?那用什么来接受临时对象呢?

我的做法:

放弃需要使用临时对象的 API 设计. C 没有用临时对象, 也做了这么多软件.
用临时对象又带来一堆的问题, 很讨厌.

我宁愿失去灵活性, 失去操作符重载的方便(反正我也不怎么用), 也不想去弄值对象(&引用) 做参数, 返回值对象(&引用) 的东西.

多余的灵活性带来不必要的复杂性.

还是老 C++ 的方法, 就是 C 带上类的能力, 再加上模板, 这样做就够了.

Atry

unread,
Dec 12, 2007, 4:02:17 AM12/12/07
to pon...@googlegroups.com


在07-12-12,pongba <pon...@gmail.com> 写道:


On Dec 12, 2007 3:41 PM, Atry <pop....@gmail.com> wrote:
2.4.1 引用
我受够了!左值/右值、复制引用/复制值 这些问题已经折磨了我很长时间了。我想,如果要用指针就用指针,不用遮遮掩掩说那是一个引用。如果 boost 不需要支持引用的话,我估计 boost 里面跟模板相关的代码可以减少三分之一。我今天决定了,以后除非调用别人的函数要求传引用,否则我绝对不在自己的代码里面用引用。

const&也不用吗?那用什么来接受临时对象呢?

你指的是 void foo(std::string const & s); foo("ssss"); 这样的代码吗?我为什么要写这样的代码?我为什么要依赖于临时对象?我传字符串的时候,宁愿传std::pair<char const*, char const*> ,一样可以用 Boost.Range 来操作,一样可以用上 STL 的算法。

2.4.2 private 、 public 和 class
任何暴露在头文件中的东西,都理所当然是 public 的。而需要隐藏的实现,如果有办法隐藏,自然可以放到 cpp 文件中。用 C 的那些办法来隐藏实现,比用自欺欺人的 private 好多了。我们知道 class 和 struct 在 C++ 中的唯一区别就是 class 默认成员为 private ,由于 private 是自欺欺人的,所以我们在一切地方都用 struct 代替 class

这么做的动机需要更多解释。是为了解二进制依赖吗?如果是像std::complex这样的值类型又如何呢?

《Unix 编程艺术》上面举了个例子,他说,如果你给他看代码,他看了还是不明白。如果你把数据结构他看了,行了,不需要看代码了。所以,隐藏数据结构而只提供一组方法是本末倒置。

katkat lim

unread,
Dec 12, 2007, 4:12:13 AM12/12/07
to pon...@googlegroups.com
我也很讨厌引用,有时候搞半天还不明白,干脆自己来管理内存,全部用指针。

徐梓鼎

unread,
Dec 12, 2007, 4:29:41 AM12/12/07
to pon...@googlegroups.com
能举例说说吗?我一直用,没发现啥问题啊

在2007-12-12来自pilo...@gmail.com的邮件[[TopLanguage] Re: 我最近的 C++
编码风格]中写到:

> 维护性方面,项目大了代码多了之后的问题,



--
DEBUG是件快乐的事情 <xuzi...@gmail.com>


Googol Lee

unread,
Dec 12, 2007, 4:58:26 AM12/12/07
to pon...@googlegroups.com
比如:
class Base
{
virtual func(int arg = 1);
};

class Derived : public Base
{
virtual func(int arg = 2);
};

Base *b = new Derived();
b->func(); // 默认参数是多少?

至于由于维护造成重载函数间有的有默认参数,有的没有,结果编译器调用的不是真正预期调用的(比如删掉了一个默认参数,结果编译器不报错,转去调用领一个了)……

在 07-12-12,徐梓鼎<xuzi...@gmail.com> 写道:

pi1ot

unread,
Dec 12, 2007, 5:04:20 AM12/12/07
to TopLanguage
只是你自己写自己调用,并且你的记忆力足够强劲的话是不会有什么问题,但是如果是对外提供的而你突然有一天觉得把默认参数值改一下会更合理,唯一的办法
只有通知所有调用方复查代码逻辑,以确保不会出现隐式的逻辑依赖。

On 12月12日, 下午5时29分, 徐梓鼎 <xuzid...@gmail.com> wrote:
> 能举例说说吗?我一直用,没发现啥问题啊
>
> 在2007-12-12来自pilot...@gmail.com的邮件[[TopLanguage] Re: 我最近的 C++
> 编码风格]中写到:
>
> > 维护性方面,项目大了代码多了之后的问题,
>
> --
> DEBUG是件快乐的事情 <xuzid...@gmail.com>

徐梓鼎

unread,
Dec 12, 2007, 5:20:30 AM12/12/07
to pon...@googlegroups.com
Base呗,我在虚函数上没用过用缺省参数,那位能告诉我在需要运行时多态的地方
使用缺省参数有任何好处吗?

在2007-12-12来自goog...@gmail.com的邮件[[TopLanguage] Re: 我最近的 C++
编码风格]中写到:


> 比如:
> class Base
> {
> virtual func(int arg = 1);
> };
>
> class Derived : public Base
> {
> virtual func(int arg = 2);
> };
>
> Base *b = new Derived();
> b->func(); // 默认参数是多少?



--
DEBUG是件快乐的事情 <xuzi...@gmail.com>


徐梓鼎

unread,
Dec 12, 2007, 5:22:11 AM12/12/07
to pon...@googlegroups.com
哦,明白了,谢谢。我一般在自己的private函数当中用的多,因为这样我觉得可
以很方便的修改缺省值。
在2007-12-12来自pilo...@gmail.com的邮件[[TopLanguage] Re: 我最近的 C++
编码风格]中写到:

> 只是你自己写自己调用,并且你的记忆力足够强劲的话是不会有什么问题,但是如果是对外提供的而
> 你突然有一天觉得把默认参数值改一下会更合理,唯一的办法
> 只有通知所有调用方复查代码逻辑,以确保不会出现隐式的逻辑依赖。


--
DEBUG是件快乐的事情 <xuzi...@gmail.com>


WaterWalk

unread,
Dec 12, 2007, 6:12:58 AM12/12/07
to TopLanguage


On Dec 12, 5:02 pm, Atry <pop.a...@gmail.com> wrote:
> 在07-12-12,pongba <pon...@gmail.com> 写道:
>
>
>
> > On Dec 12, 2007 3:41 PM, Atry <pop.a...@gmail.com> wrote:
>
> > > 2.4.1 引用
> > > 我受够了!左值/右值、复制引用/复制值 这些问题已经折磨了我很长时间了。我想,如果要用指针就用指针,不用遮遮掩掩说那是一个引用。如果 boost
> > > 不需要支持引用的话,我估计 boost
> > > 里面跟模板相关的代码可以减少三分之一。我今天决定了,以后除非调用别人的函数要求传引用,否则我绝对不在自己的代码里面用引用。
>
> > const&也不用吗?那用什么来接受临时对象呢?
>
> 你指的是 void foo(std::string const & s); foo("ssss");
> 这样的代码吗?我为什么要写这样的代码?我为什么要依赖于临时对象?我传字符串的时候,宁愿传std::pair<char const*, char
> const*> ,一样可以用 Boost.Range 来操作,一样可以用上 STL 的算法。
>

但如果用其它库就不一定了。其它库可能就是要直接传递一个string引用。

莫华枫

unread,
Dec 12, 2007, 6:46:27 AM12/12/07
to pon...@googlegroups.com
引用可以为引用语义和值语义提供相同的形式,对于构建泛型算法有至关重要的作用。

longzhi

unread,
Dec 12, 2007, 8:44:01 AM12/12/07
to TopLanguage
看看《代码大全》code complete,对编码风格会有很好的帮助。

不用C++的偶的愚见。:-)

On 12月12日, 下午3时41分, Atry <pop.a...@gmail.com> wrote:
> 本文最早是在 Linus 炮轰 C++ 事件以后写下的。当时痛定思痛,对 C++
> 一下子有了一个不同的认识。当时写出来这些文字但没有公开,这两个月对这种务实编码风格有了一些实践经验,可以把这篇文章修改修改公布出来了。
>
> 自从我接触 boost 以来,我的编码习惯就模仿 boost ,其实那样并不是最好的。 boost 对 C++
> 的语言特性的使用没有限制,支持大量的不同的编译器,并且为了这些编译器能在一起工作做了许多别的事情。我没必要支持所有的 C++
> 语言特性,也没必要支持所有的编译器。所以我应该有一个不同的编码规范。
>
> 1 原则
> 1.1 封装性
> 这对我来说不是问题,我早已养成了这个习惯。但是,我必须得把这一条原则写在前面,否则,光看到第二条的读者就会被我误导。
> 1.2 大胆的硬编码
> 在模块内部,只要有利于减少代码量,什么手段都可以用!把 C++ 看成比 C 更低级的语言,这样,使用 C++
> 的奇技淫巧就不会有心理负担。我认为,增加代码可读性的最有效方法就是减少代码量。
> 2 C++ 的特性使用
> C++ 的特性,大概可以分成4类。低级特性、高级特性、语法糖、没用的特性。
> 2.1 低级特性
> 模板属于低级特性。模板以及 boost 中相关的支持,都属于低级特性,比 C 语言更低级的特性。
> 模板的使用有两种情况:
> a) 小的实用工具库,这种实用工具库应该是相对独立的,与其他数据结构不应该有什么关系。要求模板的代码必须尽量简洁。
> b) 在 cpp 文件内部用模板来提高效率和减少重复代码。这种方式使用的时候,只是一种复用小规模代码的语法糖,不应暴露在头文件中。
> 2.2 高级特性
> 虚函数就是这样一个高级特性。纯虚类用来做 handler 很好。此外,如果真的需要多态的话,也可以用虚函数。
> 要指出的是,只有真正需要多态才用虚函数,而这种需求其实并不普遍。滥用虚函数最常见的一个借口就是灵活性。大多数时候灵活性都不是需求,而仅仅是因为虚函数能-够多态因而那个程序员幻想出来有这样一个需求。
> 2.3 语法糖
> 继承、static_cast、reinterpret_cast、运算符重载、函数重载、命名空间、成员函数、类静态成员,这些都是语法糖。我对语法糖没什么-意见,能用就用。当然我们知道语法糖很容易被滥用,但我觉得其实只要接口简洁,滥用也滥不到哪里去。这些语法糖的使用应该主要是实现层次的,尽量不要出现在头文-件中。
> 2.4 没用的特性
> 2.4.1 引用
> 我受够了!左值/右值、复制引用/复制值 这些问题已经折磨了我很长时间了。我想,如果要用指针就用指针,不用遮遮掩掩说那是一个引用。如果 boost
> 不需要支持引用的话,我估计 boost
> 里面跟模板相关的代码可以减少三分之一。我今天决定了,以后除非调用别人的函数要求传引用,否则我绝对不在自己的代码里面用引用。
> 2.4.2 private 、 public 和 class
> 任何暴露在头文件中的东西,都理所当然是 public 的。而需要隐藏的实现,如果有办法隐藏,自然可以放到 cpp 文件中。用 C
> 的那些办法来隐藏实现,比用自欺欺人的 private 好多了。我们知道 class 和 struct 在 C++ 中的唯一区别就是 class
> 默认成员为 private ,由于 private 是自欺欺人的,所以我们在一切地方都用 struct 代替 class
> 2.4.3 dynamic_cast和typeid
> 使用多态的唯一正确方式就是用虚函数,正确编码的代码一定可以避免 dynamic_cast 。
> 3 关于模块
> 3.1 一个模块就是一个 cpp
> 文件,最小几十行代码,最大不超过1000行。函数分为两类。一类是出现在头文件中的接口,另一类是模块内部用的,仅仅用来避免重复代码。
> 3.2头文件。直接暴露数据结构就是编写良好接口的捷径!试图隐藏实现是愚蠢的。此外,接口函数应该以自由函数为主,但也可以使用成员函数。对于需要严格分离的-接口,可以使用纯虚类,纯虚函数来定义。
> 3.3组织模块内部编码时,唯一的目标就是代码最短,避免任何包装开销。内部不必是良好定义的,不必是正交的,为了减少代码,甚至可以大量的使用宏。同时,把模-板当成一种低级特性来用,只要有利于简短代码量,一切奇技淫巧都可以用。

Kyle M. Lee

unread,
Dec 12, 2007, 9:39:16 AM12/12/07
to TopLanguage
On 12月12日, 下午3时41分, Atry <pop.a...@gmail.com> wrote:
> 2.4.1 引用
> 我受够了!左值/右值、复制引用/复制值 这些问题已经折磨了我很长时间了。我想,如果要用指针就用指针,不用遮遮掩掩说那是一个引用。如果 boost
> 不需要支持引用的话,我估计 boost
> 里面跟模板相关的代码可以减少三分之一。我今天决定了,以后除非调用别人的函数要求传引用,否则我绝对不在自己的代码里面用引用。
可能是我的经验不足? :P
我倒是倾向于在各个输入参数都使用引用的。需要使用返回值作为输出新生的对象的时候,使用指针。
我用引用是因为可以简化函数内部的参数检查什么的。倒没有发现什么难受的地方。:)
我确实没有理解楼主不使用引用的理由,呵呵~

> 2.4.2 private 、 public 和 class
> 任何暴露在头文件中的东西,都理所当然是 public 的。而需要隐藏的实现,如果有办法隐藏,自然可以放到 cpp 文件中。用 C
> 的那些办法来隐藏实现,比用自欺欺人的 private 好多了。我们知道 class 和 struct 在 C++ 中的唯一区别就是 class
> 默认成员为 private ,由于 private 是自欺欺人的,所以我们在一切地方都用 struct 代替 class
这个应该是数据结构和接口抽象不同之处吧?还是其他的用意?

我觉得private作用还是一大把的。歪门邪道的就像nokia公布的SDK一样,人家修改下header,照样可以发布SDK。
:D有时候,发现自己加个头文件,或者声明个接口,然后可以启用隐藏关口,觉得真是....
喜欢接口和实现分离的,多使用bridge就可以了。紧密耦合数据,当然是定义结构体的。
我的对头文件,以及楼主后续的观点,认为,在C++中,dirty code最终是要落地的,漂亮之处是把好的接口给调用者的。

shark

unread,
Dec 12, 2007, 5:40:37 AM12/12/07
to pon...@googlegroups.com
同意,引用就如此不堪?
引用/指针几乎就是对象的双面,我现在做得事情可能刚好和你相反,尽量不暴露指针(仅在库内部使用,包括new/delete),使用中等内存模型,加大 栈空间。
这样可以在库外部尽可能杜绝指针使用,减少出现错误内存操作的可能性,这样在外部就仅仅是对象语意(传引用/传对象),问题变得很简单,可以专心于业务逻 辑。
对于模块内部处理的意见一致,什么都可以用,而且现在我用到最多的就是asm关键字和宏,有时真希望下个C++标准可以把D的mixin吸收进来,做个可 以控制展开范围的宏,不用像现在一样#define ...然后#undef ....一样费力。
至于语法糖,可能一两个这样特性还是糖,但多了可能就产生质变,可以当饭吃了,呵呵。

buhongbo

unread,
Dec 12, 2007, 8:48:00 PM12/12/07
to pon...@googlegroups.com
我有一点体会:在需要二重指针的地方,用指针的引用很好,减少心理负担。
 
void f(int*& p)
{
   p = new int(5);
   // ...
}
 
int* pi = 0;
f(pi);
 

buhongbo
2007-12-13

发件人: pi1ot
发送时间: 2007-12-12 16:37:43
收件人: TopLanguage
抄送:
主题: [TopLanguage] Re: 我最近的 C++ 编码风格

Googol Lee

unread,
Dec 12, 2007, 9:52:19 PM12/12/07
to pon...@googlegroups.com
觉得指针和引用是在两个层次上区分值语义和引用语义。比如

void func(someClass *a); // 一般传一个class都是这么传吧?但这个a实际是引用语义。这样someClass就缺乏了值语义
void func(int a); // 这个a是值语义
void func(int *a); // 这才是a的引用语义,只是这种语义是从内存模型层面上区分的

而如果用引用,就可以在语法层面区分
void func(someClass &a); // 明确的引用语义
void func(const someClass &a); // 明确的值语义
void func(int &a); //引用语义
void func(int a); //依旧是值语义

但结果这样做却导致了值语义的不统一,这也是楼主抛弃引用的原因吧?(为了解决一个问题,引入了另一个问题)

估计等0x加入右值引用后,这个问题会好一些。


在 07-12-13,buhongbo<buho...@gmail.com> 写道:

red...@gmail.com

unread,
Dec 12, 2007, 10:15:49 PM12/12/07
to pon...@googlegroups.com
Googol Lee 写道:

> 觉得指针和引用是在两个层次上区分值语义和引用语义。比如
>
> void func(someClass *a); // 一般传一个class都是这么传吧?但这个a实际是引用语义。这样someClass就缺乏了值语义
> void func(int a); // 这个a是值语义
> void func(int *a); // 这才是a的引用语义,只是这种语义是从内存模型层面上区分的
>
>
没有引用的时候, 调用 func(ptr_to_a), 很明确地知道, 你顶多该 ptr_to_a
指向的对象的内容, 你不会将我的指针改掉. 出现了引用之后, 每个调用, 都要检
查一下, 函数的原型是怎样的, 是指针还是引用, 引用是不是常量引用, 常量在哪
里, 才能知道我的实参会不会有机会被改掉 ---- 当然你可能说, 设计得好的 API
不应该有这样的担心, 但是, 不在某几个项目彻底用上几次, 我还不知道这个 API
好不好呢.
我觉得太累了, 宁愿用指针, 调用者, 被调者, 都知道这是值语义; 被调用者就
辛苦一点, 检查一下参数有效性吧.

Googol Lee

unread,
Dec 12, 2007, 10:51:38 PM12/12/07
to pon...@googlegroups.com
恩,确实,一个是遗留原因,另一个是现在的解决方案并不完善。

其实,一般内部使用的函数,很少检查指针是否为空,这个动作是在接口函数里保证的。

在 07-12-13,red...@gmail.com<red...@gmail.com> 写道:

red...@gmail.com

unread,
Dec 12, 2007, 10:56:44 PM12/12/07
to pon...@googlegroups.com
至于操作符重载的时候, 不用引用不行这个问题, 对我来说, 都不是问题了, 因为
我不用操作符重载.
我宁愿老老实实用函数调用的方式写代码, 虽然不漂亮, 但是可靠, 不容易碰到陷阱.

本来要解决应用领域的问题已经不容易了, 还要我脑海里面, 随时装一堆规则: 这
个操作符重载必须返回引用, 那个必须返回对象, 这个生活未免太过沉重 ---- 对
于很多人可能不成问题, 但是我的机械记忆力奇差, 不想在这里花费精力.

至于 swap 之类, 我宁愿用
template <T>
void swap(T *a, T *b)
{

}

由于inline, 调用起来不会因为指针而有额外开销, 而我调用 swap 的时候, 用到
&, 我并不会觉得不方便, 反而让我明确地知道, 这两个参数可能会被修改, 以后
阅读代码的时候, 更容易看出来, 变量在哪里被修改 ---- 写代码是一次, 调试是
n次, 阅读代码, 是整个代码生命周期, 比项目可能还长命.

说到调用 swap 的时候, 传一个 NULL 进去 ? 那是谁, 站出来 ? 下次不改, 就炒掉.


总结, 引用解决问题的同时带来其他问题, 指针更简单更一致. 我脑袋里面的 L1,
L2 cache 不够大, 应付业务层面问题的时候, cache 已经满了, 不想再面对这些
问题.


BTW:
其实我对用 macro 实现 SWAP 也不反感,
如果一个人给 SWAP() 提供的参数还带副作用, 那只能证明这个人太迟钝, 没有什
么其他好说的了, 这么简单的规则都记不住.

macro 被 C++ 评为很邪恶, 但其实我觉得macro 最大的问题是没有作用域, 容易
冲突. 参数副作用这个问题, 比起 C++ n 多规则, 实在是一个太简单的规则.


red...@gmail.com 写道:

Googol Lee

unread,
Dec 12, 2007, 11:59:35 PM12/12/07
to pon...@googlegroups.com
提到了运算符重载……

其实运算符重载的诱惑是最大的,也是最危险的吧。毕竟大家都不是学数学的,对于某个运算符需要满足哪些要求并不了然于心。

而且,很多时候理论上的东西到了实际层面就变了,比如:两个time_t相减,返回的应该是个类似timediff的东西,而不是time_t类型,但如果真的写timediff
& operator-(time_t,
time_t),实际用起来又会遇到很多这样那样的问题(比如STL好像要operator-满足一定要求吧?)

这个目前除了干净的禁止运算符重载,有比较好的解决办法么?

在 07-12-13,red...@gmail.com<red...@gmail.com> 写道:

shark

unread,
Dec 12, 2007, 4:53:39 PM12/12/07
to pon...@googlegroups.com
把D的mixin集成进来做加强版的宏

wing fire

unread,
Dec 13, 2007, 12:11:35 AM12/13/07
to pon...@googlegroups.com


在07-12-13,red...@gmail.com <red...@gmail.com> 写道:
至于操作符重载的时候, 不用引用不行这个问题, 对我来说, 都不是问题了, 因为
我不用操作符重载.
我宁愿老老实实用函数调用的方式写代码, 虽然不漂亮, 但是可靠, 不容易碰到陷阱.

本来要解决应用领域的问题已经不容易了, 还要我脑海里面, 随时装一堆规则: 这
个操作符重载必须返回引用, 那个必须返回对象, 这个生活未免太过沉重 ---- 对
于很多人可能不成问题, 但是我的机械记忆力奇差, 不想在这里花费精力.

至于 swap 之类, 我宁愿用
template <T>
void swap(T *a, T *b)
{

}

由于inline, 调用起来不会因为指针而有额外开销, 而我调用 swap 的时候, 用到
&, 我并不会觉得不方便, 反而让我明确地知道, 这两个参数可能会被修改, 以后
阅读代码的时候, 更容易看出来, 变量在哪里被修改 ---- 写代码是一次, 调试是
n次, 阅读代码, 是整个代码生命周期, 比项目可能还长命.

说到调用 swap 的时候, 传一个 NULL 进去 ? 那是谁, 站出来 ? 下次不改, 就炒掉.

你自己挖的坑,自己不盖盖子,说掉进去的人活该?

总结, 引用解决问题的同时带来其他问题, 指针更简单更一致. 我脑袋里面的 L1,
L2 cache 不够大, 应付业务层面问题的时候, cache 已经满了, 不想再面对这些
问题.


BTW:
其实我对用 macro 实现 SWAP 也不反感,
如果一个人给 SWAP() 提供的参数还带副作用, 那只能证明这个人太迟钝, 没有什
么其他好说的了, 这么简单的规则都记不住.

你可以测试一下你的同行,或者新来的 同事,在有编译器帮助的情况下,看看多少人能够一次把SWAP写的完全正确?

macro 被 C++ 评为很邪恶, 但其实我觉得macro 最大的问题是没有作用域, 容易
冲突. 参数副作用这个问题, 比起 C++ n 多规则, 实在是一个太简单的规则.

我认为,如果编译器可以制止的陷阱,那就不算陷阱.编译器无能为力的才算.对于宏,编译器基本残废.

pongba

unread,
Dec 13, 2007, 2:21:04 AM12/13/07
to pon...@googlegroups.com
说实话C#在这个问题上做得就不错,ref和out关键字的引入非常醒目地标识出了哪些变量会被修改。

wing fire

unread,
Dec 13, 2007, 2:52:11 AM12/13/07
to pon...@googlegroups.com


在07-12-13,pongba <pon...@gmail.com> 写道:
说实话C#在这个问题上做得就不错,ref和out关键字的引入非常醒目地标识出了哪些变量会被修改。

但是,如果我没有看定义,C#的客户代码也不知道是ref还是out.如果看了定义,C++中也就知道那些会被修改了.

Googol Lee

unread,
Dec 13, 2007, 3:12:50 AM12/13/07
to pon...@googlegroups.com
我倒觉得c#这里做的太冗余了,关键字和概念搞那么多……

在 07-12-13,wing fire<wing...@gmail.com> 写道:

red...@gmail.com

unread,
Dec 13, 2007, 5:15:28 AM12/13/07
to pon...@googlegroups.com


说到调用 swap 的时候, 传一个 NULL 进去 ? 那是谁, 站出来 ? 下次不改, 就炒掉.

你自己挖的坑,自己不盖盖子,说掉进去的人活该?
  或许是我的 C 背景重吧, 虽然, 可以不写 C 的话, 我从不会去写 C.

  swap 的语义就是如此, 参数是指针参数, 调用 swap 还搞不清楚语义, 活该.

  指针是明坑.
  引用是半遮盖的暗坑, 由于半遮盖了, 掉进去的几率或许小一些, 但是这个暗坑, 掉进去了之后, 别人都不知道你掉到什么地方去了.
 
  不过没有关系了, 公司以后的项目, 不会再用 C++ 写, 而是 C,D,Python, 没有引用这档破事.





你可以测试一下你的同行,或者新来的 同事,在有编译器帮助的情况下,看看多少人能够一次把SWAP写的完全正确?
  我和同事是 C/C++ 同时用的, 对 C 的做法并没有那么反感.

  见到全大写的 "函数调用", 自然明白这是 宏, 见到宏, 自然知道应该注意什么.

  从一开始就接受 "宏是邪恶的, 决不要去碰它" 的说法的人, 自然也就不明白这是什么东西了.

 


macro 被 C++ 评为很邪恶, 但其实我觉得macro 最大的问题是没有作用域, 容易
冲突. 参数副作用这个问题, 比起 C++ n 多规则, 实在是一个太简单的规则.

我认为,如果编译器可以制止的陷阱,那就不算陷阱.编译器无能为力的才算.对于宏,编译器基本残废.

   编译器废的东西多了去了, 不然大家还讨论 C++ 的陷阱干嘛.

wizard

unread,
Dec 14, 2007, 8:21:13 AM12/14/07
to pon...@googlegroups.com

引用比指针的优势在于,它永远不会为空。
至于说担心引用的变量被修改,这个很好解决,在函数接口里,如果一个参数不允许在函数体内被修改的话,它应当被定义为常数引用,如果不是常数引用,那么默认就是有可能修改的。
 
On 12/12/07, redsea <red...@gmail.com> wrote:
> 2.4.1 引用
> 我受够了!左值/右值、复制引用/复制值 这些问题已经折磨了我很长时间了。我想,如果要用指针就用指针,不用遮遮掩掩说那是一个引用。如果 boost
  你比我更激进.
  我也很讨厌调用一个函数, 要去查原型才知道我的变量会不会被修改, 所以函数参数中我不用引用.
  不过我还会在一些地方用引用, 就是类的成员变量, 用引用, 表明了态度--不能为空 & 只能设置一次.


> 4.2 时间
> 不使用任何 C++ 类,一律传整数,比如 time_t 或者其他自定义的整数 。强大的 Boost.Date_Time 可以拜拜了。
时间啊, 时间啊,  c/c++, delphi 里面的时间相关的函数, 都让人头痛无比, 好多个类型, 好多个调用, 让我从来都记不
住, 每次要查找的时候, 都感到很悲愤.
  python 里面也不是很简单.
  我讨厌时间.

怀宇范

unread,
Dec 15, 2007, 3:55:49 AM12/15/07
to pon...@googlegroups.com
我觉得这种关键字多一点并不算太坏。比C++如此晦涩的用const + &之类的表示传入传出关系要清晰明了的多。
在关键字的层面确定这件事情,有利于统一规范,而不再是使用一些默认、习惯来确定规范。
--
bool ContactMe(person you)
{
   if(you.GoTo("14#344, Tsinghua") && you.Find("Fan Huaiyu"))return true;
   if(you.MailTo (" dugu...@gmail.com ") || you.MailTo(" fan...@mails.tsinghua.edu.cn "))return true;
   if(you.PhoneTo ("13488810330") || you.PhoneTo("01062778689"))return true;
   if(you.QQTo("120628812") || you.MSNTo("dugu...@hotmail.com"))return true;
   if(you.NetTo(" www.cnblogs.com/duguguiyu "))return true;
   return false;
}

pi1ot

unread,
Dec 15, 2007, 10:11:39 AM12/15/07
to TopLanguage
如果一件事非得用别扭硌涩的方式来做,那就尽可能把补丁打得刺眼一点明确一点。
至少维护者不用再去费尽心思琢磨那么一大堆中间层代码原来就只是为了看起来更舒爽,更"专业"。

On 12月15日, 下午4时55分, "怀宇范" <dugugu...@gmail.com> wrote:
> 我觉得这种关键字多一点并不算太坏。比C++如此晦涩的用const + &之类的表示传入传出关系要清晰明了的多。
> 在关键字的层面确定这件事情,有利于统一规范,而不再是使用一些默认、习惯来确定规范。
>
> On Dec 13, 2007 4:12 PM, Googol Lee <googol...@gmail.com> wrote:
>
>
>
>
>
> > 我倒觉得c#这里做的太冗余了,关键字和概念搞那么多......
>
> > 在 07-12-13,wing fire<wing.f...@gmail.com> 写道:
> if(you.MailTo(" dugugu...@gmail.com ") || you.MailTo("
> fanh...@mails.tsinghua.edu.cn "))return true;
> if(you.PhoneTo("13488810330") || you.PhoneTo("01062778689"))return true;
> if(you.QQTo("120628812") || you.MSNTo("dugugu...@hotmail.com"))return
> true;
> if(you.NetTo("www.cnblogs.com/duguguiyu"))return true;
> return false;
>
>
>
> }- 隐藏被引用文字 -
>
> - 显示引用的文字 -- 隐藏被引用文字 -
>
> - 显示引用的文字 -

up duan

unread,
Dec 17, 2007, 3:53:23 AM12/17/07
to pon...@googlegroups.com
>你指的是 void foo(std::string const & s); foo("ssss");
这样的代码吗?我为什么要写这样的代码?我为什么要依赖于临时对象?我传字符串的时候,宁愿传std::pair<char const*,
char const*> ,一样可以用 Boost.Range 来操作,一样可以用上 STL 的算法。

这个观点其实现在越来越流行了,TIC的作者也说过:在太多的地方,我们其实需要的是Iterator,结果我们传递了Container。

引用其实很好,至少对我来说,引用简化了大量的空指针判断。同时,它跟对象语法的一致性也是我喜欢的。

Atry

unread,
Dec 17, 2007, 5:57:53 AM12/17/07
to pon...@googlegroups.com
我一点都不觉得空指针会导致什么不好找到的 bug ,访问空指针的值会马上崩掉,很好调试。大多数时候根本不用判断空指针

在07-12-17,up duan <fix...@gmail.com> 写道:

Eli

unread,
Dec 17, 2007, 6:10:21 AM12/17/07
to TopLanguage
同意.

空指针判断虽然不会让调试多么困难, 关键是空指针跟我要处理的问题, 经常一点关系也没有..

怀宇范

unread,
Dec 17, 2007, 7:24:01 AM12/17/07
to pon...@googlegroups.com
re。
除非语义允许传null进来才进行处理。
否则进来的参数一律先assert再说。。。错了一下就发现了。
--
bool ContactMe(person you)
{
   if(you.GoTo("14#344, Tsinghua") && you.Find("Fan Huaiyu"))return true;
   if(you.MailTo (" dugu...@gmail.com ") || you.MailTo(" fan...@mails.tsinghua.edu.cn "))return true;
   if(you.PhoneTo ("13488810330") || you.PhoneTo("01062778689"))return true;
   if(you.QQTo("120628812") || you.MSNTo("dugu...@hotmail.com"))return true;

Atry

unread,
Dec 17, 2007, 10:30:31 AM12/17/07
to pon...@googlegroups.com
多写一个 assert 当然是好的习惯,不过我的意思是说就算懒得写这个 assert ,也没关系

在07-12-17,怀宇范 <dugu...@gmail.com> 写道:

K.L.

unread,
Dec 18, 2007, 7:15:00 AM12/18/07
to TopLanguage
关于操作符重载,看一段luabind的示例代码:

module(L)
[
def("f", &f),
def("g", &g),
class_<A>("A")
.def(constructor<int, int>),
];

没错,这就是C++代码。这段代码把两个C++函数和一个C++类注册到lua虚拟机中,生成对应的lua符号。luabind把最罕见的逗号操作符
(operator ,)都重载了。这个设计绝对称得上精彩绝伦!

C++中没有"没用的特性",关键是你会不会用。

Googol Lee

unread,
Dec 18, 2007, 7:24:29 AM12/18/07
to pon...@googlegroups.com
莫非是在[]里对,的重载?为什么一定要对,重载?写成函数传参数不行么?莫非是为了不定长参数?

感觉都不是C++的语法了……

在 07-12-18,K.L.<xxxK....@gmail.com> 写道:

K.L.

unread,
Dec 18, 2007, 7:28:07 AM12/18/07
to TopLanguage
关于private,我的经验和楼主相反,我经常把类中的全部成员都声明成private,需要访问权限的类和函数,给它friend声明。而对于那个
friend来讲,要访问的类通常是作为模板参数进来的,或者用C++0x的术语叫concept,这样就完全不必担心发生意外的错误访问。

private不是为了隐藏,而是用来保护访问权限。

莫华枫

unread,
Dec 18, 2007, 7:54:51 AM12/18/07
to pon...@googlegroups.com
重载的威力真不小。
如果把重载也看作一种多态,那么再加上其他几种多态,将来会不会发展到成为一种完整的元编程机制?
--
反者道之动,弱者道之用
m...@seaskysh.com
longsh...@gmail.com
http://blog.csdn.net/longshanks/

K.L.

unread,
Dec 18, 2007, 8:00:16 AM12/18/07
to TopLanguage
没错,[]的重载只能有一个参数。

module(L)
[
def("f", &f),
def("g", &g),
class_<A>("A")
.def(constructor<int, int>),
];

这里的L代表lua虚拟机,一个虚拟机对应多个符号,如果分多次注册L就要出现多次,这样就违反了DRY原则。重载的逗号把几个符号注册占位符联结成一
个表达式(而不是几个独立的参数),该表达式最终被传入module(L).operator []。

至于为什么不用不定长参数表----如果说C++里还有什么"没用的特性"那就要数C中几个没用的遗留物了,不定长参数表算一个。为了参数表不定长度而损失
参数类型检查,实在是得不偿失。

On 12月18日, 下午8时24分, "Googol Lee" <googol...@gmail.com> wrote:
> 莫非是在[]里对,的重载?为什么一定要对,重载?写成函数传参数不行么?莫非是为了不定长参数?
>
> 感觉都不是C++的语法了......
>
> 在 07-12-18,K.L.<xxxK.L....@gmail.com> 写道:
>
>
>
>
>
> > 关于操作符重载,看一段luabind的示例代码:
>
> > module(L)
> > [
> > def("f", &f),
> > def("g", &g),
> > class_<A>("A")
> > .def(constructor<int, int>),
> > ];
>
> > 没错,这就是C++代码。这段代码把两个C++函数和一个C++类注册到lua虚拟机中,生成对应的lua符号。luabind把最罕见的逗号操作符
> > (operator ,)都重载了。这个设计绝对称得上精彩绝伦!
>
> > C++中没有"没用的特性",关键是你会不会用。
>
> --
> 新的理论从少数人的主张到一统天下,并不是因为这个理论说服了别人抛弃旧观点,而是因为一代人的逝去。
>
> My blog:http://googollee.blog.163.com- 隐藏被引用文字 -
>
> - 显示引用的文字 -

Wang Xin

unread,
Dec 18, 2007, 8:39:19 PM12/18/07
to pon...@googlegroups.com
重载本来就是一种多态,对于多态的划分,Generative Programming中的描述是我看过的书中写的最好的

在07-12-18,莫华枫 <longsh...@gmail.com> 写道:
重载的威力真不小。
如果把重载也看作一种多态,那么再加上其他几种多态,将来会不会发展到成为一种完整的元编程机制?


Everything is possible and available if we trust ourselves!

Atry

unread,
Dec 18, 2007, 10:47:55 PM12/18/07
to pon...@googlegroups.com
我可没说运算符重载是没用的特性。我说那只是语法糖。此外,炫耀技巧的时候可以大量使用 ^_^。

在07-12-18,K.L. <xxxK....@gmail.com> 写道:

pongba

unread,
Dec 19, 2007, 1:17:11 AM12/19/07
to pon...@googlegroups.com
On Dec 18, 2007 8:15 PM, K.L. <xxxK....@gmail.com> wrote:

C++只能支持DSL "in the small"。换句话说,对于这种小DSL的需求,可以hack出来,但遇到实现一个Functional的嵌入式子语言,就捉襟见肘了。如果觉得fc++这个例子过于极端, 也可以看一看boost.Xpressive的实现有多笨拙(实际上,不仅是实现,提供的使用接口也因为C++对元编程没有first-class的支持而处处不够直观)。C++对DSL的支持是完全偶然的,其导致的接口和实现不直观性,使得设计出来的DSL大多除了show off的价值之外,使用价值近乎0。我永远不会去用boost.Xpressive,因为不直观的界面带来的学习代价早就超出了静态类型检查带来的好处。另一方面,DSL实现的不直观性也是非常费事的, google一下boost.proto这个未发布的库,这个库的功能是提供一个"便于"DSL实现者实现DSL的框架,它把C++里面能重载的东西都重载好了放在那里,而重载的具体语义留给你去扩展。但由于本质上这是在hack语言,因此甭提有多别扭了。何必呢~何苦呢~~像Xpressive,我认为根本没有工业使用价值(当然,不是说这个luabind没有,对这种小规模的DSL,的确,hack一下语言会有意想不到的效果)。

up duan

unread,
Dec 19, 2007, 4:48:00 AM12/19/07
to pon...@googlegroups.com
>我可没说运算符重载是没用的特性。我说那只是语法糖。此外,炫耀技巧的时候可以大量使用 ^_^。

我觉得我们没有必要在乎是不是语法糖,只要不是语法毒药,吃起来甜的都要吃。

只要增加了我们的抽象能力,解放了我们的手指,提高了我们的生产力。

Reply all
Reply to author
Forward
0 new messages