使用稀疏成员数据为继承类编写访问器方法?

时间:2016-10-07 12:47:23

标签: c++ inheritance design-patterns

假设我有一个简单的矢量类vec

#include <iostream>
#include <stdlib.h>

class vec {
public:
    vec() {}
    // Constructor.
    vec(int n) {
        len = n;
        data = new double[len];
    }

    // Destructor.
    ~vec() { delete [] data; }

    // Accessor.
    double & operator[](int i) const {
        check_index(i);
        return data[i];
    }

    // Other methods...
    // ....

protected:
    int len;
    double * data;
    void check_index(int i) const {
        if(i < 0 || i >= len) {
            std::cerr << "Bad access.\n";
            exit(1);
        }
    }
};

现在假设我有一个特殊类型的vector具有稀疏结构,例如,每个偶数索引都为零。请拨打此oddvecoddvec的实例应该与vec类一样声明,但在下面,内存使用应该是高效的,因为只有一半的数据是非零的。

如果索引是偶数,oddvec类的访问器应返回0,否则返回奇数索引元素(按顺序存储)。这有几个问题:

  1. 如果索引是偶数,则会违反double &返回类型,因为会返回常量值0
  2. 当使用偶数索引元素作为lvalue时,我不清楚如何处理这种情况。例如,v[0] = 3.0类不允许oddvec,但在vector类中完全可以接受。rvalue。我们不能简单地在使用索引时抛出错误,因为即使索引是正常的,只要意图是oddvec
  3. 如何设计oddvec类的访问器函数,同时保持内存存储效率并继承父级的所有方法?

    class oddvec : public vec { public: // Constructor. oddvec(int n) { len = n; data = new double[len/2]; } // Accessor (doesn't work!) double & operator[](int i) const { check_index(i); if (i%2 == 0) return 0; else return data[(i-1)/2]; } }; 的非工作示例:

    main.cpp: In member function ‘double& oddvec::operator[](int) const’:
    main.cpp:49:20: error: invalid initialization of non-const reference of type ‘double&’ from an rvalue of type ‘double’
                 return 0;
    

    编译后:

    #ifndef PROXIES_H
    #define PROXIES_H
    
    #include <iostream>
    #include <stdlib.h>
    
    class proxy {
    public:
        proxy(int i, double v, double * d) {
            index = i;
            value = v;
            data  = d;
        }
        void operator=(double rhs) {
            data[index] = rhs;
        }
        friend std::ostream & operator<<(std::ostream & outs, const proxy & p) {
            outs << p.value;
            return outs;
        }
    protected:
        int     index;
        double  value;
        double * data;
    };
    
    class oddproxy : public proxy {
    public:
        oddproxy(int i, int v, double * d) : proxy(i, v, d) {}
        void operator=(double rhs) {
            if (index%2 == 0) {
                std::cerr << "Even entries of oddvec are not assignable.\n";
                exit(1);
            }
            data[index/2] = rhs;
        }
    };
    
    #endif
    

    使用代理类的工作示例:

    我已按照以下答案中的建议实施了代理类。

      

    proxies.h

    #ifndef VECTORS_H
    #define VECTORS_H
    
    #include "proxies.h"
    
    class vec {
    public:
        vec() {}
        // Constructor.
        vec(int n) {
            len = n;
            data = new double[len];
        }
    
        // Destructor.
        ~vec() { delete [] data; }
    
        // Accessor.
        proxy operator[](int i) const {
            check_index(i);
            return proxy(i, data[i], data);
        }
    
        inline int length() const { return len; }
    
        // Other methods...
        // ....
    
    protected:
        int len;
        double * data;
        void check_index(int i) const {
            if(i < 0 || i >= len) {
                std::cerr << "Bad access.\n";
                exit(1);
            }
        }
    };
    
    
    class oddvec : public vec {
    public:
        // Constructor.
        oddvec(int n) {
            len = n;
            data = new double[len/2];
        }
    
        // Accessor.
        oddproxy operator[](int i) const {
            check_index(i);
            return oddproxy(i, (i%2 == 0) ? 0 : data[i/2], data);
        }
    };
    
    #endif
    
      

    vectors.h

    #include <iostream>
    #include "vectors.h"
    
    int main () {
        int N = 5;
        vec V(N);
        oddvec O(N);
    
        for(int i=0; i < V.length(); i++) {
            V[i] = i;
    
            if(i%2 != 0) {
                O[i] = i;
            }
        }
    
        for(int i=0; i < O.length(); i++) {
            std::cout << "V[" << i << "]=" << V[i] << ", "
                      << "O[" << i << "]=" << O[i] << "\n";
        }
    
        O[0] = 13;
    
        return 0;
    }
    
      

    的main.cpp

    V[0]=0, O[0]=0
    V[1]=1, O[1]=1
    V[2]=2, O[2]=0
    V[3]=3, O[3]=3
    V[4]=4, O[4]=0
    Even entries of oddvec are not assignable.
    
      

    输出

    {{1}}

1 个答案:

答案 0 :(得分:1)

您可以使用代理对象执行此操作。

简单的示例代码:

#include <iostream>
#include <vector>
using namespace std;

class very_odd_vector{
public:
    class only_odd_proxy;
    friend class only_odd_proxy;
    only_odd_proxy operator [](int index);
    int operator [](int index)const{return index%2==0?0:content[index/2];}
    unsigned int size()const{return content.size()*2;}
private:
    vector<int> content{1,3,5,7,9};
};

class very_odd_vector::only_odd_proxy{
public:
   only_odd_proxy(very_odd_vector& vec,int index):vec(vec),index(index){}
   operator int(){return index%2==0 ? 0 : vec.content[index/2];}
   only_odd_proxy& operator =(int value){
      if(index%2==0)
         cout << "BAD OPERATION";//any error you want
      else
         vec.content[index/2] = value;
      return *this;
   }
private:
   very_odd_vector& vec;
   int index;
};

auto very_odd_vector::operator [](int index)->only_odd_proxy{return only_odd_proxy(*this,index);}

int main(){
   very_odd_vector v;

   cout << "reading value\n";
   for(int i=0;i<v.size();++i)
      cout << v[i] <<'\n';

   cout << "writting value\n";
   for(int i=0;i<v.size();++i){
      cout << i << ':';
      v[i]=10;
      cout << '\n';
   }

   cout << "reading value\n";
   for(int i=0;i<v.size();++i)
      cout << v[i] <<'\n';
}

修改问题的更新部分:

我认为这门课程更适合你的需要。

//Both base and inherit class return this class
class maybe_readonly_proxy {
public:
    maybe_readonly_proxy(double* data, bool readonly):readonly(readonly),data(data){}
    maybe_readonly_proxy& operator=(double rhs) {
        if(readonly){/*whatever error*/}
        else {*data = rhs;}
        return *this;
    }
    operator double()const{return *data;}
private:
    bool readonly;
    double * data;
};

您可能需要一个变量来包含readonly(在这种情况下为0)值,或者修改operator double()检查只读状态

或者只是单独实施getset方法,不要使用此代理可能是另一种选择。