繁体   English   中英

如何声明一个类没有默认构造函数的对象数组?

[英]How do I declare an array of objects whose class has no default constructor?

如果一个类只有一个带一个参数的构造函数,如何声明一个数组? 我知道在这种情况下推荐使用 vector 。 例如,如果我有一堂课

class Foo{

public:
Foo(int i) {}

}

如何声明一个包含 10000 个 Foo 对象的数组或向量?

对于数组,您必须在定义数组的位置为数组的每个元素提供一个初始值设定项。

对于向量,您可以为向量的每个成员提供一个要复制的实例。

例如

std::vector<Foo> thousand_foos(1000, Foo(42));

实际上,只要您使用初始化列表,您就可以做到,例如

Foo foos[4] = { Foo(0),Foo(1),Foo(2),Foo(3) };

但是对于 10000 个对象,这绝对是不切实际的。 我什至不确定您是否疯狂到尝试编译器是否会接受这么大的初始化列表。

sbi 对普通数组有最好的答案,但没有给出例子。 所以...

您应该使用新的展示位置:

char *place = new char [sizeof(Foo) * 10000];
Foo *fooArray = reinterpret_cast<Foo *>(place);
for (unsigned int i = 0; i < 10000; ++i) {
    new (fooArray + i) Foo(i); // Call non-default constructor
}

请记住,在使用placement new 时,有责任调用对象的析构函数——编译器不会为您执行此操作:

// In some cleanup code somewhere ...
for (unsigned int i = 0; i < 10000; ++i) {
    fooArray[i].~Foo();
}

// Don't forget to delete the "place"
delete [] reinterpret_cast<char *>(fooArray);

这是您唯一一次看到对析构函数的合法显式调用。

注意:第一个版本在删除“地点”时有一个微妙的错误。 将“地点”转换回新的相同类型很重要。 换句话说, fooArray在删除时必须转换回char * 请参阅下面的评论以获取解释。

你需要做一个指向 Foo 的指针数组。

Foo* myArray[10000];
for (int i = 0; i < 10000; ++i)
    myArray[i] = new Foo(i);

当您声明一个没有默认构造函数的对象时,您必须在声明中对其进行初始化。

Foo a; // not allowed
Foo b(0); // OK

此类类型的数组也是如此:

Foo c[2]; // not allowed
Foo d[2] = { 0, 1 }; // OK
Foo e[] = { Foo(0), Foo(1), Foo(2) }; // also OK

在您的情况下,您可能会发现像这样初始化所有 10,000 个元素是不切实际的,因此您可能希望重新考虑该类是否真的不应该具有默认构造函数。

您必须使用聚合初始值设定项,在{}之间有 10000 个单独的初始值设定项

Foo array[10000] = { 1, 2, 3, ..., 10000 };

当然,指定 10000 个初始值设定项是不可能的,但您自己要求。 你想声明一个没有默认构造函数的 10000 个对象的数组。

定义一个没有默认构造函数的类的数组的唯一方法是立即对其进行初始化 - 不是一个真正的具有 10000 个对象的选项。

但是,您可以以任何您想要的方式分配足够的原始内存,并使用placement new在该内存中创建对象。 但是如果你想这样做,最好使用std::vector来做到这一点:

#include <iostream>
#include <vector>

struct foo {
    foo(int) {}
};

int main()
{
    std::vector<foo> v;
    v.resize(10000,foo(42));
    std::cout << v.size() '\n';
    return 0;
}
class single
{
    int data;
public:
    single()
    {
        data = 0;
    }
    single(int i)
    {
        data = i;
    }
};

// in main()
single* obj[10000];
for (unsigned int z = 0; z < 10000; z++) 
{
    obj[z] = new single(10);
}

另一种选择可能是使用一组boost::optional<Foo>

boost::optional<Foo> foos[10]; // No construction takes place
                               // (similar to vector::reserve)

foos[i] = Foo(3); // Actual construction

一个警告是您必须使用指针语法访问元素:

bar(*foos[2]); // "bar" is a function taking a "Foo"

std::cout << foos[3]->baz(); // "baz" is a member of "Foo"

您还必须小心不要访问未初始化的元素。

另一个警告是,这不是Foo数组的真正替代品,因为您将无法将其传递给需要后者的函数。

在直接 C 中,使用int foo[10000] = {1}; 将第一个数组项初始化为1 ,并将数组的其余部分初始化为零。 C++ 不自动初始化未指定的数组成员,还是需要默认构造函数?

如果它对您的类有意义,您可以为构造函数参数提供一个默认值:

class Foo
{ 
public: 
  explicit Foo(int i = 0); 
}

现在你有了一个默认的构造函数。 (“默认构造函数”是可以不带参数调用的构造函数: FAQ

我还建议让您的构造函数显式,就像我上面所做的那样。 当您不想请求它们时,它会阻止您从int获取Foo

尝试这个。

 Foo **ppInstances=0; size_t total_instances = 10000; for(int parent=0;parent < total_instances;parent++){ ppInstances[parent]=new Foo( parent ); ppInstances++; } for(int parent=0;parent < total_instances;parent++){ delete *ppInstances; ppInstances--; }

正确的方法是使用std::aligned_storage 当您想要访问一个项目时,您将不得不手动构建和销毁项目以及reintrepret_cast 我建议您围绕storage_t编写一个小的包装类来处理这个问题。 有人提到使用boost::optional ,它在引擎盖下使用 bool 和 storage_t 。 这种方法为您节省了一个布尔值。

template<typename T>
using storage_t = typename std::aligned_storage<sizeof(T), alignof(T)>::type;

struct Foo;

size_t i = 55;
storage_t<Foo> items[1000]; // array of suitable storage for 1000 T's
new (reintrepret_cast<Foo*>(items + i)) Foo(42); // construct new Foo using placement new
*reintrepret_cast<Foo*>(items + i) = Foo(27);    // assign Foo
reintrepret_cast<Foo*>(items + i)->~Foo()        // call destructor

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM