1. 关于引用。
引用本身就意味着“不进行复制”,它只是一个别名罢了,所以不会有构造和析构的开销。这与是否使用了const是无关的。
注意即使是const引用,也不保证“不修改数据”。这可以从两个方面来理解。
一方面,C++存在一个关键字mutable,用它修饰的类成员,即使在const成员函数中也是可以修改的。
举例来说,你实现了一个类,用它表示硬盘,则在对它进行读取时,可能希望把这个read函数定义为const成员函数,因为读取数据之时并不会修改到硬盘中的数据。然而现实并不是那么完美。你的硬盘是带缓冲区的,在读取之时可能会修改到缓冲区的数据,这与前面的const发生了矛盾。一种解决办法就是把缓冲区数据声明为mutable,这样即使在const成员函数中也可以对它进行修改。
另一方面,C++还提供了一个const_cast。虽然一般用不到,但正所谓“只要有可能犯错,就会有人犯错”,一旦把一个const引用转化为non-const引用,“无法修改”这个限制都成为了空谈。
可见const引用也有修改数据的可能(虽然可能性极其的小)。
因此,为const引用复制一份数据的做法不仅是低效的,而且还是错误的。
2. 关于non-member, non-friend函数。
用不着想那么多,std::vector本身就是很好的例子了。
std::vector功能不少。可以线性的保存数据,各元素紧密排列,可以动态增长,可以修改插入删除等等等等。但是它也并没有包罗万象。比如排序,比如查找,这些操作都没有作为成员函数来提供,而是用了non-member, non-friend的函数。
为什么要将排序等操作单独列出来,而不再作为成员函数呢?因为设计者认为,std::vector的成员函数已经“足够多”了。这些函数已经构成了一组“完备”的操作集。即是说,其它的操作完全可以通过现有的成员函数,利用各种组合来实现。为此,没有必要再继续的增加成员函数的数量。
从维护的难度上看,如果std::vector提供排序的功能,则当std::vector的实现被修改时,其排序的代码难免也要被修改。但正如我们现在所看到的,无论std::vector的实现如何修改,std::sort却可以始终如一,这正是non-member, non-friend函数的优势。
以模块的观点来看,std::vector是“可变长度的数组”模块,而排序是属于“算法”的模块,两者没有必要放在一起(若让排序成为std::vector的成员函数,则显然是放在一起了)。在进行模块设计之时,总的原则就是外松内紧。即模块之间的联系尽可能的少,模块内部各事物的联系则往往非常的多。
当然了,具体的设计总是见仁见智。哪些功能放在同一模块,哪些功能放在不同模块,这个没有标准做法,尺度只有靠设计者自己把握。
有的设计也确实把排序和可变数组做到了同一个class之中,这也并不是错误。我确实可以举出例子。比如Irrlicht(一个开源的游戏引擎),它就有一个集合了可变数组和排序功能的类。
一切都是根据需要,没有最正确的,只有最合适的。
引用本身就意味着“不进行复制”,它只是一个别名罢了,所以不会有构造和析构的开销。这与是否使用了const是无关的。
注意即使是const引用,也不保证“不修改数据”。这可以从两个方面来理解。
一方面,C++存在一个关键字mutable,用它修饰的类成员,即使在const成员函数中也是可以修改的。
举例来说,你实现了一个类,用它表示硬盘,则在对它进行读取时,可能希望把这个read函数定义为const成员函数,因为读取数据之时并不会修改到硬盘中的数据。然而现实并不是那么完美。你的硬盘是带缓冲区的,在读取之时可能会修改到缓冲区的数据,这与前面的const发生了矛盾。一种解决办法就是把缓冲区数据声明为mutable,这样即使在const成员函数中也可以对它进行修改。
另一方面,C++还提供了一个const_cast。虽然一般用不到,但正所谓“只要有可能犯错,就会有人犯错”,一旦把一个const引用转化为non-const引用,“无法修改”这个限制都成为了空谈。
可见const引用也有修改数据的可能(虽然可能性极其的小)。
因此,为const引用复制一份数据的做法不仅是低效的,而且还是错误的。
2. 关于non-member, non-friend函数。
用不着想那么多,std::vector本身就是很好的例子了。
std::vector功能不少。可以线性的保存数据,各元素紧密排列,可以动态增长,可以修改插入删除等等等等。但是它也并没有包罗万象。比如排序,比如查找,这些操作都没有作为成员函数来提供,而是用了non-member, non-friend的函数。
为什么要将排序等操作单独列出来,而不再作为成员函数呢?因为设计者认为,std::vector的成员函数已经“足够多”了。这些函数已经构成了一组“完备”的操作集。即是说,其它的操作完全可以通过现有的成员函数,利用各种组合来实现。为此,没有必要再继续的增加成员函数的数量。
从维护的难度上看,如果std::vector提供排序的功能,则当std::vector的实现被修改时,其排序的代码难免也要被修改。但正如我们现在所看到的,无论std::vector的实现如何修改,std::sort却可以始终如一,这正是non-member, non-friend函数的优势。
以模块的观点来看,std::vector是“可变长度的数组”模块,而排序是属于“算法”的模块,两者没有必要放在一起(若让排序成为std::vector的成员函数,则显然是放在一起了)。在进行模块设计之时,总的原则就是外松内紧。即模块之间的联系尽可能的少,模块内部各事物的联系则往往非常的多。
当然了,具体的设计总是见仁见智。哪些功能放在同一模块,哪些功能放在不同模块,这个没有标准做法,尺度只有靠设计者自己把握。
有的设计也确实把排序和可变数组做到了同一个class之中,这也并不是错误。我确实可以举出例子。比如Irrlicht(一个开源的游戏引擎),它就有一个集合了可变数组和排序功能的类。
一切都是根据需要,没有最正确的,只有最合适的。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10697500/viewspace-528620/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/10697500/viewspace-528620/