65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (17投票s)

2000年1月25日

viewsIcon

275811

downloadIcon

1681

一个简单的派生模板类,可以提高程序的效率。

引言

如果您使用CArray,并且使用了const关键字,您的程序可能会慢 50%! 有兴趣吗? 请继续阅读...

背景

喜欢面向对象编程。 在编写了 15 年的 C 程序后,我很乐意永远用 C++ 编写代码。

我最喜欢的两件事是:模板和const关键字。

如果您像我一样,到处都使用const。 它封装了封装。 嗯,有点... 无论如何,如果我可以传递一个const引用或const指针,我会这样做。 为什么? 因为这意味着我的调用例程知道其数据是安全的。 它降低了复杂性。

模板不言自明。 嗯,实际上,它们不是...而且语法很烂(每次我想创建一个模板时,我都必须跑到教科书前 - 这要么意味着语法很烂,要么我太笨,或者可能我只是喝了太多的红酒...)。

无论如何,Microsoft 编写了几个有用的模板类,包括CArray。 遗憾的是他们做得这么差,尤其是在文档方面。

CArray 有什么问题?

我已经多次被CArray坑过。 我的代码运行良好,但后来我发现发生了很多不必要的复制。

CArray 对于intdouble 的数组来说很好,但如果给它一个具有多个字节数据的类,那么程序的效率就会受到影响。

这是我喜欢做的事情

 //

 // 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[] constGetAt 方法文档 返回一个副本。

但为什么?

我想不出任何好的理由(除非 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 周期......

© . All rights reserved.