web-dev-qa-db-ja.com

基本クラスの静的変数はすべての派生クラスで共有されていますか?

私のようなものがあれば

class Base {
    static int staticVar;
}

class DerivedA : public Base {}
class DerivedB : public Base {}

DerivedADerivedBの両方が同じstaticVarを共有しますか、それともそれぞれ独自のものを取得しますか?

それらにそれぞれ独自のものを持たせたい場合、私は何を勧めますか?

50
mpen

これらはそれぞれ、staticVarの同じインスタンスを共有します。

各派生クラスが独自の静的変数を取得するには、別の名前で別の静的変数を宣言する必要があります。

その後、基本クラスで関数の仮想ペアを使用して変数の値を取得および設定し、派生クラスのそれぞれでそのペアをオーバーライドして、そのクラスの「ローカル」静的変数を取得および設定できます。または、参照を返す単一の関数を使用することもできます。

class Base {
    static int staticVarInst;
public:
    virtual int &staticVar() { return staticVarInst; }
}
class Derived: public Base {
    static int derivedStaticVarInst;
public:
    virtual int &staticVar() { return derivedStaticVarInst; }
}

次に、これを次のように使用します。

staticVar() = 5;
cout << staticVar();
43
Greg Hewgill

各クラスに独自の静的変数があることを確認するには、 "Curiously recurring template pattern"(CRTP) を使用する必要があります。

template <typename T>
class Base
{
    static int staticVar;
};

template <typename T> int Base<T>::staticVar(0);

class DerivedA : public Base<DerivedA> {};
class DerivedB : public Base<DerivedB> {};
30
Mark Ingram

それらは同じインスタンスを共有します。

サブクラスごとに個別の静的変数を宣言する必要があります。または、派生クラスによって参照される変数を格納できる単純な静的マップを検討することもできます。


編集:これに対する考えられる解決策は、基本クラスをテンプレートとして定義することです。このテンプレートで静的変数を定義すると、各派生クラスは独自の静的インスタンスを持つことになります。

3
Alan

あなたの場合、staticVarは1つだけです:Base::staticVar

クラスで静的変数を宣言すると、変数はそのクラスに対してのみ宣言されます。あなたの場合、DerivedAはstaticVarを見ることさえできません(それはプライベートであり、保護されていない、またはパブリックではないため)、staticVar変数が存在することさえ知りません。

3
Reed Copsey

@einpoklumによって指定されたサンプルコードは、静的メンバーfoo_の初期化の欠落、FooHolder宣言の継承の欠落、およびpublicキーワードの欠落のため、そのままでは機能しません。クラスを扱っています。これが修正版です。

#include <iostream>
#include <string>

class A {
public:
    virtual const int& Foo() const = 0;
};

template <typename T>
class FooHolder : public virtual A {
public:
    const int& Foo() const override { return foo_; }
    static int foo_;
};

class B : public virtual A, public FooHolder<B> { };
class C : public virtual A, public FooHolder<C> { };

template<>
int FooHolder<B>::foo_(0);
template<>
int FooHolder<C>::foo_(0);

int main()
{
  B b;
  C c;
  std::cout << b.Foo() << std::endl;
  std::cout << c.Foo() << std::endl;
}
1
milembar

残念ながら、C++には仮想静的データメンバーはありません。これを多少なりともシミュレートする方法がいくつかあります。

  • @ GregHewgillのソリューション は、各派生クラスで静的変数を複製します。このソリューションは単純で簡単で、追加のクラスを導入しませんが、冗長であるため、このクラスは気に入らず、むしろ規律を守らなければなりません。
  • @MarkIngramは、 CRTPベースのソリューション を提案しました。これにより、ほとんどの入力を節約できます。ただし、以前はAのサブクラスであったものが実際にはクラスとして関連付けられなくなったため、継承構造が台無しになります。結局、同じ名前でテンプレート引数が異なる2つのテンプレート化された型は、任意の2つの型になります。

[〜#〜] crtp [〜#〜] -basedソリューションを使用して、 mix-in クラスを使用することをお勧めします。

 class A {
      virtual const int& Foo() const = 0;
 }

 template <typename T>
 class FooHolder {
      static int foo_;
      const int& Foo() const override { return foo_; }
 }

 class B : A, virtual FooHolder<B> { }

 class C : B, virtual FooHolder<B> { }

サブクラスで行う必要があるのは、ミックスイン継承を示すことだけです。ここでは見逃している仮想継承の警告があるかもしれません(私はめったに使用しません)。

各サブクラスの静的変数をどこかでインスタンス化して初期化するか、inline変数(C++ 17)にしてテンプレート内で初期化することができます。

この回答は、私の回答から 重複質問 に変更されました。

1
einpoklum

この質問には既に答えられていることは知っていますが、静的メンバーによる継承の小さな例を提供したいと思います。これは、静的変数とそれぞれのコンストラクターで何が起こっているかだけでなく、有用性を示す非常に良い方法です。

FooBase.h

#ifndef FOO_BASE_H
#define FOO_BASE_H

#include <string>

class FooBase {
protected:
    std::string _nameAndId;
private:
    std::string _id;
    static int _baseCounter;

public:
    std::string idOfBase();
    virtual std::string idOf() const = 0;

protected:
    FooBase();    
};

#endif // !FOO_BASE_H

FooBase.cpp

#include "FooBase.h"
#include <iostream>

int FooBase::_baseCounter = 0;

FooBase::FooBase() {
    _id = std::string( __FUNCTION__ ) + std::to_string( ++_baseCounter );
    std::cout << _id << std::endl;
}

std::string FooBase::idOfBase() {
    return _id;
}

std::string FooBase::idOf() const {
    return "";
} // empty

DerivedFoos.h

#ifndef DERIVED_FOOS_H
#define DERIVED_FOOS_H

#include "FooBase.h"

class DerivedA : public FooBase {
private:    
    static int _derivedCounter;

public:
    DerivedA();

    std::string idOf() const override;
};

class DerivedB : public FooBase {
private:
    static int _derivedCounter;

public:
    DerivedB();

    std::string idOf() const override;
};

#endif // !DERIVED_FOOS_H

DerivedFoos.cpp

#include "DerivedFoos.h"
#include <iostream>

int DerivedA::_derivedCounter = 0;
int DerivedB::_derivedCounter = 0;

DerivedA::DerivedA() : FooBase() {
    _nameAndId = std::string( __FUNCTION__ ) + std::to_string( ++DerivedA::_derivedCounter );
    std::cout << _nameAndId << std::endl;
}

std::string DerivedA::idOf() const {
    return _nameAndId;
}    

DerivedB::DerivedB() : FooBase() {
    _nameAndId = std::string( __FUNCTION__ ) + std::to_string( ++DerivedB::_derivedCounter );
    std::cout << _nameAndId << std::endl;
}

std::string DerivedB::idOf() const {
    return _nameAndId;
}

main.cpp

#include "DerivedFoos.h"

int main() {
    DerivedA a1;  
    DerivedA a2;
    DerivedB b1;
    DerivedB b2;

    system( "PAUSE" );
    return 0;
}

__FUNCTION__はコンストラクタで機能していません。その場合は、__PRETTY_FUNCTION__または__func__、または各クラスの名前を手動で入力します:(

1
Francis Cugler