查询对象的本地副本和复制构造函数的工作
Query about local copy of object and working of copy constructor
对于按值传递对象时到底发生了什么,以及复制构造函数的工作,我几乎没有混淆。
为了实践这个概念,我写了下面的代码。
#include <iostream>
using namespace std;
class Cat
{
public:
Cat();
Cat(Cat&);
~Cat() { cout << "Destructor called\n";}
int itsage;
};
Cat::Cat()
{
cout << "Constructor called\n";
itsage=2;
}
Cat::Cat(Cat& )
{
cout << "Copy constructor called\n";
itsage=50;
}
Cat myFunction(Cat Frisky)
{
cout << "Frisky's age: " << Frisky.itsage << "\n";
Frisky.itsage=100;
cout << "Reassigned Frisky's age: "<< Frisky.itsage << "\n";
return Frisky;
}
int main()
{
Cat Mani;
cout << "Mani's age: " << Mani.itsage << "\n";
myFunction(Mani);
cout << Mani.itsage;
cout << endl;
return 0;
}
我得到的输出为:
Constructor called
Mani's age: 2
Copy constructor called
Frisky's age: 50
Reassigned Frisky's age: 100
Copy constructor called
Destructor called
Destructor called
2
Destructor called
前六行输出我已经看懂了。我读过当你传递或 return 对象时
按值,创建对象的临时副本。所以, Frisky
的临时副本得到了
在执行语句 return Frisky
时创建。但它立即被销毁,因为它没有分配给
任何事物。我认为这是第一次调用析构函数时产生的第七行输出。第九行的输出
十对我来说也很清楚。我对第二次调用析构函数的第八行输出感到困惑。
疑问1:第二次调用析构函数时,到底销毁了什么?当我们通过语句 myFunction(Mani)
调用 myFunction 时,Mani
的本地副本
被创建。复制构造函数完成后,Frisky
也在 myFunction
中创建。现在当 myFunction
结束时,什么被摧毁了? Mani
或 Frisky
?
的本地副本
疑问2:这个对象的本地副本存储在哪里?意思是当我调用 myFunction
时,Mani
的本地副本存储在哪里?在 myFunction
、main
或其他地方?
疑问3:拷贝构造函数也是函数的一种。然后在header中,在参数列表中,我们只提到了参数类型Cat &
而没有写参数名。我读到在函数原型中不写参数名是可以的,但是当我们在写函数定义时,我们应该写类型和名称。复制构造函数是此规则的例外吗?
主要问题是,“复制”构造函数 Cat::Cat(Cat &)
不会复制 itsage
的值,而是将其设置为任意值。因此,按值传递 Cat
对象并 returning 它会产生奇怪的结果,因为接收到的对象与原始对象不相似。
详细说明:函数myFunction
的参数Frisky
是按值传递的。这意味着调用 myFunction
会将传递的对象“复制到”Frisky
。副本将由复制构造函数Cat::Cat(Cat &)
构造。由于此构造函数将 itsage
设置为 50
,因此 myFunction
始终报告 50
的年龄,而不管原始传递值如何。
这同样适用于 return 值:从 myFunction
返回对象会使用复制构造函数 Cat::Cat(Cat &)
为调用者构造一个副本。 returned 副本始终具有值 itsage=50
。
解决方法:写一个合适的拷贝构造函数:
Cat::Cat(const Cat &other)
: itsage(other.itsage)
{ }
甚至更好:通过简单地省略复制构造函数来使用默认值。这个默认构造函数与上面提到的基本相同,而且是免费提供的。
如果你真的想修改传递给myFunction
的值并在myFunction
之外看到这些修改,你需要通过引用传递参数Frisky
:
Cat myFunction(Cat &Frisky)
{
// ...
}
解答你的疑惑:
第一个析构函数调用来自参数 Frisky
,它在函数 myFunction
return 时结束其生命周期。第二个析构函数调用来自 returned 对象。因为它永远不会被使用,所以它在 myFunction
调用后立即被销毁。第三次析构函数调用是在main
.
结束时结束Mani
的生命周期
就在进入 myFunction
之前,Mani
的本地副本存储在堆栈中,可以在 myFunction
中被 Frisky
引用。将在myFunction
.
结束时销毁
是的,您可以省略参数的名称,但在复制构造函数的情况下,这是无稽之谈。复制构造函数应该构造一个类似于传递的对象的对象。因为它需要访问传递的对象内的状态。
Lifetime:下图展示了classCat
:
各个对象的生命周期
int main()
{ Mani
Cat Mani; Frisky -+-
myFunction(Mani) -+- |
Cat myFunction(Cat Frisky) | |
{ | |
Frisky.itsage=100; Return | |
return Frisky; -+- | |
} | | |
-(*)----------------------------------------------+- | |
;-(*)--------------------------------------------------------+- |
return 0; |
}-(*)---------------------------------------------------------------------+-
(*)
大致表示调用其析构函数的时刻。
你问,为什么Mani
的本地副本没有析构函数调用。 Mani
的唯一本地副本(我不完全确定,你的意思)是为了在函数 myFunction
中获取一个名为 Frisky
的对象而创建的。您不需要 Mani
.
的任何其他副本
对于按值传递对象时到底发生了什么,以及复制构造函数的工作,我几乎没有混淆。 为了实践这个概念,我写了下面的代码。
#include <iostream>
using namespace std;
class Cat
{
public:
Cat();
Cat(Cat&);
~Cat() { cout << "Destructor called\n";}
int itsage;
};
Cat::Cat()
{
cout << "Constructor called\n";
itsage=2;
}
Cat::Cat(Cat& )
{
cout << "Copy constructor called\n";
itsage=50;
}
Cat myFunction(Cat Frisky)
{
cout << "Frisky's age: " << Frisky.itsage << "\n";
Frisky.itsage=100;
cout << "Reassigned Frisky's age: "<< Frisky.itsage << "\n";
return Frisky;
}
int main()
{
Cat Mani;
cout << "Mani's age: " << Mani.itsage << "\n";
myFunction(Mani);
cout << Mani.itsage;
cout << endl;
return 0;
}
我得到的输出为:
Constructor called
Mani's age: 2
Copy constructor called
Frisky's age: 50
Reassigned Frisky's age: 100
Copy constructor called
Destructor called
Destructor called
2
Destructor called
前六行输出我已经看懂了。我读过当你传递或 return 对象时
按值,创建对象的临时副本。所以, Frisky
的临时副本得到了
在执行语句 return Frisky
时创建。但它立即被销毁,因为它没有分配给
任何事物。我认为这是第一次调用析构函数时产生的第七行输出。第九行的输出
十对我来说也很清楚。我对第二次调用析构函数的第八行输出感到困惑。
疑问1:第二次调用析构函数时,到底销毁了什么?当我们通过语句 myFunction(Mani)
调用 myFunction 时,Mani
的本地副本
被创建。复制构造函数完成后,Frisky
也在 myFunction
中创建。现在当 myFunction
结束时,什么被摧毁了? Mani
或 Frisky
?
疑问2:这个对象的本地副本存储在哪里?意思是当我调用 myFunction
时,Mani
的本地副本存储在哪里?在 myFunction
、main
或其他地方?
疑问3:拷贝构造函数也是函数的一种。然后在header中,在参数列表中,我们只提到了参数类型Cat &
而没有写参数名。我读到在函数原型中不写参数名是可以的,但是当我们在写函数定义时,我们应该写类型和名称。复制构造函数是此规则的例外吗?
主要问题是,“复制”构造函数 Cat::Cat(Cat &)
不会复制 itsage
的值,而是将其设置为任意值。因此,按值传递 Cat
对象并 returning 它会产生奇怪的结果,因为接收到的对象与原始对象不相似。
详细说明:函数myFunction
的参数Frisky
是按值传递的。这意味着调用 myFunction
会将传递的对象“复制到”Frisky
。副本将由复制构造函数Cat::Cat(Cat &)
构造。由于此构造函数将 itsage
设置为 50
,因此 myFunction
始终报告 50
的年龄,而不管原始传递值如何。
这同样适用于 return 值:从 myFunction
返回对象会使用复制构造函数 Cat::Cat(Cat &)
为调用者构造一个副本。 returned 副本始终具有值 itsage=50
。
解决方法:写一个合适的拷贝构造函数:
Cat::Cat(const Cat &other)
: itsage(other.itsage)
{ }
甚至更好:通过简单地省略复制构造函数来使用默认值。这个默认构造函数与上面提到的基本相同,而且是免费提供的。
如果你真的想修改传递给myFunction
的值并在myFunction
之外看到这些修改,你需要通过引用传递参数Frisky
:
Cat myFunction(Cat &Frisky)
{
// ...
}
解答你的疑惑:
第一个析构函数调用来自参数
结束时结束Frisky
,它在函数myFunction
return 时结束其生命周期。第二个析构函数调用来自 returned 对象。因为它永远不会被使用,所以它在myFunction
调用后立即被销毁。第三次析构函数调用是在main
.Mani
的生命周期就在进入
结束时销毁myFunction
之前,Mani
的本地副本存储在堆栈中,可以在myFunction
中被Frisky
引用。将在myFunction
.是的,您可以省略参数的名称,但在复制构造函数的情况下,这是无稽之谈。复制构造函数应该构造一个类似于传递的对象的对象。因为它需要访问传递的对象内的状态。
Lifetime:下图展示了classCat
:
int main()
{ Mani
Cat Mani; Frisky -+-
myFunction(Mani) -+- |
Cat myFunction(Cat Frisky) | |
{ | |
Frisky.itsage=100; Return | |
return Frisky; -+- | |
} | | |
-(*)----------------------------------------------+- | |
;-(*)--------------------------------------------------------+- |
return 0; |
}-(*)---------------------------------------------------------------------+-
(*)
大致表示调用其析构函数的时刻。
你问,为什么Mani
的本地副本没有析构函数调用。 Mani
的唯一本地副本(我不完全确定,你的意思)是为了在函数 myFunction
中获取一个名为 Frisky
的对象而创建的。您不需要 Mani
.