忍者ブログ
ADMINNEW ENTRY
C A L E N D A R
02 2024/03 04
S M T W T F S
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

HP→http://sora-blue.net/~kuar/index.html
No.428 No.427 No.426 No.425 No.424 No.423
2024-03-19(Tue)
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2011-08-07(Sun)
[C++]あたかもメンバの別名のように振る舞う
あまりこういうことには出会わないとは思うが稀にメンバの名前を変えたいとか振る舞い方を変えたいとかあったりする。
例えばWindowsでのRECT構造体。
struct RECT{
 int left,top,right,bottom;
};
こいつをちょっと不便だから
struct Rect{
 int x,y,width,height;
};
にしたいと考える。
このままRectを拡張してしまえばいいじゃないかと思う人は話はここまでである。

しかしまあ、このまま拡張しても不便だから。特にRECTの参照渡しをする機会が多いのでその都度
RECT rect = Rect.RECT();
hoge(&rect);
とするのも面倒なわけである。

そのままダイレクトに引数に入れるために継承を行うとする。
struct Rect:public RECT{

};

メンバをどうするかである。プロパティとしてはx,y,width,heightとして扱いたい。
xとyに関しては少々不細工であってもleftとrightのポインタを持っておけばなんとかなりそうでもない。
struct Rect:public RECT{
 Rect():x(&left),y(&top){}
 int* x,y;
};
widthやheightはそうはいかない。widthはright-left、heightはbottom-topという式になる。

struct Rect:public RECT{
 Rect():x(&left),y(&top){}
 int width()const{return right-left;}
 int width(int newWidth){return right = left + newWidth;}
 int height()const{return bottom-top;}
 int height(int newHeight){return bottom = top + newHeight;}
 int* x,y;
};

形はどうであれこれでひとまずは、できたかのように思える。
しかしもっと自然に振舞いたいと思うわけである。
*rect.x = 1;
rect.width(3);
これを
rect.x = 1;
rect.width = 3;
などど記述できればまさにメンバの名前を変えたように振る舞っているといえよう。

ということで本題である。
プリミティブ型はoverrideできないので一旦クラスにしてやる。
そして代入演算子をoverrideする。
template<class T>
class Dummy{
public:
 Dummy(T* _t):t(_t){}
 T operator=(const T& _t){return *t=t;}
private:
 T* t;
};

こうしてやれば直に代入演算ができる。
struct Rect:public RECT{
 Rect():x(&left),y(&top){}
 Dummy x,y;
};
rect.x = 3;

右辺値参照ではDummy型になって不便なのでキャスト演算子と左辺値参照の+オペレーターを用意しよう。

template<class T>
class Dummy{
public:
 Dummy(T* _t):t(_t){}
 T operator=(const T& _t){return (*t = _t);}
 T operator +()const{return *t;}
 T operator -()const{return -*t;}
 T operator+(T _t)const{ return (*t) + _t;}
 operator T()const{ return *t;}
private:
 T* t;
};

これでちょっと違和感はあるもののprintf("%d\n",+rect.x);などで記述できる。
もっともキャストしてやってもいい。+を打つほうが楽だが。

同様にしてwidthとheight用のクラスを用意する。
template<class T>
class SubDummy{
public:
 SubDummy(const T* _t1,T* _t2):t1(_t1),t2(_t2){}
 T operator=(const T& _t)const{return *t2 = _t + *t1;}
 T operator +()const{return *t2 - *t1;}
 T operator -()const{return -(*t2 - *t1);}
 T operator+(T _t)const{return *t2 - *t1;}
 operator T()const{return *t2 - *t1;}
private:
const T* t1;
T* t2;
};

ついでにostream用にoverrideもしておくとstd::coutできるようになったりして便利。
template<typename T>
inline std::ostream& operator<<(std::ostream& os, const Dummy<T>& dummy){
 return os << +dummy;
}
template<typename T>
inline std::ostream& operator<<(std::ostream& os, const SubDummy<T>& dummy){
 return os << +dummy;
}

できたRectがこいつ。

class Rect : public RECT{
public:
 Rect(int _left,int _top,int _right,int _bottom):x(&left),y(&top),width(&left,&right),height(&top,&bottom){
  left = _left;
  top = _top;
  right = _right;
  bottom = _bottom;
 }
 Dummy<int> x,y;
 SubDummy<int> width,height;
};

int main(void){
 Rect rect(1,2,3,4);
 rect.left = 3;
 rect.width = 5;
 rect.top = 6;
 printf("%d,%d,%d\n",+rect.x,+rect.width,rect.right);
 return 0;
}

本来のleftやtopにアクセスしても大丈夫であるし、widthを変更してもleftやtopに変更がなされる。
出力や代入で右辺値にくる場合は+かキャストが必要になるのが少々気になるがほぼ完全にプロパティを偽っていると言えよう。
PR



・C O M M E N T
  • この記事へのコメント投稿フォームです。


  • Name:
    Title:
    Mail:
    Url:
    Color:
    Decoration:
    Vodafone絵文字 i-mode絵文字 Ezweb絵文字
    Message:
    Pass:
    ※編集時に必要です。


    TRACKBACK
    • この記事のURLとトラックバックURLです。
    • 必要に応じてご使用くださいませ。
    この記事のURL▽
    この記事のトラックバックURL▽
    No.428 No.427 No.426 No.425 No.424 No.423

    Powered by NinjaBlog. Template by うさねこ.
    忍者ブログ[PR]