类型是不是需要默认构造函数才能声明它的数组?

Posted

技术标签:

【中文标题】类型是不是需要默认构造函数才能声明它的数组?【英文标题】:Does a type require a default constructor in order to declare an array of it?类型是否需要默认构造函数才能声明它的数组? 【发布时间】:2010-02-09 18:22:35 【问题描述】:

我注意到当你声明一个数组时,必须需要默认构造函数。是对的吗? 有什么例外吗?

例如,

struct Foo
Foo(int i  ) 
;

int main () 
    Foo f[5];
    return 0;

上面的代码无法编译。

【问题讨论】:

【参考方案1】:

其他答案都可以,但为了完整起见:您也可以使用数组初始化语法:

Foo f[5] = 1,2,3,4,5;

如果 Foo 的 ctor 不明确,则此方法有效。如果是的话,你就必须是……明确的:

Foo f[5] = Foo(1), Foo(2), Foo(3), Foo(4), Foo(5);

注意1:这两种情况的区别可能并不明显,因此值得注意:第一种,数组元素是直接从初始化列表中的ints,通过调用Foo(int) ctor。在第二种情况下,初始化列表由Foos 构成,使用explicit Foo(int) ctor 构造,数组元素是从初始化列表中的元素复制构造。因此,在后一种情况下需要 Foo 的复制 ctor。

[1] 感谢 MSalters 的评论。

【讨论】:

第二个例子需要一个可用的复制ctor,特别是可以复制临时的。在创建 auto_ptr<T> s 的数组时仍然会遇到问题。 @MSalters:感谢您的精彩评论。我在答案中添加了注释。【参考方案2】:

这个问题根本与数组无关。

默认初始化类类型的对象时,需要默认构造函数。如果您的类没有默认构造函数,那么您别无选择,只能在创建该类的对象时提供显式初始化程序。就这样。

通过在 Foo 类中声明一个非默认构造函数,您禁用了默认构造函数的隐式生成,因此现在您必须在每次创建 Foo 对象时提供一个初始化器,无论是否是否为数组元素。

此声明

Foo f; // ERROR

不是数组,但出于同样的原因它不会编译。为了让它编译,你必须提供一个显式的初始化器

Foo f(3); // OK

同样的事情发生在数组上,除了在这种情况下你必须使用聚合初始化语法为每个元素提供一个初始化

Foo f[5] =  1, 2, 3, 4, 5 ;

当然,如果您最终处于不允许聚合初始化语法的上下文中(在当前版本的 C++ 标准中),例如构造函数初始化列表或 new-expression,那么您确实被搞砸了。在这种情况下,唯一的出路是提供数组元素类型的默认构造函数(只要您坚持使用内置数组)。

【讨论】:

【参考方案3】:

该代码无法编译,因为编译器当然不知道您要传递给每个元素的构造函数的内容。基本上有两种方法:

    将数组设为向量,并将所需大小加上单个元素传递给它——这为每个元素提供了相同的参数。

    使数组成为指针数组,并使用 for 循环和 new 运算符构造每个元素。当然,缺点是您还必须在以后释放每个元素。

【讨论】:

【参考方案4】:

请参阅 C++ FAQ Lite,10.5 部分

创建数组时,会为数组中的每个元素调用默认构造函数。

“如果你的类没有默认构造函数,当你尝试创建一个数组时你会得到一个编译时错误”

不过,更喜欢使用 std::vector 而不是内置数组。

【讨论】:

【参考方案5】:

也不例外。在可能被视为异常的情况下,编译器声明了默认构造函数。

【讨论】:

【参考方案6】:

请注意,如果您使用 std::vector 而不是数组,则不需要默认构造函数 - 您可以指定要使用的构造函数:

std::vector <Foo> f;                // OK
std::vector <Foo> f( 5, Foo(0) );   // also OK

【讨论】:

【参考方案7】:

没有。

数组,在 C/C++ 中是一块内存。创建一个数组就是保留那个块。创建对象是“1.分配空间 2.在每个块上运行构造函数”因此,如果您有一个没有构造函数的对象,您仍然可以创建一个数组(因为该对象具有大小并且由于内存可以理解“大小”)。

简而言之,它没有任何区别。您将在创建对象以填充数组时运行构造函数,或者如前所述,当您将其分配给某些东西时(依次分配空间、运行构造函数)。

【讨论】:

此外,你怎么能有一个“没有构造函数的对象”?这是没有意义的——有构造函数,而所有类至少有一个构造函数。如果你不声明一个,编译器会。【参考方案8】:

是的,这里需要默认构造函数,因为Foo f[5]; 实际上创建了 5 个Foos。您可以通过将其设为Foo* f[5],然后使用new 创建5 个Foos 来解决此问题。

例如:

Foo* f[5];
for(int i = 0; i < 5; ++i) 
    f[i] = new Foo(i);


// later on...
f[0]->whatever();

【讨论】:

【参考方案9】:

警告:稍微偏离主题

如果你有一个没有默认构造函数的类,你绝对需要一个数组,而且你不想招致动态内存分配的开销,你可以使用boost::optionals的数组:

boost::optional<Foo> foos[4];  // Stack storage allocated but no objects 
                               // constructed (roughly equivalent to what 
                               // you get with vector<T>::reserve)

if(foos[2]) // Check if the third element has been constructed

     foos[2]->bar(); // Access members of Foo with arrow    


foos[1] = Foo(1, "a"); // Constructs the second element

foos[1].reset(); // Destroy second element (storage remains there though)

很遗憾,您将无法将其传递给期望真正的 Foo[] 的函数。

【讨论】:

以上是关于类型是不是需要默认构造函数才能声明它的数组?的主要内容,如果未能解决你的问题,请参考以下文章

string碎碎念

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

动态数组进阶

C# 关于 构造函数问题 关于对象实例化

Java面向对象:成员变量—OOP中的内存管理—构造函数

:构造函数语意学之Default constructor的构造操作