數組類模板中的[ ]運算符重載問題

絲毫不誇張,這真的是我在大便的時候想出來的...[speechless]
注:在《C++ Primer Plus》6th P577,書上光是使用瞭如下代碼,但是沒有分析爲什麼:
//arraytp.h -- Array Template
#ifndef ARRAYTP_H_
#define ARRAYTP_H_

#include <iostream>
#include <cstdlib>

using namespace std;

template <typename Type, int n>
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 <typename Type, int n>
ArrayTP<Type, n>::ArrayTP(const Type & v)
{
    for (int i = 0; i < n; ++i)
    {
         ar[i] = v;
    }
}

template <typename Type, int n>
Type & ArrayTP<Type, n>::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 <typename Type, int n>
Type ArrayTP<Type, n>::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 <iostream>
#include "array.h"
int main(void)
{
    using std::cout;
    using std::endl;

    ArrayTP<int, 10> sums;
    ArrayTP<double, 10> aves;
    ArrayTP<ArrayTP<int, 5>, 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<ArrayTP<int, 5>, 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!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章