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

时间:2016-05-06 17:15:38

标签: c++ c++11 vector copy-constructor push-back

下面是C ++中矢量回退方法的签名。

 void push_back (const value_type& val);

现在下面是代码

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

在这里,我们看到在vector_test函数中创建的2个对象导致2次 构造函数调用。 但对于析构函数,它被调用了5次。

现在我的怀疑和疑问是

  1. 如何构造函数和析构函数调用的数量不匹配?
  2. 我知道有一些临时对象被创建。但这种机制如何运作?
  3. 只是为了测试,我试图提供一个复制构造函数,但它会导致编译失败。可能是什么原因?
  4. 是否有一些逻辑将m1和m2的内容复制到新对象并放入容器?
  5. 如果有人详细解释,我真的很感激。感谢..

2 个答案:

答案 0 :(得分:1)

您还需要为复制构造函数添加日志记录:

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

编译器允许一些构造函数调用被省略,即。在以下行中:

MyInt m1 = 1;

您可能希望首先调用复制构造函数来实例化临时MyInt(1),然后使用此临时调用复制构造函数。所以你会看到:

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

但由于copy elision编译器将使用m1构造函数直接实例化您的MyInt(int n)实例,即使您的复制构造函数有副作用(使用std :: cout)。因此,不会出现// for temporary以上的日志。

要使用gcc,请使用-fno-elide-constructors选项来复制构造函数elision。

同样优良的做法是使MyInt(int n)这样的构造函数显式化,这是为了不允许错误地制作MyInt对象实例 - 你必须明确它,即MyInt var; var = static_cast<MyInt>(1);

答案 1 :(得分:0)

您必须记住,如果您不提供这些方法,编译器可能会添加五种方法。在这种情况下,您受到编译器生成的复制构造函数的影响。

#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);
 }

当我跑步时,我得到了

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

注意:您正在从析构函数中泄漏内存。

这也是我们拥有emplace_back()界面的原因。这将减少创建的对象的副本数。此外,当启用优化时,您将看到其中一些对象未被复制,而是就地创建。

 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.
 }