2

请考虑以下示例

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++ 标准强制程序员做这样的事情。其背后的原理是什么?为什么不一致?请问有哪位懂这个的聪明人给我解释一下吗?提前致谢!

4

3 回答 3

4

Foo即使它没有默认构造函数,您也可以创建数组。只是在声明数组时必须构造元素。所以你可以这样做:

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

另一种方法是使用动态数组(您的示例,这可能是最好的)或指向(如)vector<Foo>的指针数组Fooshared_ptr<Foo> arrFoo[3]

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

关于 :的最后一点说明:由于事先知道数组的大小,因此您可以通过在向量中为所有未来svector<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++ 中的静态数组只是一堆连续放置在内存中的对象。它不是一个单独的对象。

希望这是有道理的。

于 2012-10-13T07:30:37.253 回答
3

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

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

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

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

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

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;
于 2012-10-13T07:08:53.700 回答
1

您必须选择:要么不定义默认构造函数,因此,您不能声明Foo. 或者声明一个默认构造函数(甚至为空)并且可以声明一个Foo.

如果您之前使用过 C# 或 Java 等 OOP 语言,并且您有一个class Fooand Foo[] 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];
于 2012-10-13T07:26:31.190 回答