将指针复制到已分配的内存

时间:2014-12-15 19:43:21

标签: c++ pointers memory-management

这是一个通用的编程问题,我希望答案能够为问题提供一种替代方法,而不是快速修复或破解。我有两个对象,每个对象都有一些指向已分配内存的指针。我想将一些内部信息从一个对象复制到另一个对象。由于信息非常大,我只想复制指针。问题是当调用两个对象的析构函数时,它们每个都会在内部信息(现在在两个对象中)上调用析构函数。这导致析构函数在同一个指针上被调用两次。

由于这是一个相当复杂的场景,向您展示整个代码并不实际。我设计了一个简单的例子来说明问题的根源。代码附加两个预先存在的列表,而不复制任何数据。如输出所示,析构函数在最后两个节点上被多次调用。 (一旦K被破坏,并且L被销毁,因为两个列表都有指向这些节点的指针)。

#include <iostream>

struct Node {
    int data;
    Node * next;
};

class List {
    public:
        List(const int);
        ~List();
        void append(const int);
        void append(const List&);
        void print()const;
    private:
        Node * head;
        Node * tail;
};

List::List(const int x)
{
    Node * q = new Node;
    q->data = x;
    q->next = 0;

    head = q;
    tail = q;
}

List::~List()
{
    while (head != 0){
        Node * temp = head->next;
        std::cout << "Deleting " << head->data << std::endl;
        delete head;
        head = temp;
    }
}

void List::append(const int x)
{
    Node * q = new Node;
    q->data = x;
    q->next = 0;
    tail->next = q;
    tail = q;
}

void List::append(const List& L2)
{
    this->tail->next = L2.head;
    this->tail = L2.tail;
}

void List::print()const
{
    for (Node * iter = head; iter; iter=iter->next){
        std::cout << iter->data << " ";
    }
    std::cout << std::endl;
}

int main()
{
    List L = List(1);
    L.append(3);
    std::cout << "List L:\n";
    L.print();

    List K = List(5);
    K.append(10);
    std::cout << "List K:\n";
    K.print();

    L.append(K);
    std::cout << "List L:\n";
    L.print();
}

输出结果为:

List L:
1 3 
List K:
5 10 
List L:
1 3 5 10 
Deleting 5
Deleting 10
Deleting 1
Deleting 3
Deleting 0
Deleting 39125056

3 个答案:

答案 0 :(得分:1)

不使用指向节点的原始指针,而是使用std::shared_ptr<Node>并从析构函数中删除显式delete。只要在范围内有一个shared_ptr实例指向它,共享指针就会将Node保留在内存中。当不再有shared_ptr个实例指向Node时,Node将自动删除。

答案 1 :(得分:0)

使用std::shared_ptr代替原始指针将解决您的问题。

这是您的代码转换为使用std::shared_ptr

#include <iostream>
#include <memory>

struct Node {
    int data;
    std::shared_ptr<Node> next;
    Node(int d) : data(d), next(nullptr) {}
};

class List {
    public:
        List(const int);
        ~List();
        void append(const int);
        void append(const List&);
        void print()const;
    private:
        std::shared_ptr<Node> head;
        std::shared_ptr<Node> tail;
};

List::List(const int x)
{
    Node * q = new Node(x);
    head.reset(q);
    tail = head;
}

List::~List()
{
}

void List::append(const int x)
{
    Node * q = new Node(x);
    tail->next.reset(q);
    tail = tail->next;
}

void List::append(const List& L2)
{
    this->tail->next = L2.head;
    this->tail = L2.tail;
}

void List::print()const
{
    for (std::shared_ptr<Node> iter = head; iter.get() != nullptr; iter=iter->next){
        std::cout << iter->data << " ";
    }
    std::cout << std::endl;
}

int main()
{
    List L = List(1);
    L.append(3);
    std::cout << "List L:\n";
    L.print();

    List K = List(5);
    K.append(10);
    std::cout << "List K:\n";
    K.print();

    L.append(K);
    std::cout << "List L:\n";
    L.print();
}

输出:

List L:
1 3
List K:
5 10
List L:
1 3 5 10

<强>更新

std::share_ptr功能的简单实现:

template <typename T>
struct SharedPtr
{
   SharedPtr() : dataPtr(new Data(nullptr)) {}
   SharedPtr(T* n): dataPtr(new Data(n)) {}
   SharedPtr(SharedPtr const& copy) : dataPtr(copy.dataPtr)
   {
      dataPtr->useCount++;
   }

   ~SharedPtr()
   {
      dataPtr->useCount--;
      if ( dataPtr->useCount == 0 )
      {
         delete dataPtr;
      }
   }

   void reset(T* n)
   {
      dataPtr->useCount--;
      if ( dataPtr->useCount == 0 )
      {
         delete dataPtr;
      }
      dataPtr = new Data(n);
   }

   T* get() const
   {
      return dataPtr->n;
   }

   T* operator->() const
   {
      return get();
   }

   SharedPtr& operator=(SharedPtr const& rhs)
   {
      if ( this != & rhs )
      {
         dataPtr->useCount--;
         if ( dataPtr->useCount == 0 )
         {
            delete dataPtr;
         }

         dataPtr = rhs.dataPtr;
         dataPtr->useCount++;
      }
      return *this;
   }

   struct Data
   {
      Data(T* in) : n(in), useCount(1) {}
      ~Data() { if ( n != nullptr ) delete n; }
      T* n;
      size_t useCount;
   };
   Data* dataPtr;
};

答案 2 :(得分:0)

在main函数中,你声明了两个局部变量L,K,它们将在程序存在之前被解构。

在你的代码中,你将列表K附加到列表L,当解构时,K首先被解构,因为K仍然保持指针节点1,3,这意味着这个节点将是空闲的。但是L中的节点3仍然保持指向K头部的指针,这就是错误发生的方式。

在解构器中放置断点,你会发现它是如何发生的。