放置新的唯一指针

时间:2017-05-01 16:02:06

标签: c++ segmentation-fault unique-ptr

这是我的第一篇帖子。我希望它包含正确的,最少的信息。如果我遗漏了任何内容,请告诉我。

我正在尝试使用placement new来提高后续代码块的效率(我希望减少内存分配调用的数量,因为number_of_actions变量很大(> 500k))。

首先,我使用两个类,其关系可以概括为:

class txn {
public:
  int i;
  txn(): i(0) {};
};

class ActionClass {
private:
  txn* t;
public:
  ActionClass(txn* t): t(t) {};
  ~ActionClass() { delete t; }
};

我最初用来创建指向对象的指针数组的代码:

std::vector<std::unique_ptr<IBatchAction>> allocate_actions(unsigned int number_of_actions) {
  std::vector<std::unique_ptr<IBatchAction>> res;
  for (unsigned int i = 0; i < number_of_actions; i++) {
    // construct the action
    std::unique_ptr<IBatchAction> act = std::make_unique<ActionClass>(new TestTxn());

    res.push_back(std::move(act));
  }
  return res;
}

更改为使用展示位置后的代码:

std::vector<std::unique_ptr<IBatchAction>> allocate_actions(unsigned int number_of_actions) {
  std::vector<std::unique_ptr<IBatchAction>> res(number_of_actions);

  // allocate all of the memory for actions up front to amortize the cost.
  ActionClass* actions = 
    reinterpret_cast<ActionClass*>(new char[number_of_actions * sizeof(ActionClass)]);
  txn* txns = reinterpret_cast<txn*>(new char[number_of_actions * sizeof(TestTxn)]);

  // use placement new to initialize actions and assign them to unique_ptrs
  for (unsigned int i = 0; i < number_of_actions; i++) {
    // construct the action using placement new from the memory allocated above.
    res[i].reset(new(&(actions[i])) ActionClass(new(&(txns[i])) TestTxn()));
  }
  return res;
}

在main.cpp中我只是多次调用上面的函数,计时并返回0.从上面的函数返回的向量在循环迭代之间被销毁。结果我在segfault之前获得了以下堆栈跟踪:

#0  std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >::~unique_ptr (
    this=<optimized out>, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/unique_ptr.h:236
#1  std::_Destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> > > (
    __pointer=<optimized out>) at /usr/include/c++/5/bits/stl_construct.h:93
#2  std::_Destroy_aux<false>::__destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >*> (__last=<optimized out>, __first=0x7ffff7f06018)
    at /usr/include/c++/5/bits/stl_construct.h:103
#3  std::_Destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >*> (
    __last=<optimized out>, __first=<optimized out>) at /usr/include/c++/5/bits/stl_construct.h:126
#4  std::_Destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >*, std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> > > (__last=0x7ffff7fc9510, 
    __first=<optimized out>) at /usr/include/c++/5/bits/stl_construct.h:151
#5  std::vector<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >, std::allocator<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> > > >::~vector (this=0x7fffffffd910, 
    __in_chrg=<optimized out>) at /usr/include/c++/5/bits/stl_vector.h:424
#6  time_workload_creation (exp_conf=...) at start_batch/main.cc:18
#7  0x000000000040320c in main (argc=<optimized out>, argv=<optimized out>)
    at start_batch/main.cc:44

我对放置新概念不熟悉。以下是我用来编写上述代码的内容:

  1. Can I use placement new to reset an object within a shared_ptr? - 使用重置来分配使用展示位置
  2. 创建的新对象
  3. placement new on shared_ptr make seg fault when delete - 一个非常类似的问题。这个答案对我有帮助:(
  4. 我可能会做一些明显错误的事情,但我无法弄清楚。救命?如果你需要main(简化)中的实际代码,那么它(actions.h包含我讨论过的函数)。

    #include "actions.h"
    
    #include <chrono>
    #include <vector>
    
    void time_workload_creation(unsigned int act_num) {
      std::chrono::system_clock::time_point time_start, time_end;
      std::vector<double> results;
      for (unsigned int i = 0; i < 10; i++) {
        time_start = std::chrono::system_clock::now();
        auto workload = allocate_actions(act_num);
        time_end = std::chrono::system_clock::now();
        results.push_back(
            std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_start).count());
      }
    
      for (unsigned int i = 0; i < 10; i++) {
        std::cout << i << "\t\t" <<  
          act_num << "\t\t" <<  
          results[i] << "\t\t" <<  
          act_num / results[i] << std::endl;
      }
    };
    
    int main(int argc, char** argv) {
      time_workload_creation(1000000);
      return 0;
    }
    

    编译使用:gcc版本5.4.0 20160609(Ubuntu 5.4.0-6ubuntu1~16.04.4)

1 个答案:

答案 0 :(得分:3)

  

更改为使用展示位置后的代码新:
  [...]

此代码具有未定义的行为。您使用<rewrite> <rules> <clear /> <rule name="Force WWW and SSL" enabled="true" stopProcessing="true"> <match url="(.*)" /> <conditions logicalGrouping="MatchAny"> <add input="{HTTP_HOST}" pattern="^[^www]" /> <add input="{HTTPS}" pattern="off" /> </conditions> <action type="Redirect" url="https://www.sysadmit.com/{R:1}" appendQueryString="true" redirectType="Permanent" /> </rule> </rules> </rewrite> 创建char数组,然后重置N new[]个对象,这些对象将尝试删除N个不同的unique_ptr个对象。你怎么期望这个工作?您需要使用ActionClass释放char数组,而不是在delete[]数组的块上使用N delete个操作。

同样,每个char会尝试ActionClassdelete个对象,但这也是完全未定义的,因为您没有分配N {{1}你分配了另一个txn数组。

这不是内存分配的工作原理。你不能分配一个大块然后解除分配它。 (嗯,你可以,但只能编写自己的自定义(de)分配函数或分配器)。