2016-05-06 27 views
0

以下是C++中向量推回方法的签名。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的内容复制到新对象中并放入容器中?

我真的很感激,如果有人解释这一点的细节。谢谢..

+11

您错过了跟踪复制构造函数(有一个由编译器生成的)。 –

+0

一旦你获得了复制构造函数的编写和跟踪,请注意对象创建和销毁的次数取决于编译器,编译器优化等。 – PaulMcKenzie

+0

“我试图提供一个复制构造函数,但它会导致编译失败。可能是什么原因?”显示,也许我们可以告诉。否则,最好的人可以说是你可能有语法错误。 – user4581301

回答

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 

但由于复制省略编译器将使用MyInt(int n)构造,即使你的拷贝构造函数有副作用(STD ::法院时)直接实例化m1实例。所以不会有超过// for temporary的日志出现。

要使用gcc使用:-fno-elide-constructors选项,它可以复制拷贝构造函数elision。

此外,它是一个很好的做法,使构造如MyInt(int n)明确的,这是不允许的错误使MyInt对象实例 - 你将不得不使其明确,即MyInt var; var = static_cast<MyInt>(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. 
}