C++是否支持抽象数组长度

Does C++ support abstract array lengths

本文关键字:数组 抽象 是否 支持 C++      更新时间:2023-10-16

如果我有一个基类A,我希望能够在A中编写代码,该代码使用由其子级确定大小的数组

我希望能够有一个指向A的指针数组,但我不希望为每个长度的创建单独的成员函数

我能想出的最好的办法如下:

class A
{
   char type;
   int * values;
   int calc(int pos);  // implementation is not relevant to question
public:
   A (int * arr) : values(arr) {}
   int foo(int pos)
   {
      int index=calc(pos);
      return values[index];
   }
};
template <size_t X>
class B : public A
{
    int vals[X];
public:
    B() : A(vals) {}
};
template<>
class B<0>;  // don't want 0 allowed

这允许A访问子级确定大小的数组,并且该数组是连续内存

但是,它浪费了values指针的空间,并混淆了编译器可以用于优化的信息,因为在实现时,它不必是子级在构造时传递的连续内存,但我希望需要连续内存。

理想情况下,我想直接在A 中使用偏移

C中,这与int values[]一样工作,但没有C++等效的

可以,使用模板:

template<std::size_t size>
struct A {
    A(std::array<int, size> _values) : values{_values} {}
private:
    std::array<int, size> values;
};
struct B : A<4> {
    using A<4>::A;
};

然后,你可以这样使用你的类:

B myB{5, 6, 3, 2};

std::array在堆栈上或直接在结构中分配内存,就像固定数组一样。你可以通过比较尺寸来测试这一点。

如果你需要一个通用基类,你可以这样做:

struct C {
    virtual ~C() {}
    virtual int* data();
    virtual std::size_t size();
};

然后在A:中否决论文

template<std::size_t size>
struct A : C {
    A(std::array<int, size> _values) : values{_values} {}
    int* data() override {
        return values.data();
    }
    std::size_t size() override {
        return size;
    }
private:
    std::array<int, size> values;
};

实现这一点的经典方法是使用继承和虚拟成员:

class A {
    virtual int& value_at(size_t pos);
    // other interesting methods
};
// subclass of A that uses std::vector for storage
class B: public A {
    std::vector<int> storage;
    int& value_at(size_t pos) {
        return storage[pos];
    }
};
// subclass of A that uses a fixed-size array for storage
template<int N>
class C: public A {
    int storage[N];
    int& value_at(size_t pos) {
        return storage[pos];
    }
};
B b;     // ...initialize b...
C<10> c; // ...initialize c...
A *a1 = &b;
A *a2 = &c;
// call a1->value_at(), a2->value_at() to access arrays
// transparently, regardless of storage (along with other
// public methods of A).

这种方法将要求A::value_at通过虚拟表或等效机制进行调度。如果在编译时知道将使用哪种存储策略,则可以将A作为模板类:

template<typename T>
class A: public T {
    // other interesting methods, that use value_at() from T
};
class vec_storage {
    std::vector<int> storage;
public:
    int& value_at(size_t pos) {
        return storage[pos];
    }
};
// subclass of A that uses a fixed-size array for storage
template<int N>
class array_storage {
    int storage[N];
public:
    int& value_at(size_t pos) {
        return storage[pos];
    }
};
A<vec_storage> b;
A<array_storage<10>> c;

在本例中,bc在运行时将在没有额外间接性的情况下执行,但代价是没有泛型基类,因此不能向期望某个A &的函数传递对bc的引用。

正如Fred Larson所指出的,为了获得更改对象数组大小的基本能力,您可能正在寻找模板。其实很简单。。。

template <const int arraySize>
class MyClass
{
    private:
        int vals[arraySize];
}

在这一点上,您甚至不需要任何派生类。您可以随时随地使用任何尺寸的

但是,请记住,在这种情况下,MyClass不是一个有效的类型。因为这是一个模板,所以必须指定大小。

MyClass wrong; //This will throw an error. MyClass is not a type.
MyClass<64> right; //This is a proper use of the type.

如果您需要存储不同大小的对象的额外功能,则可以使用虚拟的[但不是抽象的]基类将其与继承结合起来。

(请忽略这个糟糕的设计,因为我在这个例子中没有费心定义我的构造函数/析构函数。)

class BaseClass
{
    public:
        BaseClass(){}
        virtual int access(int i)
        {
            return 0;
        }
        virtual ~BaseClass(){}
};
template <const int arraySize>
class MyClass : public BaseClass
{
    public:
        MyClass(){}
        int access(int i)
        {
            return vals[i];
        }
        ~MyClass(){}
    private:
        int vals[arraySize];
};

警告:如果您希望能够将派生类存储在向量中,则基不能是抽象的,这一点至关重要。请参阅此问题。

当然,在本例中,您可能希望创建用于访问数组的包装器函数。