繁体   English   中英

为什么用户定义类型的数组必须调用其默认构造函数?

[英]Why does an array of user defined type have to call the default constructor thereof?

请考虑以下示例

struct Foo
{
    int bar;    
    Foo(int i):bar(i){cout << "real ctor\n";}   
    Foo(){cout << "default ctor\n";}
};

int main()
{   
    Foo fooArr[3];//default ctor called 3 times 
    for(int i=0;i!=3;++i)cout << fooArr[i].bar << endl;//bare memory junk
    cout << endl;

    vector<Foo> fooVec;
    for(int i=0;i!=3;++i){
        fooVec.push_back(Foo(i));     //only real ctor called
        cout << fooVec[i].bar << endl;//real thing 
    }
    cout << endl;

    int iArr[3];
    for(int i=0;i!=3;++i)cout << iArr[i] << endl;//bare memory junk
}

我不希望Foo任何用户调用其默认构造函数,因为它不在我的设计中。 但是我希望用户能够使用Foo数组来支持这一点,因此我不得不提供一个毫无意义且令人困惑的Foo :: Foo()。 我只是不明白为什么C ++标准会迫使程序员去做这样的事情。 其背后的原理是什么? 为什么不一致? 你们中任何一个懂这个的聪明人都可以向我解释一下吗? 提前致谢!

您可以创建Foo数组,即使它没有默认构造函数也是如此。 这只是元素具有在声明数组构造。 因此,您可以执行以下操作:

Foo fooArr[] = { Foo( 1 ), Foo( 2 ), Foo( 3 ) };

另一种选择是使用一个动态数组(您的vector<Foo>示例,可能是最好的)或一个指向Foo的指针数组(例如shared_ptr<Foo> arrFoo[3] )。

shared_ptr<Foo> arrFoo[3];
arrFoo[2].reset( new Foo(3) );

关于vector<Foo>最后一点说明:由于数组的大小是事先已知的,因此可以通过为所有将来的Foo保留向量中的足够空间来提高性能:

vector<Foo> arrFoo;
arrFoo.reserve( 3 );

for( int i = 0; i<3; ++i )
    arrFoo.push_back( Foo( i ) );

编辑:您的问题是为什么您必须具有默认构造函数才能创建类型的静态数组。 我以为答案很明确,但我会尽力解释。

Foo bar1; Foo bar2; 因为没有提供参数,所以使用默认构造函数创建两个对象。

Foo bar[2]; 本质上是同一件事。 它声明了两个需要构造的对象。 没有构造它就无法声明一个对象-这就是首先声明它的目的。

C ++中的静态数组只是一堆连续放置在内存中的对象。 它不是一个单独的对象。

希望有道理。

理由是数组中充满了默认构造的元素,因此元素的类型必须是默认可构造的。 如果使用一些值初始化数组,则不需要默认构造:

Foo fooArr1[3]; // full of default constructed Foos
Foo fooArr[3] = {1,2,3}; // default constructor not required. Foo(int) called.

请注意,代码示例的第二行使用隐式Foo(int)转换构造函数提供的从intFoo的隐式转换。

您必须提供自己的默认构造函数的原因是,您已经声明了一个构造函数,这将禁用默认构造函数的自动生成。 其基本原理是,如果您需要提供一些构造函数,则可能还需要在默认构造函数中做一些特殊的事情。

如果您真的担心用户提供的构造函数,那么可以使您的类成为真正的聚合并使用聚合初始化:

struct Foo
{
  // no user declared constructors
  int foo;
};

int main()
{   
    Foo fooArr1[3]; // OK
    Foo fooArr[3] = { {1}, {2}, {3} }; // aggregate construction 
}

在C ++ 11中,您可以使用default启用编译器生成的默认构造函数:

Foo()=default;

您必须选择:不定义默认构造函数,因此,您不能声明Foo数组。 或者声明一个默认的构造函数(空偶),并且可以声明一个Foo数组。

如果您之前使用过C#或Java之class Foo OOP语言,并且拥有class FooFoo[] arr ,则不必声明默认构造函数,因为这些语言中的数组仅携带对以下内容的引用(地址):对象。 数组本身是一个对象,因此创建时arr将== null 当使用arr = new Foo[3]; 然后我们创建一个包含3个引用的数组新对象: arr == { null, null, null } 然后为每个引用分配一个对象: for (int i = 0; i < 3; ++i) arr[i] = new Foo(i);

但是,C ++有所不同,因为数组携带对象本身而不是对象的引用。 因此,在携带对象本身时,它必须具有无参数构造函数才能与每个对象一起调用。 (即在C ++中: Foo arr[3];然后arr = { objectOfFoo, objectOfFoo, objectOfFoo }

通过对指针数组进行除垢可以找到解决问题的方法:

Foo * arr[10] = { 0 }; // arr = { NULL, NULL, NULL, ... , NULL }
for (int i = 0; i < 10; ++i) arr[i] = new Foo(3); // you don't have to declare default constructor

// some using of array
// C++ doesn't have a garbage collector
for (int i = 0; i < 10; ++i) delete arr[i];

暂无
暂无

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

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