Answers:
实际上,这两行都是正确的,但是做的事情却稍有不同。
第一行通过调用以下格式的构造函数在堆栈上创建一个新对象 Thing(const char*)
。
第二个比较复杂。它基本上执行以下操作
Thing
使用构造函数创建类型的对象Thing(const char*)
Thing
使用构造函数创建类型的对象Thing(const Thing&)
~Thing()
在步骤1中创建的对象Thing myThing = Thing(...)
不使用赋值运算符,它仍然是复制构造的,就像说的那样Thing myThing(Thing(...))
,并且不涉及默认构造的Thing
(编辑:帖子后来被更正了)
我认为第二行实际上是您的意思:
Thing *thing = new Thing("uiae");
这将是创建新动态对象(动态绑定和多态性所必需)并将其地址存储到指针的标准方法。您的代码执行JaredPar描述的操作,即创建两个对象(一个传递了一个const char*
,另一个传递了一个const Thing&
),然后~Thing()
在第一个对象上调用destructor()const char*
)。
相比之下,这是:
Thing thing("uiae");
创建一个静态对象,该对象将在退出当前范围时自动销毁。
编译器可以将第二种形式优化为第一种形式,但是不必这样做。
#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
很简单,这两行都在堆栈上创建对象,而不是像“ new”一样在堆上创建对象。第二行实际上涉及对副本构造函数的第二次调用,因此应避免使用它(也需要按照注释中的说明进行更正)。您应该尽可能多地将堆栈用于小对象,因为它的速度更快,但是,如果对象要比堆栈框架生存更长的时间,那么显然是错误的选择。
理想情况下,编译器将优化第二个,但这不是必需的。第一种是最好的方法。但是,了解C ++中堆栈和堆之间的区别非常重要,因为您必须管理自己的堆内存。
我玩了一点,当构造函数不带参数时,语法似乎变得很奇怪。让我举个例子:
#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实际上会调用构造函数,而Thing myThing()使编译器产生您想要创建函数指针或其他东西的作用!
除了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部分
您的第一,第二构造称为直接初始化
12.1,第13部分
功能符号类型转换(5.2.3)可用于创建其类型的新对象。[注意:语法看起来像构造函数的显式调用。] ...以这种方式创建的对象未命名。[注:12.2描述了临时对象的生存期。] [注意:显式构造函数调用不会产生左值,请参见3.10。]
在哪里可以阅读有关RVO的信息:
12个特殊的成员函数/ 12.8复制类对象/第15部分
当满足某些条件时,即使该对象的副本构造函数和/或析构函数具有副作用,也允许实现忽略类对象的副本构造。
使用注释中的编译器标志将其关闭以查看此类复制行为)