【问题标题】:Declaring an array inside a class, and setting its size with the constructor在类中声明一个数组,并用构造函数设置它的大小
【发布时间】:2023-03-24 03:58:01
【问题描述】:

我已经有一段时间没有使用 c++ 了,但我刚刚开始使用它进行项目。这可能是不可能的,但我试图用一个数组创建一个模板类,该数组将其大小设置为我试图用构造函数设置的常量的值。

这是构造函数的代码:

Tarray(int s): start_size(s){
    }

这是设置数组大小的代码:

const int start_size;
T this_array[start_size];

这是整个文件:

#ifndef TARRAY_H_
#define TARRAY_H_


template<typename T>
class Tarray {
private:
    const int start_size;
    T this_array[start_size];
    int array_size;
public:
    Tarray(int s): start_size(s){
    }
    ~Tarray(){
        delete[] this_array;
    }
    T & operator[](int i){
        return this_array[i];
    }
};



#endif /* TARRAY_H_ */

这些是我得到的错误:

..\/template_array/Tarray.h:16:24: error: 'Tarray<T>::start_size' cannot appear in a constant-expression
..\/template_array/Tarray.h:16:34: error: 'new' cannot appear in a constant-expression
..\/template_array/Tarray.h:16:34: error: ISO C++ forbids initialization of member 'this_array' [-fpermissive]
..\/template_array/Tarray.h:16:34: error: making 'this_array' static [-fpermissive]
..\/template_array/Tarray.h: In instantiation of 'Tarray<Person>':
..\Human.cpp:17:24:   instantiated from here
..\/template_array/Tarray.h:16:34: error: invalid in-class initialization of static data member of non-integral type 'Person*'
Build error occurred, build is stopped
Time consumed: 343  ms. 

当我尝试调整代码时,错误消息一直在发生变化,但这些是来自此特定构建的错误。

感谢您的帮助

【问题讨论】:

  • 谢谢,但我仍然想知道你是怎么做到的。好久没用过c++了,想重新学习一下。
  • 如果允许这样的构造,sizeof 将如何工作?
  • C++ 不支持这种方式的可变长度数组。 C99 有,但 C++ 没有(甚至 C++11 也没有)。 GNU 在 C++ 中支持它们作为扩展,但对于自动变量,而不是类成员(据我所知)。您需要显式使用new/malloc,或使用vector 并让该类为您管理动态分配(几乎在所有情况下都是更好的方法)。
  • 如果该值仅在运行时已知,则必须使用 new 动态分配数组。如果值在编译时已知,则可以是模板参数,模板参数可用于数组大小。
  • 我认为 C99 也不支持它作为结构的一部分,因此这些限制同样适用于 C99 和 C++-with-gnu-extensions。

标签: c++ arrays class constructor constants


【解决方案1】:

您收到编译器错误的原因是这一行:

T this_array[start_size];

这一行将使您的Tarray 实际上包含start_sizeT 实例。它不会保存对这些实例的指针或引用——它们将是包含 Tarray 的其他实例变量的同一内存块的一部分。 这将使类的大小取决于 start_size,而 start_size 在编译时是未知的。任何 C++ 类的大小必须在编译时就知道,这是不可能的。

有两种方法可以解决这个问题:

  1. 使用array new 在堆上分配T 实例数组。这就是std::vector 所做的。编写这样一个类并在它被复制/移动/扩展/等时让它表现正确是困难和乏味的,所以我建议只使用std::vector
  2. 固定T实例的数量,作为模板参数传递

即:

template<typename T, std::size_t N>
class TArray
{
    ...
    T this_array[N];
    ...
}

这就是 std::array(仅限 C++11)和 boost::array 所做的。同样,我建议使用其中之一,而不是自己编写。除非这是家庭作业,当然……

最后,值得注意的是这是一个错误:

~Tarray(){
    delete[] this_array;
}

this_array 没有分配给new,所以你不应该delete 它。如果数组是这里的类的一部分(而不是单独堆分配并由类拥有),那么默认情况下它将与类的其余部分一起被销毁。调用delete 不仅没有必要,而且几乎肯定会导致崩溃。

【讨论】:

    【解决方案2】:

    std::vector 正是这项工作的工具:

    template<typename T>
    class Tarray {
    private:
        std::vector<T> this_array;
    public:
        Tarray(int s): this_array(s){
        }
        ~Tarray(){
        }
        T & operator[](int i){
            return this_array[i];
        }
    };
    

    【讨论】:

    • this_array.reserve(s) 会更好。
    • @iammilind 不同意,语义将完全不同,因为如果仅调用 reserve 则不会创建元素。
    • @DavidRodríguez-dribeas,我知道,它只是保留。这就是目的。使用vector::reserve() 可以根据需要初始化元素。如果超出s 的大小,将只使用一半,那么创建整个数组就没有意义了。
    【解决方案3】:

    以下代码做了类似的事情,但不使用构造函数:

    #ifndef TARRAY_H_ 
    #define TARRAY_H_ 
    
    
    template<int SizeT> 
    class Tarray { 
    private: 
        T this_array[SizeT]; 
    public: 
        Tarray() {} 
        ~Tarray() {} 
        T & operator[](int i){ 
            return this_array[i]; 
        } 
    }; 
    
    #endif /* TARRAY_H_ */ 
    

    你可以这样使用它:

    TArray<10> myArray;
    

    【讨论】:

      【解决方案4】:

      您必须在运行时创建数组。

      template<typename T>
      class Tarray {
      private:
          const int start_size;
          T* this_array;
          int array_size;
      
          Tarray( const Tarrat& inObj ); // no copy
      
      public:
          Tarray(int s): start_size(s), this_array( new T[s] ) {
          }
          ~Tarray(){
              delete[] this_array;
          }
          T & operator[](int i){
              return this_array[i];
          }
      };
      

      注意,要使其工作,T 必须有一个默认构造函数(即,一个不带参数的构造函数)。

      【讨论】:

      • 这段代码会很脆弱——如果不小心复制了会怎样?
      • 问题是如何在模板中创建向量,我回答了这个问题。但是,不应发生意外复制,因为已声明构造函数(这会阻止编译器自动创建构造函数),但未声明复制构造函数。结果,如果发生任何隐式副本,编译器会抱怨。避免了脆弱性。我会很感激 -1 被带走,谢谢。
      • 编译器会自动为你生成一个拷贝构造函数。如果您想不指定复制构造函数,则需要将其作为显式私有构造函数提供,但随后无法提供实现。此外,为了让我删除 -1,需要更新答案。
      • 我的错,我对默认构造函数感到困惑。已更新。
      【解决方案5】:

      改用 std::vector,让自己的生活变得简单。 :)

      (如果你想要一个固定大小的数组,那么 std::array 可能是一种可能性,我认为这是在 C++11 中,如果没有,那么 boost 可能有一个实现)。

      如果你坚持使用普通的数组语法,就像你使用 ye-olde C 一样,那么你将需要使用模板参数,这样你的模板类就有两个参数 - 一个用于 'T'它现在已经有了,另一个用于数组大小。

      你自己管理那个数组会让你的生活变得特别困难——如果你觉得你必须定义一个析构函数,那么除了构造函数之外,你真的应该定义复制构造函数。 (如果我没记错的话,这就是所谓的三巨头规则),而是依赖 RAII,避免自己显式调用 operator delete 或 delete[]。

      【讨论】:

      • 谢谢,但我仍然收到类似“'Tarray::start_size' 不能出现在常量表达式中”之类的错误
      • @Mike G:如果您使用的是 std::vector,那么保持 ::start_size 是没有意义的。也就是说,您已经在那里指定了 'const' 而没有给它一个值。 (而且你不能在这样的课堂上这样做)
      猜你喜欢
      • 2017-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-28
      • 2011-10-12
      • 1970-01-01
      • 2021-05-30
      相关资源
      最近更新 更多