【问题标题】:Is there a way to specify size of array in base class in inherited class?有没有办法在继承类的基类中指定数组的大小?
【发布时间】:2013-10-26 20:05:29
【问题描述】:

我有一个抽象基类,它包含一个数组以及两个或多个希望在基类中有一个稍大的数组的继承类。

我尝试通过使用模板来解决这个问题:

template <int arraySize>
class Baseclass {
public:
    uint16_t arr[arraySize];
};

class InheritedClass :
    public Baseclass <5> {};

我现在面临的问题是: 每当我使用指向某个基类对象的指针时,编译器都会抱怨:

类模板“Baseclass”的参数列表丢失

我想我明白这里发生了什么:没有模板参数的基类现在不是一个完整的类型 - 但是编译器需要一个。

因此我想知道 - 是否有一种(更好的)方法来实现我想要做的事情,而不是在 InheritedClass 中分配数组并将指针向下传递给 Baseclass?

提前谢谢你!

【问题讨论】:

  • 使用向量而不是数组。
  • 您的代码可以工作,但听起来您尝试使用指向模板的指针 (Baseclass*) 而不是指向基类的指针。这当然行不通:模板不是类型
  • @Konrad Rudolph:尝试使用Baseclass * 可能源于希望在层次结构中有一个公共基类。这可能是实际需要的。
  • @user2923748:为了理解“你想要做什么”,我们需要知道你是否关心在你的层次结构中有一个公共基类。你呢?
  • 谢谢,我通过定义一个带有虚函数的非泛型基类解决了这个问题。

标签: c++ templates inheritance


【解决方案1】:

如果数组直接嵌入到基类中,如您的示例所示,则无法指定该数组的大小。更准确地说,更改该数组的大小将需要为每个特定数组大小生成该基类的完全不同版本。这将破坏目的,因为您的意图显然是在层次结构中拥有一个公共基类。是吗?

这正是您在使用模板“解决方案”时遇到的问题。您的程序不再有共同的Baseclass。相反,您有Baseclass&lt;5&gt;Baseclass&lt;10&gt; 等等——所有完全不同的独立基类。显然,这不是您所需要的。

实现包含运行时大小数组的公共基类的唯一方法是将数组间接存储在基类中。 IE。在基类中声明一个uint16_t * 指针并在运行时分配适当的内存量。或者您可以简单地使用 std::vector&lt;uint16_t&gt; 代替原始数组。

请注意,如果您决定采用指针方式,则并非绝对需要动态分配数组内存。您可以简单地使实际的“数组内存”成为已经“知道”特定大小的派生类的成员

class Baseclass {
public:
  uint16_t *arr;
  size_t arraySize;

  Baseclass(uint16_t *arr, size_t arraySize) : arr(arr), arraySize(arraySize)
    {}
};

class InheritedClass : public Baseclass
{
  InheritedClass() : Baseclass(arr_memory, 5)
    {}
private:
  uint16_t arr_memory[5];
};

因此,底线是,如果您希望将数组内存管理完全封装到 Baseclass 中,那么您别无选择,只能在运行时动态分配内存。如果这不可接受,那么您只能在其他地方执行内存管理并将其从外部传递给Baseclass

【讨论】:

  • 问题作者提到他们的要求限制了在基类中为数组使用动态内存!
  • 当然,改变大小会生成一个新类型——这就是我的目标。我现在所做的是使用访问动态数组的虚拟方法指定一个抽象基类。这样我就可以使用基本类型,而在运行时为给定类型选择正确的编译时实现。我忘了说:指针也不好,因为类是序列化的(从一个地方复制到另一个地方),而指针会破坏这一点。非常感谢您的详细回答:)
  • @user2923748:使用抽象基类购买,您只需将实现从“数据指针”转移到“代码指针”。后者不是明确可见的,但它们就在那里。最后,两者都会让您处理相当相似的序列化问题。
【解决方案2】:

您可以让Baseclass 派生自非模板类,并改用指向该类的指针。

【讨论】:

    【解决方案3】:

    作为一种可能会起作用的技巧,在baseclass 中包含一个方法,将reinterpret_casts this 放入baseclass&lt;1&gt;

    这取决于您的 ty0es 被编译成相对健全的布局:为了鼓励这种情况发生,请确保 baseclass 至少是 pod 或标准布局。

    虽然结果是未定义的行为,但在我使用的每个编译器中它都可以工作。

    缺点?您的数组有多大必须并行传输,并且您在运行时数组的大小上向您的类的其他用户撒谎。另外它非常脆弱:数组必须是baseclass的最后一个元素。

    您最好取消编译时静态界限,并存储带有运行时界限的std::vector,因为这将避免未定义的行为黑客并减少每行代码的谎言。

    【讨论】:

      【解决方案4】:
      class Baseclass {
      public:
          uint16_t *arr;
          Baseclass(int arrlen) {
              arr = new int[arrlen];
          }
      
          ~Baseclass() {
              delete arr;
          }
      
      };
      
      class InheritedClass :
          public Baseclass {
      public:
          Inherited() : Baseclass(5){
          }
      };
      

      如果可以,您可以简单地使用动态分配。

      【讨论】:

      • 我不能使用动态分配,所以我也不能使用 std::vector。
      【解决方案5】:

      如果显式指定参数,C++ 会忽略默认表达式。因此,在您的派生类中,您明确指定 CRTP'd 基的需求,但在基中,您还提供一个默认值计算,以便在实例化后(仅)使用。 所以:

      #include <iostream>
      // instantiation specifies size explicitly, default calculation ignored
      // after instantiation names in default calculation are bound correctly
      
      template<class d,int size=sizeof d::m2/sizeof *d::m2>
      struct b {
              int m[size];
      };
      
      struct d: b<d,20> {
              double m2[20];
      };
      
      int main() {
              std::cout<<sizeof b<d>::m/sizeof *b<d>::m<<'\n';
      }
      

      【讨论】:

        猜你喜欢
        • 2021-07-24
        • 2011-01-14
        • 1970-01-01
        • 2019-09-10
        • 1970-01-01
        • 2010-09-28
        • 1970-01-01
        • 2013-03-24
        • 1970-01-01
        相关资源
        最近更新 更多