CArray: 一个简单但高效的改进






4.93/5 (17投票s)
2000年1月25日

275811

1681
一个简单的派生模板类,可以提高程序的效率。
引言
如果您使用CArray
,并且使用了const
关键字,您的程序可能会慢 50%! 有兴趣吗? 请继续阅读...
背景
我喜欢面向对象编程。 在编写了 15 年的 C 程序后,我很乐意永远用 C++ 编写代码。
我最喜欢的两件事是:模板和const
关键字。
如果您像我一样,到处都使用const
。 它封装了封装。 嗯,有点... 无论如何,如果我可以传递一个const
引用或const
指针,我会这样做。 为什么? 因为这意味着我的调用例程知道其数据是安全的。 它降低了复杂性。
模板不言自明。 嗯,实际上,它们不是...而且语法很烂(每次我想创建一个模板时,我都必须跑到教科书前 - 这要么意味着语法很烂,要么我太笨,或者可能我只是喝了太多的红酒...)。
无论如何,Microsoft 编写了几个有用的模板类,包括CArray
。 遗憾的是他们做得这么差,尤其是在文档方面。
CArray 有什么问题?
我已经多次被CArray
坑过。 我的代码运行良好,但后来我发现发生了很多不必要的复制。
CArray
对于int
和 double
的数组来说很好,但如果给它一个具有多个字节数据的类,那么程序的效率就会受到影响。
这是我喜欢做的事情
// // Declare a useful class. // class MyClass { protected: // data here (maybe lots) public: // etc. etc. etc. etc }; typedef CArray<MyClass,MyClass&> MyClassArray;
然后,我将按如下方式使用这个数组
MyFunction(const MyClassArray& array) { for (int ii = 0 ; ii < array.GetSize() ; ii++) DoSomething(array[ii]); } DoSomething(const MyClass& my_object) { // do stuff here }
很简单,对吧? 但是使用CArray
,调用DoSomething(array[ii])
会在调用 DoSomething
之前创建一个数组元素的临时副本(在本例中为 MyClass
)! 然后,在下一个循环迭代之前销毁临时副本。
如果我的数组元素是一个int
,对我来说没问题。 但如果它是一个包含 1K 数据的类,那么 CArray
就会在背后默默地捅我一刀。
当然,公平地说,CArray
并非“沉默”。 它的 operator[] const
和 GetAt
方法文档 返回一个副本。
但为什么?
我想不出任何好的理由(除非 CArray
仅设计用于 int
等数组),这些方法返回副本。 他们应该返回一个const
引用。
在第 N 次被烧伤后,我做了一些事情。
解决方案
我对模板类CArray
进行了简单的派生,称为OCArray
(OC 代表 Open Concepts - 我的公司之一)。 或者,如果您愿意,它可以表示“Optimised-CArray
”。
/* * Template Class: OCArray * Author: Russell Robinson * Purpose: * To provide a generic array class like CArray without the problems. * OCArray takes one parameter - TYPE. Unlike CArray, OCArray always * returns references and expects references as parameters. */ template <class TYPE> class OCArray : public CArray<TYPE,TYPE&> { public: /* * Method: OCArray::operator[] const * Parameters: i_index the array index to access * Returns: const TYPE& reference to the element at the index * Author: Russell Robinson * Purpose: * To return an element of the array for const access. */ inline const TYPE& operator[](int i_index) const { ASSERT(0 <= i_index && i_index < GetSize()); return (GetData()[i_index]); }; /* * Method: OCArray::GetAt * Parameters: i_index the array index to access * Returns: const TYPE& reference to the element at the index * Author: Russell Robinson * Purpose: * To return an element of the array for const access. */ inline const TYPE& GetAt(int i_index) const { ASSERT(0 <= i_index && i_index < GetSize()); return (GetData()[i_index]); }; /* * Method: OCArray::operator[] * Parameters: i_index the array index to access * Returns: TYPE& reference to the element at the index * Author: Russell Robinson * Purpose: * To return an element of the array for possible modification. * This method is needed because the compiler * loses the base class's method. */ inline TYPE& operator[](int i_index) { ASSERT(0 <= i_index && i_index < GetSize()); return (GetData()[i_index]); }; };
只需使用OCArray
代替CArray
。 它只需要一个参数,因为参数类型隐含为引用。 这也有助于提醒您,您没有使用CArray
。
结果是,当您通过const
引用或指针访问数组时,不会进行复制。
在优化程序中,节省的时间约为 50%,在调试版本中可以达到 75%!
以上是您所需要的全部内容,但我提供了一个演示项目,以便您可以看到差异。
现在我们可以考虑如何处理所有那些空闲的 CPU 周期......