在 class 定义中使用内置数组,但将大小推迟到派生的 class 会导致隐藏吗?

Would using a built in arrays within a class definition, but with size deferred to derived class cause hiding?

树中的每个 class 都依赖于与其他 class 的关系,用于监视电压和电流传感器(通常命名为 1,2,3...)。问题在于这些传感器中有多少取决于所模拟的单元类型;这意味着只有派生的 classes 会知道。

#include <iostream>
class A {
public: 
     A() {};
     virtual void Display() = 0;
protected:
     int array[]; // size is purposefully left out in base class
};

class B : public A {
public:
     B(int numbers[4]);
     virtual void Display();
protected:
     int array[4]; // 4 sensors are required, number fixed at compile time
}

B::B(int numbers[4]) {
     for(int i=0; i<4; i++)
          array[i] = numbers[i];
}

void B::Display() {
     cout << " B numbers are: ";
     for(int i = 0; i < 4; i++)
          cout << array[i] << " ";
     cout << endl;
}

class C : public A {
public: 
     C(int numbers[8]);
     virtual void Display();
protected:
     int array[8]; // 8 sensors needed, number fixed at compile time
};

C::C(int numbers[8]) {
     for(int i=0; i<8; i++)
          array[i] = numbers[i];
}

void C::Display() {
     cout << " C numbers are: ";
     for(int i = 0; i < 8; i++)
          cout << array[i] << " ";
     cout << endl;
}

此驱动程序表明这在使用 g++ 编译器时在技术上是可行的,但我担心我可能会通过在 B 和 C 中重新声明数组来隐藏数据 classes。

main() {
    int B_numbers[] = {1,2,,3,4};
    int C_numbers[] = {5,6,7,8,9,10,11,12};
    B b(B_numbers[]);
    C c(C_numbers[]);
    b.Display();
    c.Display();
}

感谢您提供的任何建议。

可以创建函数 'virtual',这意味着实际调用的函数将通过在对象的 vtable 中查找给定对象来确定。这就是重载的工作原理。为派生 class 实例化的对象将有一个指向派生函数的 vtable。

C++ 不会为数据成员这样做。您派生的 class 的数据成员与基础成员不同。如果您尝试访问数组,您 select 将基于指向对象的变量类型, 而不是 对象本身的类型。而且那个也不是什么好东西

C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off. —Bjarne Stroustrup's FAQ

您显示的任何代码中都没有使用 A::array,因此您可以删除不需要的代码。 BC 只是有他们自己的个人数组,他们各自对 Display() 的覆盖知道如何处理 - A 不需要在这里涉及。只需:

struct A {
    virtual void Display() = 0;
};

请注意,就构造函数而言,B(int numbers[4]); 实际上与 B(int *numbers) 没有任何不同,那里的数字只是给人一种安全的错觉——我可以轻松地将错误大小的数组传递给那里。出于这个原因,更喜欢使用 std::array - 它具有可复制构造的额外好处:

class B : public A {
public:
     B (std::array<int, 4> const& arr)
     : array(arr)
     { }

     virtual void Display();
protected:
     std::array<int, 4> array;
}

您可能会受益于使用模板化方法在基 class 中提供 数组 ,导出的 classes 可以使用并定义数组的大小在编译时。

像这样:

#include <array>
#include <iostream>

template<size_t N>
class A
{
public:
    A(): arr{} {}
    A(const std::array<int, N>& arr): arr{arr} {}

    virtual ~A() = default; // needs virtual destructor

    void Display() const // doesn't need to be virtual
    {
        for(auto n: arr) // generic code
            std::cout << n << ' ';
        std::cout << '\n';
    }

    int Get(size_t n) const { return arr.at(n); }
    void Set(size_t n, int value) { arr.at(n) = value; }

protected:
    std::array<int, N> arr;
};

class B: public A<4>
{
public:
    B(const std::array<int, 4>& numbers): A(numbers) {}
};

class C: public A<8>
{
public:
    C(const std::array<int, 8>& numbers): A(numbers) {}
};

int main()
{
    std::array<int, 4> B_numbers = {1, 2, 3, 4};

    B b(B_numbers);
    C c({5, 6, 7, 8, 9, 10, 11, 12});

    b.Display();
    c.Display();
}

输出:

1 2 3 4 
5 6 7 8 9 10 11 12 

这种方法的好处是,通过在基础 class 中编写泛型函数(仅依赖于 std::array class 的属性,如 arr.size()迭代器) 您可以节省创建类似 classes 的时间,它们只是大小不同。

您可以通过多种方式解决此问题。

当您在基 class 中声明 array[] 受保护时,这意味着所有派生的 class 都可以访问它(因此您不必重新定义它) .

如果您想确保您的数据始终符合预期,您可以删除额外的 array 定义,因为它们会产生歧义(您有基础 class 和派生 class 同名成员),并将数组的预期大小声明为受保护变量,然后您可以在每个派生的 class' 构造函数中分配它。

另一种编码方法是使用向量,如下所示:

#include <iostream>
#include <vector>

using namespace std;

class A {
    public: 
        A() {};
        virtual void Display() = 0;
    protected:
        vector<int> array;
        int size;
};

class B : public A {
    public:
        B(int *numbers);
        virtual void Display();
};

B::B(int *numbers) {
    size=4;
    for(int i=0; i<size; i++)
        array.push_back(numbers[i]);
}

void B::Display() {
    cout << " B numbers are: ";
    for(int i = 0; i < size; i++)
        cout << array[i] << " ";
    cout << endl;
}

class C : public A {
    public: 
        C(int *numbers);
        virtual void Display();
};

C::C(int *numbers) {
    size=8;
    for(int i=0; i<size; i++)
        array.push_back(numbers[i]);
}

void C::Display() {
    cout << " C numbers are: ";
    for(int i = 0; i < size; i++)
        cout << array[i] << " ";
    cout << endl;
}

int main() {
    int B_numbers[] = {1,2,3,4};
    int C_numbers[] = {5,6,7,8,9,10,11,12};
    B b(B_numbers);
    C c(C_numbers);
    b.Display();
    c.Display();
    return 0;
}

这样你只需要在每个 class(构造函数)的一个地方声明大小,并且只有一个可能的容器可以将你的数据保存在你的对象中。您也不必担心内存管理。

如果您想更进一步,可以在任何地方使用向量(和迭代器),这将确保您的输入大小始终与存储大小相匹配。