数组类模板中的[ ]运算符重载问题

丝毫不夸张,这真的是我在大便的时候想出来的...[speechless]
注:在《C++ Primer Plus》6th P577,书上光是使用了如下代码,但是没有分析为什么:
//arraytp.h -- Array Template
#ifndef ARRAYTP_H_
#define ARRAYTP_H_

#include
#include

using namespace std;

template
class ArrayTP
{
public:
    ArrayTP() {}
    explicit ArrayTP(const Type & v);
     virtual Type & operator[](int i);
     virtual Type operator[](int i) const;
private:
    Type ar[n];
};

template
ArrayTP::ArrayTP(const Type & v)
{
    for (int i = 0; i < n; ++i)
    {
         ar[i] = v;
    }
}

template
Type & ArrayTP::operator[](int i)
{
    if (i < 0 || i >= n)
    {
         cerr << "Error in array limits: " << i << " is out of range\n";
         exit(EXIT_FAILURE);
    }
    return ar[i];
}

template
Type ArrayTP::operator[](int i) const
{
    if (i < 0 || i >= n)
    {
         cerr << "Error in array limits: " << i << " is out of range\n";
         exit(EXIT_FAILURE);
    }
    return ar[i];
}

#endif
首先请注意,为什么在类中重载了两次[ ]运算符,而且实现的功能“完全相同”,除了 一个为返回引用为非const版本,一个返回值为const版本。两个函数的返回都不是局部变量,返回引用和值有区别吗?如果追求效率那就都用返回引用不就好了吗?为什么引用对应的是非const版本而值对应的是const版本?这是巧合吗?
然后,又不由分说的使用了如下代码:
//twod.cpp -- making a 2-d array
#include
#include "array.h"
int main(void)
{
    using std::cout;
    using std::endl;

    ArrayTP sums;
    ArrayTP aves;
     ArrayTP, 10> twodee;

    int i, j;
    for (i = 0; i < 10; ++i)
    {
         sums[i] = 0;
         for (j = 0; j < 5; ++j)
         {
              twodee[i][j] = (i + 1) * (j + 1);
             sums[i] += twodee[i][j];
         }
         aves[i] = (double) sums[i] / 10;
    }

    for (i = 0; i < 10; ++i)
    {
         for (j = 0; j < 5; ++j)
         {
             cout.width(2);
             cout << twodee[i][j] << ' ';
         }
         cout << ": sum = ";
         cout.width(3);
         cout << sums[i] << ", average = " << aves[i] << endl;
    }

     cout << "Done.\n";

     return 0;
}
先说个题外话,这里使用了模板的递归多功能性:ArrayTP, 10> twodee; 。这使得 twodee 是一个包含10个元素的数组,其中每个元素都是一个包含5个 int 元素的数组。与之等价的常规数组声明:int twodee[10][5]; 。请注意,在模板语法中,维的顺序与等价的二维数组相反,想清楚了原因就很容易记忆了,就用这个例子来说,这是声明的一个10行5列的数组,在模板数组中,先看内层:声明一个5维数组(一行有5个元素,即我们把它看成5列),再看外层,声明一个10维数组(一列有10个元素,即我们把它看成10行,每一行都是一个5维数组),所以与常规数组的声明顺序相反。但是!在使用时,与常规数组的使用方法无二异,即:twodee[i][j],先是 i 行,然后是 j 列。

,有个疑点,为什么 声明的顺序相反,而调用的顺序相同,而且调用的时候又是使用的[ ]运算符,想到这里,我就觉得找到了突破口。
Brainy is new sexy.
  • 为什么有const和非const两个版本?
因为在程序中有的地方将 ar[i] 作为右值(cout << sums[i];),有的地方将 ar[i] 作为左值(sums[i] = 0;),有的地方两种版本都使用了(sums[i] += twodee[i][j];、aves[i] = (double) sums[i] / 10;)。作为右值时不需要修改数据,是const版本;作为左值时需要修改数据,是非const版本。
  • 为什么引用对应的是非const版本而值对应的是const版本?
因为返回值时(const)不需要修改成员变量(右值),返回数组的副本,所以使用const修饰符限制成员函数;而返回引用时(非const)需要修改成员变量(左值),所以只能用引用和非const。而且返回引用只能对应非const(需要修改成员数据,不能加const限定符),返回值只能对应const(如果为了追求效率,const版本加上&变成返回引用,则不能使用const限定符,因为编译器无法将带有const限定符的数据转换为非const数据,这也是引入const限定符作为保护数据不被随意修改的初衷,详见我的《const修饰符的作用》;如果为了编译通过去掉const的话,不就变成了返回引用的非const版本了吗,哈哈,这里就有了两个相同的函数声明,没必要声明两次,当然编译器也不允许。)
  • 为什么声明的顺序相反而调用的顺序相同?二维模板数组的使用究竟是怎样实现的?(以左值为例)
twodee[i][j]   <==>   (twodee.operator[ ](i)).operator[ ](j)
首先是内层调用,这里使用的是返回引用非const版本,调用结束之后,函数返回的是一个对象的引用,所以可以再次调用运算符成员函数;
其次是外层调用,由于内层调用函数的返回值为一个对象的引用,所以可以再次调用运算符成员函数,两个都应该是返回引用的非const版本,因为是左值需要修改数据。
具体的调用哪个版本的运算符成员函数,最终还是取决于编译器对具体语句功能的解析。
到此,基本讲清了这里面的关系,yeah!

你可能感兴趣的:(C++)