rxu
rxu

Reputation: 1407

c++ class return reference to itself

Can a c++ object have a method that return a reference to itself? I want multiple independent instance of the class of the object.

It is for implementing the [] operator of a multi-dimensional array. I want something like Array[0][1][2] to work.

Thanks.

Update:

Implementation of a multi-dimensional array:

Intended usage:

A[0][1] = 4;    //the element at 0th row and 1st column is now 4.
A[0] = 5;       //all elements of the 0th row are now 5.
A["all"][1] = 10; //all elements of the 1st column are now 10;

Object A has pointer ptr_buffer to a chunk of memory with correct simd alignment. Constructor of A would allocate the memory. Destructor of A deallocate the memory. A[] returns object B.

Object B has a pointer ptr_A_buffer to a subsection of the memory of A. B[] modifies ptr_A_buffer and also returns reference to itself. I don't want to constantly make a bunch of object B for each [] operation.

Both A and B belong to the same abstract class. Math functions take the abstract class as argument.

Upvotes: 0

Views: 1788

Answers (1)

Peter - Reinstate Monica
Peter - Reinstate Monica

Reputation: 16017

I was curious about the idea. Here is a data type which seems to fit the bill. It's an array type which, when called with three integers for the dimensions, allocates contiguous memory and then allows the user to define "views" into the data which have smaller dimensions (a plane, a row, a single value).

I made use of shared pointers which I have not really used before, so I may have made mistakes. I welcome corrections.

The idea is that copying views around is shallow; they all operate on the same underlying data. This allows me to pass them around by value with, as I believe, reasonable efficiency.

#include <iostream>
#include <iomanip>
#include <memory>

using namespace std;

/// This class is a three-dimensional array of doubles.
/// It defines an index operator which returns a view into that
/// data that is of one lesser dimension, just like the standard
/// index operator on plain old arrays. The last index operation 
/// yields an "array" which is a single value. 
/// Converting to double and assigning from double is defined as
/// using the first double in the view.
class NDimArrT 
{
    /// All slices hold a shared pointer to the base data they
    /// are looking into so that their view stays valid.
    const shared_ptr<double> baseData;

    /// The data the view is looking at. Identical to the shared
    /// ptr for the original object.
    double *const slice;

    /// The three dimensions, in the order of indexing.
    /// All three of them may be zero, indicating a single value.
    const int dim1, dim2, dim3;

public:

    /// A single double value view into a one-dimensional array
    NDimArrT(const shared_ptr<double> base, double *sliceStart) 
        : baseData(base), slice(sliceStart), dim1(0), dim2(0), dim3(0) {}

    /// A 1D vector/row view into a two-dimensional array.
    /// @param dim1Arg is the length of the row.
    NDimArrT(const shared_ptr<double> base, double *sliceStart, int dim1Arg) 
        : baseData(base), slice(sliceStart), dim1(dim1Arg), dim2(0), dim3(0) {}

    /// A 2D matrix plane view into the cube
    NDimArrT(const shared_ptr<double> base, double *sliceStart, int dim1Arg, int dim2Arg) 
        : baseData(base), slice(sliceStart), dim1(dim1Arg), dim2(dim2Arg), dim3(0) {}

    /// A 3D cube. This actually allocates memory.
    NDimArrT(int dim1Arg, int dim2Arg, int dim3Arg) 
        :   baseData(new double[dim1Arg*dim2Arg*dim3Arg], std::default_delete<double[]>() ),
            slice(baseData.get()), // the data view is the whole array
            dim1(dim1Arg), dim2(dim2Arg), dim3(dim3Arg) {}

    /// Perform a shallow copy. We assume that e.g. returning a slice means
    /// essentially returning another view into the main base array.
    NDimArrT(const NDimArrT &rhs) = default;

    /// Use standard move semantics. The rhs will be invalidated, and the
    /// reference count to the base array does not grow. Can be used to return results from 
    /// functions.
    NDimArrT(NDimArrT &&rhs) = default;

    /// Default destructor; destroy baseData if it's the last reference.
    ~NDimArrT() = default;

    /// Get the value of the first element we look at. Handy for 
    /// single value views.
    operator double() const { return *slice; }

    /// Return an instance of NDimArrT representing a view
    /// with one dimension less than this. If we have a single value
    /// already, simply return this single value. (We should
    /// perhaps throw an exception there.)
    NDimArrT operator[](int ind)
    {
         // This could be regarded an error, because this view 
         // is already a single element.
        if(GetDims() == 0) { return *this; }

        // This view is a 1-dimensional vector. Return the element at ind.
        if(dim2==0) { return NDimArrT(baseData, slice + ind); } // return a single value.

        // This view is a 2D matrix. Return the row no. ind.
        // The row length is dim2. (Dim 1 indicates how many rows.)
        if(dim3==0) { return NDimArrT(baseData, slice + dim2*ind, dim2); } // return a 1D vector

        // This view is a true 3D cube matrix. dim1 is the number of planes,
        // dim2 is the number of rows in a plane, dim3 is the length of each row.
        // Return the plane no. ind, which starts at ind*planesize, i.e. ind*dim2*dim3.
        // The number of rows and row length are dim2 and dim3, respectively.
        return NDimArrT(baseData, slice + dim2*dim3*ind, dim2, dim3); // return a 2D matrix.
    } 

    NDimArrT &operator=(double d) { *slice = d; }

    int Len() { return dim1 ? dim1 : 1; } // interestingly, length is always dim1.

    int GetDims() const
    { 
        return dim1 
                ? dim2 
                    ? dim3
                        ? 3
                        : 2
                    : 1
                : 0;
    }

};

/// An example function which initializes an NDimArr of unknown
/// dimensionality with nice numbers..
void initNDimArr(NDimArrT arr, int &startVal)
{
    // Single value? Give it the start value and increment that.
    if( arr.GetDims() == 0 ) { arr = startVal++; }
    else
    {
        for(int ind=0; ind<arr.Len(); ind++) { initNDimArr(arr[ind], startVal); }
    }
}

// An example function doing something with
// an unknown n-dimensional array
void printNdimArr( NDimArrT nDimArr)
{
    if( nDimArr.GetDims() == 0) { cout << setw(4) << nDimArr << " "; }
    else 
    { 
        for(int i=0; i<nDimArr.Len(); i++) { printNdimArr(nDimArr[i]); }
        cout << endl;
    }   
}
int main()
{
    NDimArrT arr(3,4,5);
    int start = 1;
    initNDimArr(arr, start);
    printNdimArr(arr);

    // now use the middle plane of the 3 4x5 planes
    cout << "Middle plane, values starting at 100:" << endl;
    auto middlePlane = arr[1];
    start = 100;
    initNDimArr(middlePlane, start);
    printNdimArr(middlePlane);

    cout << "Whole array now:" << endl;
    printNdimArr(arr);

    cout << "Print line 2 of the 3rd plane:" << endl;
    printNdimArr(arr[2][1]);

    cout << endl << "Last number in that row is " << arr[2][1][4] << endl;
}

Sample session:

$>g++ -std=c++11 -o ndimContiguousArr ndimContiguousArr.cpp && ./ndimContiguousArr
   1    2    3    4    5
   6    7    8    9   10
  11   12   13   14   15
  16   17   18   19   20

  21   22   23   24   25
  26   27   28   29   30
  31   32   33   34   35
  36   37   38   39   40

  41   42   43   44   45
  46   47   48   49   50
  51   52   53   54   55
  56   57   58   59   60


Middle plane, values starting at 100:
 100  101  102  103  104
 105  106  107  108  109
 110  111  112  113  114
 115  116  117  118  119

Whole array now:
   1    2    3    4    5
   6    7    8    9   10
  11   12   13   14   15
  16   17   18   19   20

 100  101  102  103  104
 105  106  107  108  109
 110  111  112  113  114
 115  116  117  118  119

  41   42   43   44   45
  46   47   48   49   50
  51   52   53   54   55
  56   57   58   59   60


Print line 2 of the 3rd plane:
  46   47   48   49   50

Last number in that row is 50

Upvotes: 1

Related Questions