【问题标题】:Array of objects without default constructors on the stack?堆栈上没有默认构造函数的对象数组?
【发布时间】:2021-03-02 18:10:16
【问题描述】:

我可以使用选项 3 实现我需要的功能,但是我想调查是否可以在堆栈上创建数组。

#include <array>
#include <vector>

struct NotDefaultConstructable
{
    NotDefaultConstructable(int val){};
};

int main()
{
    //std::array<NotDefaultConstructable, 5> aA;    // Fails to compile. [On stack]

    //std::vector<NotDefaultConstructable> aV(5); // Fails to compile. [On heap]

    std::vector<NotDefaultConstructable> aV; // Compiles. [ On heap]
    aV.reserve(5);
}

【问题讨论】:

  • 它可以编译,因为aV 是一个 empty 向量。 aV.reserve(5) 不会使其成为 5 的向量 NotDefaultConstructables
  • 选项 3 正在创建一个空的“数组”。它不包含任何对象。所以不清楚你到底在追求什么。
  • @Harry NotDefaultConstructable aV[5] 也不起作用:godbolt.org/z/8MvxTo
  • 您的选项 1,2 和 3 都非常不同,因此您需要说明您想要实现的功能。仅从代码中尚不清楚。 (选项 3 不创建任何 NotDefaultConstructable 对象顺便说一句)
  • @AidanGallagher 我认为你的对象的生命周期管理会像那样糟糕; godbolt.org/z/54q9o1 - 这是一种做你想做的事的方式,但你需要自己显式地构造和销毁对象

标签: c++ arrays constructor


【解决方案1】:

从 OP 提到研究使用放置 new 的事实来看,我想制作一个完整的示例,说明使用堆栈分配的数组作为动态构造对象的内存。

使用此方法,您可以分配堆栈内存,而无需使用实例对其进行初始化。然而,问题是您必须自己监控每个实例的对象生命周期。在这个例子中,我构建了所有这些,然后可以简单地假设它们都必须被销毁,但是监控有多少实例是活着的并且应该被销毁是相当麻烦的。

这就是为什么dfrib 的答案更适合您的情况,因为它允许您使用std::array&lt;std::optional&lt;Type&gt;, 5&gt; 分配堆栈上的内存并稍后分配实例。对象的生命周期也将为您管理,因此更为可取。

示例 1:新展示位置:

#include <array>
#include <iostream>

struct NotDefaultConstructable {
    int Value;

    NotDefaultConstructable(int val) : Value(val) {
        std::cout << "constructed: " << Value << "\n";
    };

    ~NotDefaultConstructable(){
        std::cout << "destructed: " << Value << "\n";
    }
};

int main() {
    // allocate enough memory on the stack for 5 instances 
    char aV[sizeof(NotDefaultConstructable) * 5];
    
    // get a pointer to the first NotDefaultConstructable in that array 
    auto avArray = static_cast<NotDefaultConstructable*>(static_cast<void*>(&aV[0]));

    // use placement new to construct each instance 
    for (auto i = 0; i < 5; ++i) 
        new (&avArray[i]) NotDefaultConstructable((i + 1) * 2);

    // do stuff with the instances 
    for (auto i = 0; i < 5; ++i) 
        std::cout << "instance: " << avArray[i].Value << "\n";

    // destruct them all manually, this is what makes placement new a little 
    // cumbersome. I would advise to use std::optional instead.
    for (auto i = 0; i < 5; ++i) 
        avArray[i].~NotDefaultConstructable();
}

示例 1:https://godbolt.org/z/7jW8Pb


示例 2:std::array 和 std::optional

这是一个使用 std::optional 的示例,它具有较小的开销(每个对象大约 4 个字节)以实现更多便利:

#include <array>
#include <optional>
#include <iostream>

struct NotDefaultConstructable {
    int Value;

    NotDefaultConstructable(int val) : Value(val) {
        std::cout << "constructed: " << Value << "\n";
    };

    ~NotDefaultConstructable(){
        std::cout << "destructed: " << Value << "\n";
    }
};

int main() {
    // allocate enough memory on the stack for 5 instances 
    std::array<std::optional<NotDefaultConstructable>, 5> avArray;

    // use placement new to construct each instance 
    for (auto i = 0; i < 5; ++i) 
        avArray[i] = NotDefaultConstructable((i + 1) * 2);

    // do stuff with the instances 
    for (auto i = 0; i < 5; ++i) 
        std::cout << "instance: " << avArray[i].value().Value << "\n";
}

示例 2:https://godbolt.org/z/Ynx8aE

【讨论】:

    【解决方案2】:

    std::array 是一个聚合类类型,可能有一个微不足道的、隐式默认的构造函数。如果T 不是默认构造函数,则隐式默认构造函数被定义为已删除,根据[class.ctor]/5.3。

    由于这适用于您的情况,默认情况下您不能构造 std::array&lt;NotDefaultConstructable, 5&gt; 的对象。但是,您可以通过聚合初始化来构造它:

    #include <array>
    
    struct NotDefaultConstructable {
        NotDefaultConstructable(int){};
    };
    
    int main() {
        std::array<NotDefaultConstructable, 3> arr{1, 2, 3};
    }
    

    从这个意义上说,std::array 对象的所有元素都应该被初始化,即使它们表示一个不存在的对象(如果你愿意,还需要“填充”)。

    您可以找到一个合适的静态矢量容器,例如boost::static_vector,或者您可以例如在std::array 周围实现一个瘦包装器,它存储一个如上所述的数组以及它的运行时大小。另一种选择是使用 std::array 的选项:

    #include <array>
    #include <iostream>
    #include <optional>
    
    struct NotDefaultConstructable {
        NotDefaultConstructable(int val) : val_(val) {};
        int val_;
    };
    
    int main() {
        std::array<std::optional<NotDefaultConstructable>, 3> arr{};
        arr[1] = NotDefaultConstructable{42};
        for(const auto& element : arr) {
            if(element.has_value()) {
                std::cout << "has value: " << element.value().val_;
            }  // has value: 42
        }
    }
    

    没有发生动态内存分配的地方 (cppreference):

    如果一个可选项包含一个值,则该值保证作为可选对象占用空间的一部分进行分配,即不会发生动态内存分配。

    【讨论】:

      【解决方案3】:

      你可以试试下面的

      #include <iostream>
      
      
      struct NotDefaultConstructable {
          NotDefaultConstructable(int val) {
              
          };
      };
      
      int main() {
          NotDefaultConstructable aV[5] { 6, 6, 6, 6, 6 };
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-01-22
        • 2011-06-12
        • 2019-11-22
        • 1970-01-01
        • 2023-03-20
        • 2016-07-18
        • 1970-01-01
        相关资源
        最近更新 更多