質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.20%

シングルトンパターンを持つ基底クラスを継承したい

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 7,415

penpen

score 12

今作成しているプログラムでシングルトンパターンを持つ基底クラスを継承したクラスを作る必要が出てきたので,こちらのサイト(http://glass5er.hatenablog.com/entry/20130630/1372556362)を参考にしていたのですが,このサイトで扱われているソースコードに対して1つ疑問があったので質問しました.

サイトのソースコードは以下のようになっています.

#include <iostream>
#include <string>
 
using namespace std;
 
//----------------
 
template <class U>
class SingletonParent
{
  public:
    static U* getInstance() {
      static U instance;
      return &instance;
    }
 
  protected:
    //--  template func in template class available
    template<typename T> void print2Times(T v);
 
    SingletonParent() {
      base_str = string("BASE");
    }
    virtual ~SingletonParent() {}
    string base_str;
};
 
 
//--  use template succession
class SingletonChild : public SingletonParent<SingletonChild> {
  public:
    SingletonChild() {
      child_value = 0;
    }
    ~SingletonChild() {}
 
    void set(int v) { child_value = v; }
    int  get()      { return child_value; }
 
    void print()    {
      print2Times(base_str);
      print2Times(child_value);
    }
  private:
    int child_value;
};
 
//--  when defining template func in template class,
//--  2 template declaraton required
template<class U> template<typename T>
void
SingletonParent<U>::print2Times(T v)
{
  cout << v << v << endl;
}
 
 
//----------------
 
 
int
main(int argc, char const* argv[])
{
  //--  actually the same instance
  SingletonChild *inst1 = SingletonChild::getInstance();
  SingletonChild *inst2 = SingletonChild::getInstance();
 
  //--  both 0
  cout << inst1->get() << endl;
  cout << inst2->get() << endl;
 
  //--  BASEBASE, 00
  inst1->print();
 
  inst2->set(3);
 
  //--  both 3
  cout << inst1->get() << endl;
  cout << inst2->get() << endl;
 
  //--  BASEBASE, 33
  inst1->print();
  return 0;
}

派生クラスのコンストラクタがpublicになっているので,これではシングルトンの意味がないと思い,privateに変更したところ,下記のようにSingletonChildのコンストラクタがprivateだからアクセスできないと怒られました...
私としては,基底クラスから継承したgetInstance()を使って派生クラスの内部からSingletonChild()にアクセスしているので,問題ないと考えていたのですが,どのあたりが間違っているのでしょうか?

g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/main.d" -MT"src/main.d" -o "src/main.o" "../src/main.cpp"
../src/main.cpp: In instantiation of ‘static U* SingletonParent<U>::getInstance() [with U = SingletonChild]’:
../src/main.cpp:93:43:   required from here
../src/main.cpp:70:5: error: ‘SingletonChild::SingletonChild()’ is private
     SingletonChild() {
     ^
../src/main.cpp:40:16: error: within this context
       static U instance;
                ^
make: *** [src/main.o] エラー 1

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

私が昔ハマった勘違いを同じようにされているかもしれないので投稿します。

私は昔classのprivateやprotected宣言をクラスのインスタンスから見た公開範囲の指定だと考えていました。しかし、実際には同一クラスであれば別のインスタンスからも変更できます。

class Hoge
{
private:
    int _value;
public:
    Hoge(int value): _value(value) {}

    int GetValue() {  _value; }
    void AddsTo(Hoge &obj)
    {
        // 別のインスタンスのprivateメンバを変更する
        // ※昔は別インスタンスなのでできないと思っていた
        obj._value += this->_value;
    }
};

void main()
{
    Hoge a(10);
    Hoge b(5);

    a.AddsTo(b);
    printf("%d", b.GetValue()); // 15が表示される
}

class の private や protected の宣言は、クラス定義から見た公開範囲を指定しています。
privateは継承関係無く同一クラス内のみ、protectedは継承関係にある派生クラス側への許可のみで、そのためにfriend宣言もクラス定義間の条件付けになっています。
このために、派生クラス側の private な指定には、基底クラス側からはアクセスできません。
基底クラス側に処理を公開する方法は、public宣言か、friend宣言しかありません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/05/05 15:00

    回答ありがとうございます.

    指摘されたとおり,今まで私はprivateやprotected宣言を"クラスのインスタンスから見た公開範囲の指定"と考えていたようです.
    指摘されるまではそのように考えていたことにすら気がつかなかったのですが...

    private や protected の宣言は、”クラス定義から見た公開範囲を指定”と考えると辻褄があって色々とスッキリしました.
    このような誤って理解している場合は自分ではなかなか気が付けないので大変ありがたいです.
    本当にありがとうございました.

    キャンセル

0

privateメンバは、自クラス内部 または friend宣言したクラス からしかアクセスできません。

SingletonParent<SingletonChild>クラスをfriend宣言すれば、仰っている内容は実現できます。
class SingletonChild : public SingletonParent<SingletonChild> {
  friend class SingletonParent<SingletonChild>;  // 追加
  private:  // privateに変更
    SingletonChild() {
      child_value = 0;
    }
  public:
  ...

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/05/04 00:16

    回答ありがとうございます.

    私は,基底クラスから継承したメンバは派生クラスのメンバになる(つまり,getInstance()のアクセスは内部からのアクセスになる)と理解していたのですが,誤った理解をしていたみたいですね...
    実際の継承のメカニズムはどのようになっているのでしょうか?
    参考書やネットには継承の内部的な話はなかなか載っていないので,教えていただけるとありがたいです.

    キャンセル

  • 2015/05/06 00:31

    (分かりやすさはさておき)正確な定義は下記を参照してみてください。

    http://ezoeryou.github.io/cpp-book/C++11-Syntax-and-Feature.xhtml#class.access

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.20%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる