简体   繁体   English

C ++向量推回方法和临时对象创建

[英]C++ vector pushback method and temporary object creation

Below is the signature of vector pushback method in C++. 下面是C ++中向量推回方法的签名。

 void push_back (const value_type& val);

Now below is code 现在下面是代码

class MyInt
{
   int *a;
public:
  MyInt(int n):a(new int(n)){cout<<"constructor called"<<endl;}
   ~MyInt(){cout<<"destructor called"<<endl;}
   void show(){
   cout<<*a<<endl;
   }
 };


 void vector_test(vector<MyInt> &v)
 {
    MyInt m1(1);
    MyInt m2(2);
    v.push_back(m1);   
    v.push_back(m2);
 }



Output
-------------
constructor called
constructor called
destructor called
destructor called
destructor called
1
2
destructor called
destructor called

Here we see that for 2 objects which is created in vector_test function causes 2 times constructor invocation. 在这里,我们看到在vector_test函数中创建的2个对象导致2次构造函数调用。 But for destructor it got invoked 5 times. 但是对于析构函数,它被调用了5次。

Now my doubts and questions are 现在我的疑问和疑问是

  1. How come number of constructor and destructor calls are not matching? 构造函数和析构函数调用的数量为什么不匹配?
  2. I understand that there are some temporary objects getting created. 我知道有一些临时对象正在创建。 But how this mechanism is working? 但是这种机制是如何工作的?
  3. Just to test, I tried to provide a copy constructor but it causes compilation failure. 为了测试,我尝试提供一个副本构造函数,但它会导致编译失败。 What could be the reason? 可能是什么原因?
  4. Is there some logic inside which is copying the contents of m1 and m2 into new objects and puts in to the container? 内部是否存在将m1和m2的内容复制到新对象并放入容器的逻辑?

I would really appreciate if someone explain this in detail. 如果有人详细解释,我将不胜感激。 Thanks.. 谢谢..

You need to add logging also for copy constructor: 您还需要为复制构造函数添加日志记录:

MyInt(const MyInt& rhs):a(new int(*rhs.a)){cout<<"copy constructor called"<<endl;}

Some constructor calls are allowed by compiler to be elided, ie. 编译器允许省略一些构造函数调用,即。 in following line: 在以下行中:

MyInt m1 = 1;

you might expect copy constructor to be first called to instantiate temporary MyInt(1) then a copy constructor called with this temporary. 您可能希望首先调用复制构造函数以实例化临时MyInt(1)然后再调用一个与此临时复制实例一起调用的复制构造函数。 So you would see: 这样您会看到:

constructor called // for temporary
copy constructor called // for m1
destructor called       // for m1
destructor called  // for temporary

but because of copy elision compiler will directly instantiate your m1 instance using MyInt(int n) constructor, even though your copy constructor has side effects (std::cout is used). 但是由于复制省略,编译器将使用MyInt(int n)构造函数直接实例化您的m1实例,即使您的复制构造函数具有副作用(使用std :: cout)。 So no above // for temporary logs will be present. 因此将不存在// for temporary以上// for temporary日志。

To see this with gcc use: -fno-elide-constructors option which diasbles copy constructor elision. 要在gcc中使用以下命令,请使用: -fno-elide-constructors elide -fno-elide-constructors选项,该选项可避免复制构造函数省略。

Also it is a good practice to make constructors such as MyInt(int n) explicit, this is to disallow making MyInt object instance by mistake - you will have to make it explicit, ie MyInt var; var = static_cast<MyInt>(1); 将诸如MyInt(int n)构造函数设为显式也是一个好习惯,这是不允许错误地使MyInt对象实例成为MyInt -您必须将其显式化,即MyInt var; var = static_cast<MyInt>(1); MyInt var; var = static_cast<MyInt>(1); .

You have to remember that the compiler can potentially add five methods if you don't provide them. 您必须记住,如果不提供编译器,则可能会添加五个方法。 Im this case you are being affected by the compiler generated copy constructor. 在这种情况下,您会受到编译器生成的副本构造函数的影响。

#include <iostream>
#include <vector>
using namespace std;

class MyInt
{
   int *a;
public:
  MyInt(int n):a(new int(n)){cout<<"constructor called"<<endl;}
  MyInt(MyInt const& copy): a(new int(*copy.a)) {cout<<"copy constructor called"<<endl;}
  MyInt(MyInt&& move): a(move.a) {move.a = nullptr; cout<<"move constructor called"<<endl;}
   ~MyInt(){cout<<"destructor called"<<endl;}
   void show(){
   cout<<*a<<endl;
   }
 };


 void vector_test(vector<MyInt> &v)
 {
    MyInt m1(1);
    MyInt m2(2);
    v.push_back(m1);
    v.push_back(m2);
 }

 int main()
 {
    vector<MyInt> v;
    vector_test(v);
 }

When I run this I get 当我运行这个我得到

batman> ./a.out
constructor called
constructor called
copy constructor called
copy constructor called
copy constructor called
destructor called
destructor called
destructor called
destructor called
destructor called

Note: You are leaking memory from the destructor. 注意:您正在从析构函数中泄漏内存。

This is also why we have the emplace_back() interface. 这也是为什么我们有emplace_back()接口的原因。 This will reduce the number of copies of the object that are created. 这将减少所创建对象的副本数。 Additionally when optimizations are enabled you will see that some of those object are not copied but created in-place. 此外,启用优化后,您会看到其中的某些对象未复制,而是就地创建。

 void vector_test(vector<MyInt> &v)
 {
    MyInt m1(1);
    MyInt m2(2);

    // Can use emplace back
    v.emplace_back(1); // Created the object in-place in the vector.

    // Or you can use a temorary.
    v.push_back(2);  // Compiler sees there is a single argument constructor
                     // and will insert the constructor to convert a 2 into
                     // MyInt object.

    v.push_back(MyInt(3)); // Or you can create a temporary explicitly.
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM