创建一个类的实例

134

第1、2、3、4行的区别是什么?

我应该在什么时候使用它们?

为什么第3行输出constructor Foo,第7行返回一个错误,而第8行不会?

#include <iostream>     
using namespace std;

class Foo
 {
   public:
   Foo ( )
   {
      cout << "constructor Foo\n";
   }               
};

class Bar
 {
   public:
   Bar ( Foo )
   {
      cout << "constructor Bar\n";
   }
};

int main()
{
   /* 1 */ Foo* foo1 = new Foo ();
   /* 2 */ Foo* foo2 = new Foo;
   /* 3 */ Foo foo3;
   /* 4 */ Foo foo4 = Foo::Foo();

   /* 5 */ Bar* bar1 = new Bar ( *new Foo() );
   /* 6 */ Bar* bar2 = new Bar ( *new Foo );
   /* 7 */ Bar* bar3 = new Bar ( Foo foo5 );
   /* 8 */ Bar* bar3 = new Bar ( Foo::Foo() );

   return 1;
}

18
无论你做什么,都不要使用 *new Foo。那会导致内存泄漏。除非你真的非常需要它,否则请避免使用 new - R. Martinho Fernandes
11
这个对我来说看起来像作业。 - Qnan
3
我只有一本关于C++的书,但这个主题在书中没有讨论过。 - Kolyunya
7
你应该意识到你的修改使得原来的两个答案过时了,对吧?而且,它是无效的……第五和第六个提供了一个很好的例子,说明不该怎么做。它们在那里并不是坏事。 - Luchian Grigore
2
你应该在列表中添加 Foo foo5();。 - KRoy
1
你忘记包含 Foo foo5 = Foo();。 - RetroCode
3个回答

160
   /* 1 */ Foo* foo1 = new Foo ();

在动态内存中创建一个类型为Foo的对象。foo1指向该对象。通常情况下,你不应该在C++中使用裸指针,而是使用智能指针。如果Foo是POD类型,这将执行值初始化(但这里不适用)。

   /* 2 */ Foo* foo2 = new Foo;

与之前相同,因为Foo不是POD类型。

   /* 3 */ Foo foo3;

创建一个名为foo3Foo对象,存在于自动存储中。

   /* 4 */ Foo foo4 = Foo::Foo();

使用复制初始化在自动存储中创建名为foo4Foo对象。

   /* 5 */ Bar* bar1 = new Bar ( *new Foo() );

使用Bar的转换构造函数在动态存储中创建一个Bar类型的对象。bar1指向该对象的指针。

   /* 6 */ Bar* bar2 = new Bar ( *new Foo );

与之前相同。

   /* 7 */ Bar* bar3 = new Bar ( Foo foo5 );

这只是语法错误。你不能在那里声明一个变量。

   /* 8 */ Bar* bar3 = new Bar ( Foo::Foo() );

如果在7中未声明bar3,则与5和6相同的原理也会适用于它。

5和6存在内存泄漏问题。

类似new Bar ( Foo::Foo() );的语法并不常见。通常是new Bar ( (Foo()) ); - 额外的括号解决了最棘手的语法分析问题。(已更正)


2
实际上,Foo::Foo() 是否合法?我认为不是。 - Konrad Rudolph
1
最棘手的解析虽然不能发生在new表达式中;) - R. Martinho Fernandes
@KonradRudolph:是的,这是合法的。语言决定类型名称被注入到类型定义中,因此任意数量嵌套的 Foo::Foo::..::Foo() 都将解析为完全相同的东西:Foo()。这是一种简化语言导致允许神秘代码的情况之一。 - David Rodríguez - dribeas
@DavidRodríguez-dribeas,“语言决定类型名称被注入到类型定义中”,您能否详细解释一下这是什么意思?谢谢。 - AturSams
@ArthurWulfWhite:在类定义内部,类本身的名称被添加用于查找目的,就好像它是一个成员一样,有点类似于struct Type { typedef ::Type Type; };(尽管这是非法代码)。这意味着Type::Type等同于Type,因此Type::Type::Type也等同于查找目的中的Type。表达式Foo::Foo()等同于Foo;它不是类内构造函数的名称,而是创建类型为Foo的值初始化临时对象的类型Foo()。不确定这是否更清晰了。 - David Rodríguez - dribeas
显示剩余7条评论

26
  1. 不要,除非你喜欢在代码中添加不必要的修饰。
  2. 当你想创建一个超出当前范围的对象时使用。记得在完成后删除它,并学习如何使用智能指针更方便地控制生命周期。
  3. 当你只需要在当前范围内存在的对象时使用。
  4. 不要,除非你认为第3个选项看起来有些无聊,并想添加一些不必要的修饰。
  5. 不要,因为它会泄漏内存且无法恢复。
  6. 不要,因为它会泄漏内存且无法恢复。
  • 不要这样做,因为它无法编译
  • 当你想从临时的 Foo 创建一个动态的 Bar 时。

    1. 从自由存储区分配一些动态内存,并使用其默认构造函数在该内存中创建一个对象。您从未删除它,因此内存泄漏了。为什么我从未删除它?我可以只需调用“delete foo1”。
    - Kolyunya
    2
    @Kolyunya:是的,你可以这样做,而且应该这样做。但是你在示例代码中没有这样做,我认为值得指出。 - Mike Seymour

    5

    第1,2,3,4行将调用默认构造函数。它们在本质上是不同的,因为1,2是动态创建的对象,而3,4是静态创建的对象。

    在第7行中,你在参数调用中创建一个对象。所以这是一个错误。

    第5和6行都会导致内存泄漏问题。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接