这是一个通用的编程问题,我希望答案能够为问题提供一种替代方法,而不是快速修复或破解。我有两个对象,每个对象都有一些指向已分配内存的指针。我想将一些内部信息从一个对象复制到另一个对象。由于信息非常大,我只想复制指针。问题是当调用两个对象的析构函数时,它们每个都会在内部信息(现在在两个对象中)上调用析构函数。这导致析构函数在同一个指针上被调用两次。
由于这是一个相当复杂的场景,向您展示整个代码并不实际。我设计了一个简单的例子来说明问题的根源。代码附加两个预先存在的列表,而不复制任何数据。如输出所示,析构函数在最后两个节点上被多次调用。 (一旦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
答案 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头部的指针,这就是错误发生的方式。
在解构器中放置断点,你会发现它是如何发生的。