我不明白constructors work
怎么样?
这里我声明了一个对象obj2
。它调用构造函数abc()
,这非常好。
但是当我分配时
obj2 = 100
为什么编译器允许将整数初始化为类对象?如果它完全允许它如何破坏对象,然后它是如何调用另一个参数化构造函数。
现在我有另一个问题,为什么destructor
只被调用一次,因为有two
个对象?
我还有一个疑问,编译器是not doing anything
default constructor
那么为什么默认构造函数是required
?
class abc{
public:
int a, b;
abc()
{a = 0; b = 0;}
abc(int x)
{a = x;}
~abc()
{std::cout << "Destructor Called\n";}
};
int main()
{
abc obj1;
cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
abc obj2;
cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
obj2 = 100;
cout << "OBJ2 " << obj2.a << "\n";
system("pause");
return 0;
}
output:
OBJ1 0...0
OBJ2 0...0
Destructor Called
OBJ2 100
答案 0 :(得分:7)
但是当我分配obj2 = 100时,编译器如何允许将整数初始化为类对象?
这是因为当您执行以下操作时:
obj2 = 100;
这个将首先调用abc(int x)
来生成类的对象,然后调用默认的复制赋值运算符(因为没有提供用户定义)来将值100赋给现有的obj2
。分配后,临时对象被破坏。
如果您不希望出现此效果,请将析构函数标记为explict
以避免隐式调用。
explicit abc(int x) {
//do something
}
答案 1 :(得分:5)
obj2 = 100;
您定义了一个带int
的构造函数。这允许从int到abc
的隐式转换。这需要创建一个新对象。它不仅通过调用构造函数神奇地在现有对象中设置字段;构造函数构造新对象。
编辑:来自@Steve Jessop
的正确事件序列创建一个新实例,然后将其复制分配给原始实例,然后销毁临时(不是原始实例)。复制分配确实在现有对象中神奇地设置了两个字段。
答案 2 :(得分:3)
让我们播放节目并讲述,让我们设置所有特殊成员:
#include <iostream>
class abc{
public:
int a, b;
abc()
{ std::cout << "Default constructor\n"; a = 0; b = 0;}
abc(int x)
{ std::cout << "Int constructor\n"; a = x;}
abc(abc const& other): a(other.a), b(other.b)
{ std::cout << "Copy constructor (" << a << ", " << b << ")\n"; }
abc& operator=(abc const& other) {
std::cout << "Assignment operator (" << a << ", " << b << ") = (" << other.a << ", " << other.b << ")\n";
a = other.a;
b = other.b;
return *this;
}
~abc()
{std::cout << "Destructor Called\n";}
};
int main()
{
abc obj1;
std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
abc obj2;
std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
obj2 = 100;
std::cout << "OBJ2 " << obj2.a << "\n";
return 0;
}
我们获得this output:
Default constructor
OBJ1 0...0
Default constructor
OBJ2 0...0
Int constructor
Assignment operator (0, 0) = (100, 0)
Destructor Called
OBJ2 100
Destructor Called
Destructor Called
所以,让我们用线源来协调它们:
int main()
{
abc obj1;
// Default constructor
std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
// OBJ1 0...0
abc obj2;
// Default constructor
std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
// OBJ2 0...0
obj2 = 100;
// Int constructor
// Assignment operator (0, 0) = (100, 0)
// Destructor Called
std::cout << "OBJ2 " << obj2.a << "\n";
// OBJ2 100
return 0;
// Destructor Called
// Destructor Called
}
你们大部分都拥有这一切,让我们来看看这些惊喜。
第一个惊喜:即使obj2
稍后更改值abc obj2;
仍会在声明点调用默认构造函数。
第二个惊喜:obj2 = 100
实际上意味着obj2.operator=(abc(100));
,即:
abc
abc(100)
obj2
第三个惊喜:在范围的最后,在结束括号}
之前调用析构函数(是的,在<{em> return
之后)。由于您使用的是system("pause")
,我假设您使用的是Windows =&gt;虽然幸运,但是在你结束暂停之后它们会被调用,因此你的控制台Windows会在它们出现的那一刻眨眼间消失。您可以从更永久的控制台启动程序,也可以使用额外的范围:
int main () {
{
// your code here
}
system("pause");
return 0;
}
答案 3 :(得分:1)
这是因为有构造函数可以采用int
类型的参数。通过调用默认的副本分配,将临时创建的对象复制到obj2
。
要避免此类转换,请将构造函数标记为显式。
答案 4 :(得分:1)
你的析构函数被调用了3次,因暂停而无法看到它。