在没有new的情况下用c ++调用构造函数

时间:2010-04-27 16:09:59

标签: c++

我经常看到人们使用

在C ++中创建对象
Thing myThing("asdf");

而不是:

Thing myThing = Thing("asdf");

这似乎有用(使用gcc),至少只要没有涉及模板。我现在的问题是,第一行是否正确,如果是这样,我应该使用它吗?

7 个答案:

答案 0 :(得分:133)

这两条线实际上都是正确的但却做了微妙的不同。

第一行通过调用格式为Thing(const char*)的构造函数在堆栈上创建一个新对象。

第二个更复杂一点。它基本上做了以下

  1. 使用构造函数Thing
  2. 创建Thing(const char*)类型的对象
  3. 使用构造函数Thing
  4. 创建Thing(const Thing&)类型的对象
  5. 在步骤1中创建的对象上调用~Thing()

答案 1 :(得分:30)

我假设第二行你的意思是:

Thing *thing = new Thing("uiae");

这将是创建新动态对象(动态绑定和多态性所必需)并将其地址存储到指针的标准方法。您的代码执行JaredPar描述的内容,即创建两个对象(一个传递const char*,另一个传递const Thing&),然后在第一个对象上调用析构函数(~Thing()const char*一个。)

相比之下,这个:

Thing thing("uiae");

创建一个静态对象,在退出当前范围时会自动销毁。

答案 2 :(得分:21)

编译器可能会将第二种形式优化为第一种形式,但它不必。

#include <iostream>

class A
{
    public:
        A() { std::cerr << "Empty constructor" << std::endl; }
        A(const A&) { std::cerr << "Copy constructor" << std::endl; }
        A(const char* str) { std::cerr << "char constructor: " << str << std::endl; }
        ~A() { std::cerr << "destructor" << std::endl; }
};

void direct()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a(__FUNCTION__);
    static_cast<void>(a); // avoid warnings about unused variables
}

void assignment()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a = A(__FUNCTION__);
    static_cast<void>(a); // avoid warnings about unused variables
}

void prove_copy_constructor_is_called()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a(__FUNCTION__);
    A b = a;
    static_cast<void>(b); // avoid warnings about unused variables
}

int main()
{
    direct();
    assignment();
    prove_copy_constructor_is_called();
    return 0;
}

gcc 4.4的输出:

TEST: direct
char constructor: direct
destructor

TEST: assignment
char constructor: assignment
destructor

TEST: prove_copy_constructor_is_called
char constructor: prove_copy_constructor_is_called
Copy constructor
destructor
destructor

答案 3 :(得分:10)

很简单,两行都在堆栈上创建对象,而不是像'new'那样在堆上创建。第二行实际上涉及对复制构造函数的第二次调用,因此应该避免(它还需要按照注释中的说明进行更正)。你应该尽可能地将堆栈用于小对象,因为它更快,但是如果你的对象要比堆栈框架存活更长时间,那么这显然是错误的选择。

答案 4 :(得分:2)

理想情况下,编译器会优化第二个,但不是必需的。第一种是最好的方法。但是,了解C ++中堆栈和堆之间的区别非常关键,正确的是你必须管理自己的堆内存。

答案 5 :(得分:2)

我玩了一下,当构造函数不带参数时,语法似乎变得很奇怪。让我举个例子:

#include <iostream> 

using namespace std;

class Thing
{
public:
    Thing();
};

Thing::Thing()
{
    cout << "Hi" << endl;
}

int main()
{
    //Thing myThing(); // Does not work
    Thing myThing; // Works

}

所以只需编写Thing myThing w / o括号实际上调用构造函数,而Thing myThing()使编译器想要创建函数指针或其他东西?? !!

答案 6 :(得分:2)

附加 JaredPar 回答

1通常的ctor,具有临时对象的第2功能类ctor。

使用不同的编译器

在此处http://melpon.org/wandbox/编译此源代码
// turn off rvo for clang, gcc with '-fno-elide-constructors'

#include <stdio.h>
class Thing {
public:
    Thing(const char*){puts(__FUNCTION__ );}
    Thing(const Thing&){puts(__FUNCTION__ );}   
    ~Thing(){puts(__FUNCTION__);}
};
int main(int /*argc*/, const char** /*argv*/) {
    Thing myThing = Thing("asdf");
}

你会看到结果。

来自ISO / IEC 14882 2003-10-15

  

8.5,第12部分

您的第1,第2结构称为直接初始化

  

12.1,第13部分

     

功能表示法类型转换(5.2.3)可用于创建   类型的新对象。 [注意:语法看起来像是显式调用   构造函数。 ] ...以这种方式创建的对象是未命名的。   [注意:12.2描述了临时对象的生命周期。 ] [注意:   显式构造函数调用不会产生左值,请参见3.10。 ]

在哪里阅读有关RVO的信息:

  

12特殊成员函数/ 12.8复制类对象/第15部分

     

当满足某些条件时,允许省略实现   类对象的复制构造,如果是复制构造函数,则甚至   和/或对象的析构函数具有副作用

从注释中使用编译器标志关闭它以查看此类复制行为)