Подтвердить что ты не робот

Статические переменные в базовом классе, разделяемые всеми производными классами?

Если у меня есть что-то вроде

class Base {
    static int staticVar;
}

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

Будут ли теги DerivedA и DerivedB использовать один и тот же staticVar или каждый из них получит свои собственные?

Если бы я хотел, чтобы у каждого была своя, что бы вы порекомендовали, я делаю?

4b9b3361

Ответ 1

Каждый из них будет иметь один и тот же экземпляр 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();

Ответ 2

Чтобы у каждого класса была своя собственная статическая переменная, вы должны использовать "Любопытно повторяющийся шаблон" (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> {};

Ответ 3

Они будут использовать один и тот же экземпляр.

Вам нужно будет объявить отдельные статические переменные для каждого подкласса, или вы могли бы рассмотреть простую статическую карту, в которой вы могли бы хранить переменные, на которые ссылаются производные классы.


Изменить. Возможным решением для этого будет определение вашего базового класса в качестве шаблона. Наличие статической переменной, определенной в этом шаблоне, будет означать, что каждый производный класс будет иметь свой собственный экземпляр static.

Ответ 4

В вашем случае есть только один staticVar: Base::staticVar

Когда вы объявляете статическую переменную в классе, переменная объявляется только для этого класса. В вашем случае DerivedA даже не может видеть staticVar (поскольку он является закрытым, не защищенным или общедоступным), поэтому он даже не знает, существует ли переменная staticVar.

Ответ 5

Увы, C++ не имеет виртуальных статических членов данных. Есть несколько способов смоделировать это, более или менее:

  • Решение @GregHewgill позволяет вам повторить статическую переменную в каждом производном классе; Это решение простое, прямое и не вводит дополнительные классы, но оно мне не нравится, так как оно многословно, и вы должны быть довольно дисциплинированны с ним.
  • @MarkIngram предложила CRTP -based решение, которое сэкономит вам большую часть печати; однако, это портит структуру наследования, потому что то, что раньше было подклассами A, больше не связано как классы. В конце концов, два шаблонных типа с одним и тем же именем, но разными аргументами шаблона могут быть просто любыми двумя типами.

Я предлагаю другое решение CRTP -based с использованием смешанного класса:

 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) и инициализировать ее в шаблоне.

Этот ответ был адаптирован из моего ответа на двойной вопрос.

Ответ 6

Я знаю, что на этот вопрос уже дан ответ, но я хотел бы привести небольшой пример наследования со статическими членами. Это очень хороший способ продемонстрировать полезность, а также то, что происходит со статическими переменными и соответствующими конструкторами.

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__, или вручную ввести имя каждого класса :(.

Ответ 7

Пример кода, предоставленный @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;
}