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

Posted

技术标签:

【中文标题】在类定义中使用内置数组,但大小推迟到派生类会导致隐藏吗?【英文标题】:Would using a built in arrays within a class definition, but with size deferred to derived class cause hiding? 【发布时间】:2016-04-27 18:48:09 【问题描述】:

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

#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 类中重新声明数组来隐藏数据。

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();

感谢您提供的任何建议。

【问题讨论】:

您不能覆盖成员数据(您正在创建独立数组)。 int array[]; 是零大小的数组。是时候研究模板了。 您的第一个问题是 C++ 中不允许使用零长度数组,因此 GCC 扩展认为这不是可移植的。 考虑在基类virtual int* Array()中添加一个虚函数,派生类实现返回指向其数组的指针。 我一直在研究使用 std:;array 或 std::vector ,到目前为止更喜欢前者,因为大小在编译时是固定的。使用 std::array 是否仍然允许我将数组的大小推迟到派生类? @Cam2025 你不能推迟类的大小直到一些后续定义,所以不可能推迟成员的大小或虚拟化。 【参考方案1】:

您显示的任何代码中都没有使用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;

【讨论】:

【参考方案2】:

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

C++ 不会对数据成员执行此操作。派生类的数据成员与基成员不同。如果您尝试访问该数组,您选择哪一个将基于指向对象的变量的类型,而不是对象本身的类型。而这不是一件好事

C 可以很容易地射中自己的脚; C++ 让它变得更难,但是当你这样做时,它会把你的整条腿都吹掉。 —Bjarne Stroustrup 的常见问题解答

【讨论】:

一个明确的问题,因为层次结构在完成后将有 3 个级别,基类,数组大小已知的中间类,然后是具有特定配置调整的“叶”类到各种虚函数。【参考方案3】:

有很多方法可以解决这个问题。

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

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

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

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

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

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

【讨论】:

【参考方案4】:

您可能会受益于使用模板化方法在基类中提供一个数组,派生类可以使用该数组并在编译时定义其大小。

类似这样的:

#include <array>
#include <iostream>

template<size_t N>
class A

public:
    A(): arr 
    A(const std::array<int, N>& arr): arrarr 

    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 

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

【讨论】:

以上是关于在类定义中使用内置数组,但大小推迟到派生类会导致隐藏吗?的主要内容,如果未能解决你的问题,请参考以下文章

C++在类中定义vector并初始大小的问题

将默认构造函数添加到基类会更改sizeof()派生类型[duplicate]

自定义组件:将事件推迟到 FormCreate 之后

面向对象访问修饰符

c ++:使用模板在类中定义可变长度数组

重置设计 rails 的密码会导致“没有将数组隐式转换为字符串”错误