最近、Webを見ていて「安易な継承を使うよりも委譲を使うほうが良い」
見たいな事が書いてあったのですが、
「委譲」とは具体的にどのようなものでしょうか?
「継承」との違いは?

ご存知のかた、教えてください。

このQ&Aに関連する最新のQ&A

A 回答 (3件)

「委譲」とは、他のオブジェクトに処理(責任)を譲ることをいいます。


多分、ソースを見比べると雰囲気が分かります。

クラス C のメソッド print() を、頭に「★」を付加するように拡張する例です。

class C {
public:
void print(char* str) { cout << str << endl; }
};

//継承で機能拡張
class C1 : public C {
public:
void print(char* str) {
cout << "★";
C::print(str);
}
};

//委譲で機能拡張
class C2 {
private:
C* c_;
public:
C2() { c_ = new C(); }
void print(char* str) {
cout << "★";
c_->print(str);
}
}
    • good
    • 0
この回答へのお礼

大変解かり易い例をあげていただき、ありがとうございます。
なるほど、特別な機能という訳ではないのですね。(^^;

・・・ただ、やってることは理解できたのですが、
なぜ継承よりも委譲の方が良いとされるのでしょうか?
やはり、ソースの見通しが良いとかそういう理由なのでしょうか。

追加の質問で申し訳ありません。

お礼日時:2001/12/08 01:08

> なぜ継承よりも委譲の方が良いとされるのでしょうか?



オブジェクト指向というのは、要求される「機能」を実装してゆくのではなく、対象と
している問題領域を *素直に* モデル化することで、それが要求される機能の変更に
対して強い(変更箇所が局所化されている)のが利点だ、というのは本で読んだとおりです。

その「素直なモデル化」に関係することです。

派生というのは、is-a の関係にある関連です。

継承元→継承先と見ると、それは「具体化」になっているべきで、逆に、継承先→継承元と
見ると、「抽象化」になっているべきです。概念として包含関係になっているように。

例えば、動物→哺乳類→猿、というふうに概念が具体的になってゆきます。

猿から派生させるとしたら、猿を更に具体化するような、ゴリラだとかチンパンジーは
OK なんですけど、求める振る舞い(機能と言っても良い)が、殆ど同じだからといって
猿から犬を派生させちゃいけないんです。

これは「安直な派生」とみなされます。

ただ、現実問題として、同じ振る舞いをあちこちに記述するのは、オブジェクト指向が
求めるものからいっても、ふさわしいことではないですから、そういった場合の解決
方法として「委譲」の方が良いのではないか、ということです。

さっきの犬の話に戻ると、具体化のレベルを合せる意味では、犬は哺乳類から派生させる
べきで、実装を共有させるために、幾つかのメソッドを猿に委譲する方が、オブジェクト
としては素直なモデルです。


安直な派生のもうひとつのケースとして、多重継承があります。

新しい機能を実装するクラスを考えたときに、あの機能も流用したい、この機能も
流用したい、だから、いろんなクラスから多重継承してしまう、というケース。

これも「機能の実装」の側面で考えているわけですから、派生は概念の具体化だ、と
言うことに反します。

機能を流用したければ、そのオブジェクトを抱えておいて、必要な機能だけを委譲して
使う方が良い、と言われます。

# ん~、あまり上手な説明ではないですね (^^;
# 補足は遠慮無くどうぞ。できる範囲で頑張ります。
    • good
    • 1
この回答へのお礼

御礼が遅くなって申し訳ないです。
大変丁寧な解説をしていただき、ありがとうございました。

お礼日時:2001/12/11 18:43

委譲…譲り渡すこと


禅譲…徳のあるもの、有能な者に位を譲ること

禅譲??
『安易な継承(世襲)でなく『禅譲』を使うほうが良い…』なら意味がわかりますが。

具体的にはどんな分野・場面で使われていたのでしょうか?(もしかして本物のプログラム用語なのでしたら、一笑にふしてください。←まったくの素人なので勘違いかもしれんです。)
    • good
    • 0
この回答へのお礼

すみません、「委譲」は本物のプログラム用語です。
オブジェクト指向の分野で耳にしたもので質問しました。

お礼日時:2001/12/08 01:10

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q継承したクラスを、継承元のクラス型の引数に渡すとどうなるのでしょうか?

継承したクラスを、継承元のクラス型の引数に渡すとどうなるのでしょうか?

以下のようなケースで、

#include "stdio.h"
using namespace std;

// baseクラス
class base {
private:
 int m_nII;
 int m_nJJ;
 int m_nKK;
public:
 base(int i,int j,int k){ m_nII=i; m_nJJ=j; m_nKK=k; }
 int GetSum(){ return (m_nII+m_nJJ+m_nKK); }
};

// base 継承クラス
class hoge : public base {
public:
 hoge() : base(1,2,3){}
};

void func(base* obj){ // baseクラスを引数に取る関数
 printf("sum is %d\n", obj->GetSum());
}

// main
int main(){
 hoge objHoge;
 func((base*)&objHoge); // <-キャストして渡す
 return 0;
}

として、一応、gccでコンパイルは通り、実行結果も期待通りだったのですが、
このやり方で問題は無いのでしょうか?
(たとえば継承先のクラスが独自のメンバを持っていたりなどした場合、期待した結果にならないとか・・)

よろしくお願いします。

継承したクラスを、継承元のクラス型の引数に渡すとどうなるのでしょうか?

以下のようなケースで、

#include "stdio.h"
using namespace std;

// baseクラス
class base {
private:
 int m_nII;
 int m_nJJ;
 int m_nKK;
public:
 base(int i,int j,int k){ m_nII=i; m_nJJ=j; m_nKK=k; }
 int GetSum(){ return (m_nII+m_nJJ+m_nKK); }
};

// base 継承クラス
class hoge : public base {
public:
 hoge() : base(1,2,3){}
};

void func(base* obj){ // baseクラスを引数に取る関数
 printf("sum is...続きを読む

Aベストアンサー

言語仕様通りで問題ありません。

実行時の挙動の話をすると、
1.プログラムは関数と変数に分けられてメモリ上に配置される
プログラムがメモリにロードされて命令が実行される段階では、プログラムはテキスト領域、静的領域、ヒープ領域、スタック領域に分けられて実行されます。
テキスト領域には、関数が機械語に翻訳された内容が格納されますので、このことからメンバ関数を増やしても問題になりません。

2.メンバ変数は基底(継承元)クラスのメンバの後に継承クラスのメンバが継ぎ足しされる。

hoge のインスタンス(obj)のメモリ上の配置
1000から1019のアドレスにobjは格納される。
1000から1011のアドレスにobjのbaseクラスのメンバが格納される。
1012から1019のアドレスにobjのhogeクラスのメンバが格納される。
1000: base.m_nII; // base クラスのメンバ変数(obj)
1004: base.m_nJJ; // 〃
1008: base.m_nKK; // 〃
1012: hoge.m_anpan; // hoge クラスで追加したメンバ変数
1016: hoge.m_pizza; // 〃

3.メンバ関数は、構造体へのポインタを受け取る関数
「func」関数の引数で「base *obj」を指定し「obj->GetSum()」と呼び出すことは
「static int base::GetSum(base *obj)」の呼び出しと同じである。
つまり、構造体へのポインタを引数とする関数に等しい。

// 以上のことをC言語に直すとこんな風になります。
struct base { int m_nII, m_nJJ, m_nKK; };
struct hoge { struct base base; int m_anpan, m_pizza; };

void base_init(struct base *obj, int i, int j int k)
{ obj->m_nII = i; obj->m_nJJ = j; obj->m_nKK = k; }
int base_GetSum(struct base *obj)
{ return obj->m_nII + obj->m_nJJ + obj->m_nKK; }

void hoge_init(struct hoge *obj)
{ base_init((struct base *)obj, 1, 2, 3); obj->m_anpan = 120; obj->m_pizza = 3000; }
int hoge_GetPizza(struct hoge *obj)
{ return obj->m_pizza; }

int main() {
struct hoge obj; hoge_init(&obj);

printf("sum is %d\n", base_GetSum((struct base *)&obj));

obj.m_anpan = 1200;
printf("Anapn is \\%d\n", obj.m_anpan);
printf("Pizza is \\%d\n", hoge_GetPizza(&obj));

// アドレス表示
printf("obj.base.m_II : %x\n" &obj.base.m_II);
printf("obj.base.m_JJ : %x\n" &obj.base.m_JJ);
printf("obj.base.m_KK : %x\n" &obj.base.m_KK);
printf("obj.m_anpan : %x\n" &obj.m_anpan);
printf("obj.m_pizza : %x\n" &obj.m_pizza);

return 0;
}

ポイントは、クラスという概念は関数と構造体を一体的に扱う仕組みであり言語仕様上にのみ存在し、実行時にメンバ関数は単純な引数渡しと関数呼び出しになるということです。

関連として、関数のオーバーライド、ポリモルフィズムなどがありますが、それの実体は構造体のレコードに関数ポインタ配列へのポインタを持たせているだけです。
つまり理解するには、C言語の関数ポインタ、関数ポインタの配列について理解することが、遠回りですが近道になります。
# 理解しなくても使うことはできますが(ーー;)

あと、コードっぽいのは適当に書いただけなので間違いがあると思います。

参考URL:http://brain.cc.kogakuin.ac.jp/~kanamaru/lecture/MP/final/part06/node8.html

言語仕様通りで問題ありません。

実行時の挙動の話をすると、
1.プログラムは関数と変数に分けられてメモリ上に配置される
プログラムがメモリにロードされて命令が実行される段階では、プログラムはテキスト領域、静的領域、ヒープ領域、スタック領域に分けられて実行されます。
テキスト領域には、関数が機械語に翻訳された内容が格納されますので、このことからメンバ関数を増やしても問題になりません。

2.メンバ変数は基底(継承元)クラスのメンバの後に継承クラスのメンバが継ぎ足しされる。

hoge ...続きを読む

Qprivate継承はどう使う?

C++の話です

class Base{.....};
class Derived1 : public Base{.....};
class Derived2 : private Base{.....};

と書くことができますが、public継承とprivate継承にはそれぞれ意味がありますよね。
public継承は"is-a"関係を意味していて、private継承は
"is-implemented-in-terms-of"関係を表していると言います。
public継承を実際に動くプログラムは思いつくのですが、private継承を使ったプログラムが思いつきません(というより有効に使えません)

派生クラスから呼び出せない、外部からも呼び出せないメンバをどう使うのでしょうか?

Aベストアンサー

> 基底クラスの段階でのprivateと継承してからの段階でprivate
> 扱いになるのとでは意味が違うのでしょうか?

継承時のアクセス制限は、いうなれば「継承する側の都合」なので、
「継承する自分自身を"呼ぶクラス"や自分自身より"下位の派生クラス"」に影響します。
privateで継承したクラス自身はこの影響を受けずに、
自分自身は基底クラスの方の影響だけを受けるということです。

定義も派生する側に書きますよね。
同じ基底クラスからでも継承するクラス毎に別設定できるということです。
# なので、他のクラスの一部実装だけを流用するためにprivateで実装継承なんてこともありえるのです。
# publicで継承すると余計なインターフェイスまで取り込んじゃいますが、
# privateなら内部で利用するだけですから余分なものがあっても自分が使うだけで、外には漏らしません。
# (private継承できない時/言語では、変わりに「委譲」することが多いです)
# (例えばJavaとかはサブクラス側で狭めることはできないですが、C++は必要に応じて選択肢が多い、と。(その分複雑ともいう))

一方、基底クラス側のアクセス制限は「規定クラスの都合」なので、
継承する自分自身も「基底クラスの外、より下位の派生」ですから、privateの影響を受けます。
これを勝手にサブクラスが変更することは、当然できません。

> 派生クラスから呼び出せない、外部からも呼び出せないメンバをどう使うのでしょうか?

本題に戻ると、private継承であれば継承したクラス自身は(自分だけ)使えますし、
privateなメンバであれば自分だけは使えますので、自分専用の内部実装を書くのが基本かと。

# ちなみに、C++のprivate:はあくまで「クラスの"呼び出し制限"」なので、例えばこんなことも可能。

/** 純粋仮想関数を含む基底クラス */
class Foo {
private:
 /** 内部実装はサブクラスに要請 */
 virtual void doSomething() = 0; /* 呼び出しはprivateなのに実装は純粋仮想 */
public:
 /** 外部インターフェイスは持つ */
 void do(){ doSomething(); } /* このクラス内からしか呼べないようにdoSomethingはprivate */
}
/* 継承したサブクラスは、Foo::doSomethingを呼べないのに実装は定義しなければならない */

> 基底クラスの段階でのprivateと継承してからの段階でprivate
> 扱いになるのとでは意味が違うのでしょうか?

継承時のアクセス制限は、いうなれば「継承する側の都合」なので、
「継承する自分自身を"呼ぶクラス"や自分自身より"下位の派生クラス"」に影響します。
privateで継承したクラス自身はこの影響を受けずに、
自分自身は基底クラスの方の影響だけを受けるということです。

定義も派生する側に書きますよね。
同じ基底クラスからでも継承するクラス毎に別設定できるということです。
# なの...続きを読む

Q「割り算」 と 「分数の掛け算」

double型の変数にある値が代入されていて、
その数を半分にしようとしました。
演算部分を /2 としたらエラーが出てしまいました。
いろいろ試した結果、*0.5とすれば
ちゃんと計算されるようなのですが、
どうしてこのようなことが起こるのか、よくわかりません。
どなたか、ご教授ください。よろしくお願いします。

Aベストアンサー

>#define c 1
と言うことで、c は int の定数と判断されます

>double b;
で、格納する変数は double になっているのですが、

>b = c / 2;
の右辺を見ると、
定数のintである1を、定数のintである2で割っているため、この文の結果はintになってしまいます。
本来なら0.5となるべきところですが、/ の両辺が int の場合は、結果を int で返しますので、0.5 の実数部分の0が結果となるわけです。
これを右辺の b に代入するときに 暗黙の型変換にてdouble として入れているのですが、すでに切り捨てられてしまった小数部分は帰ってこないわけですね。

2.0 とするとうまくいくのは、
/ のどちらかが double の場合は、片方の int を暗黙に double に変換して double の結果を返すからですね。

演算式の、型変換について、もう一度ヘルプ等で調べて見てください。

Q「NULLポインタ」と「演算の結果としてのアドレス0」との比較

ものすごく基本的な疑問です。。。

「C言語FAQ日本語訳」http://www.kouno.jp/home/c_faq/

ここの「05.ヌルポインター」を見ると以下のような意味の記述があります。

「NULLポインタは他のどんなポインタの値とも区別可能で、有効なポインタと比較しても等しくなる事はない」
「ポインタを書くべき場所に書かれた定数0はコンパイル時にNULLポインタに変換される」

そこで以下のプログラムを Borland C++ 5.5 for Win32 でコンパイル・実行してみたところ、
p1 == p2
とりました。これって変ですよね?

p1 は明示的に定数0で初期化しているのでNULLポインタですが、p2 は演算の結果としてアドレス0番地を指しているので、NULLポインタでは無いですよね? これはコンパイラが間違っていると思って良いのでしょうか?

#include <stdio.h>

int main()
{
char *p1,*p2;

p1 = 0;
p2 = (char *)1;

p2--;

if(p1 == p2){
printf("p1 == p2");
}
else{
printf("p1 != p2");
}

return 0;
}

ものすごく基本的な疑問です。。。

「C言語FAQ日本語訳」http://www.kouno.jp/home/c_faq/

ここの「05.ヌルポインター」を見ると以下のような意味の記述があります。

「NULLポインタは他のどんなポインタの値とも区別可能で、有効なポインタと比較しても等しくなる事はない」
「ポインタを書くべき場所に書かれた定数0はコンパイル時にNULLポインタに変換される」

そこで以下のプログラムを Borland C++ 5.5 for Win32 でコンパイル・実行してみたところ、
p1 == p2
とりました。これって変ですよ...続きを読む

Aベストアンサー

規格上, 整数型からポインタに変換してよいのは次の 2つの場合に限られます:
1.もともとポインタであった値を十分な大きさの整数型に変換しており, それを元と互換なポインタ型に再度変換する場合.
2.整数型の値が定数の 0 であった場合.

これ以外の処理を行うようなプログラムは ill-formed であり, その動作は undefined である (= 規格はその動作について見放す) と規定されています. 従って示されたプログラムがどのように動作したとしても規格は一切関知しません.

ちなみに NULL ポインタと「アドレスの 0番地」は全く無関係です>#1 の人

QC# と Javaの継承の違い

http://www.atmarkit.co.jp/fdotnet/csharp_abc/csharp_abc_004/csharp_abc02.html
上記、URLの記事にて

Person someone = new Taro();  → TaroはPersonのサブクラス
Console.WriteLine( someone.getName() );
という記述がありgetName()の結果はPersonクラスのgetName()が表示されるとなっていました。

同様のことをJavaにて試してみたところ
Parent pc = new Children(); → ChildrenはParentのサブクラス
pc.getName();

getName()にはChildrenクラスのgetName()が表示されました。
C#とJavaでは継承の挙動に違いがあるのでしょうか。

Aベストアンサー

ソコの解説をよく読むべし。 C#は

public new string getName() { ... }

new の有無で挙動が変わります(変えることができます)。


このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング

おすすめ情報