关于c ++:基类中的静态变量是否由所有派生类共享?

Are static variables in a base class shared by all derived classes?

如果我有类似的东西

1
2
3
4
5
6
class Base {
    static int staticVar;
}

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

DerivedADerivedB是共享同一个staticVar还是各自拥有自己的staticVar呢?

如果我想让他们每个人都有自己的,你会建议我做什么?


他们将共享同一个staticVar实例。

为了让每个派生类获得自己的静态变量,需要用不同的名称声明另一个静态变量。

然后,可以使用基类中的一对虚拟函数来获取和设置变量的值,并在每个派生类中重写该对,以获取和设置该类的"局部"静态变量。或者,可以使用返回引用的单个函数:

1
2
3
4
5
6
7
8
9
10
class Base {
    static int staticVarInst;
public:
    virtual int &staticVar() { return staticVarInst; }
}
class Derived: public Base {
    static int derivedStaticVarInst;
public:
    virtual int &staticVar() { return derivedStaticVarInst; }
}

然后,您可以将其用作:

1
2
staticVar() = 5;
cout << staticVar();


为了确保每个类都有自己的静态变量,您应该使用"奇怪的循环模板模式"(crtp)。

1
2
3
4
5
6
7
8
9
10
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> {};


你的案子只有一个staticVarBase::staticVar

在类中声明静态变量时,只为该类声明该变量。在您的例子中,deriveda甚至看不到staticVar(因为它是私有的、不受保护的或公共的),所以它甚至不知道存在staticVar变量。


他们将共享同一个实例。

您需要为每个子类声明单独的静态变量,或者考虑一个简单的静态映射,您可以在其中存储派生类引用的变量。

编辑:一个可能的解决方案是将基类定义为模板。在这个模板中定义一个静态变量意味着每个派生类都将拥有它自己的静态实例。


我知道这个问题已经被回答了,但是我想提供一个静态成员继承的小例子。这是一个非常好的方法来演示实用性以及静态变量和相应的构造函数所发生的事情。

英尺基地

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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

Fo.Base.CPP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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

派生的

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
#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

派生Foop.CPP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#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;
}

主CPP

1
2
3
4
5
6
7
8
9
10
11
#include"DerivedFoos.h"

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

    system("PAUSE" );
    return 0;
}

如果__FUNCTION__在构造函数中不为您工作,那么您可以使用类似的东西来替换它,例如__PRETTY_FUNCTION____func__,或者手动键入每个类的名称:(


唉,C++没有虚拟静态数据成员。有几种方法可以或多或少地模拟这种情况:

  • @Greghewgill的解决方案让您在每个派生类中复制静态变量;这个解决方案简单、简单,并且不引入其他类,但是我不喜欢这个,因为它很冗长,您必须严格遵守它。
  • @Markingram建议使用基于crtp的解决方案,这样可以节省大部分类型;但是,它会破坏继承结构,因为以前的a子类不再真正与类相关。毕竟,具有相同名称但不同模板参数的两个模板化类型可以是任意两个类型。

我建议使用不同的基于crtp的解决方案,在课堂上混合使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
 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 { }

 class C : B, virtual FooHolder { }

在子类中唯一需要做的事情就是指出继承中的混合。这里可能缺少一些虚拟继承警告(因为我很少使用它)。

请注意,您必须实例化和初始化每个子类"静态变量",或者您可以使它成为EDCOX1×5变量(C++ 17),并在模板中初始化它。

此答案根据我的回答改编为重复问题。