关于c ++:在抽象基类中声明静态,常量成员,将其分配给派生类中的值?

 2020-02-15 

Declare static, constant member in abstract base class, assign it to a value in derived class?

我有一个抽象基类和一对从该基类派生的类。我想介绍一个static const成员,该成员在两个派生类之间具有不同的值,但对于给定派生类的所有实例都具有相同的值。

我有使用分配给两个派生类之一的实例的基类指针的代码,因此我可以通过更改分配给基类指针的内容轻松地在派生类之间切换。我希望能够以类似的方式使用基类获取派生类的常量值,以便可以轻松地在两个类之间进行切换。所需的行为如下所示:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
using namespace std;

// Abstract base class
class A {
    protected:
    int c; // this is really a constant and should also be static
    public:
    static int s;
    int get_c() const {
        return this->c;
    }
    virtual int foo() = 0; // makes this class abstract
};

class B : public A {
    public:
    static const int sb = 10;
    B() {
        this->c = 1;
    }
    int foo() {
        return -1;
    }
};

class C : public A {
    public:
    static const int sc = 20;
    C() {
        this->c = 2;
    }
    int foo() {
        return -2;
    }
};

int main() {
    B btest;
    C ctest;

    A *ptr = &btest; // pointer to instance of B
    //cout << ptr->c << endl; // would fail compilation (c is protected)
    cout <<"B's c =" << ptr->get_c() << endl;
    cout <<"B's foo() returns" << ptr->foo() << endl;
    cout <<"Accessing B's static const member:" << B::sb << endl;

    ptr = &ctest; // pointer to instance of C
    //cout << ptr->c << endl; // would fail compilation (c is protected)
    cout <<"C's c =" << ptr->get_c() << endl;
    cout <<"C's foo() returns" << ptr->foo() << endl;
    cout <<"Accessing C's static const member:" << C::sc << endl;

    return 0;
}

在上面的代码中,sbsc是我想要的static const成员,但是它们的问题是基类A不了解它们。基类成员c正在执行我想要的操作,但未达到所需的static const(我已将c设置为protected成员,以便无法对其进行修改,但是如果可以将其声明为const,则它可以是public)。此代码具有c中的所需行为:

  • 我可以使用基类指针从每个派生类获得c的不同值。
  • c实际上是常量,因为它是protected,并且我不提供setter函数。
  • 但是,c并不是真正的常量,因为它不是const也不是static,因此每个实例都具有不必要的副本。

    有没有一种方法可以获得期望的行为,并且还像sbsc一样用static const声明c


    您不能在A中使用静态变量来实现它; 这对于A的所有子类型都是通用的,因此您无法为每个子类型获得不同的值。 由于仅在运行时知道基类指针后面的对象的动态类型,因此您需要一种运行时机制来从中获取值。

    这可以使用每个对象的变量,如果在构造函数中对其进行初始化,则可以为const

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class A {
        // ...
        const int c;
        A(int c) : c(c) {}
    };

    class B : public A {
        // ...
        B() : A(1) {}
    };

    class C : public A {
        // ...
        C() : A(2) {}
    };

    或每个子类重写以返回不同值的虚函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class A {
        // ...
        int get_c() const = 0;
    };

    class B : public A {
        // ...
        int get_c() const {return 1;}
    };

    class C : public A {
        // ...
        int get_c() const {return 2;}
    };

    两种选择都符合您的要求; 一个代价是在访问时调用虚拟函数,而另一个代价是每个对象变量的代价。 您必须决定哪种成本更适合您的需求。