我想问你如何为以下类编写复制构造函数(和operator =)。
类节点存储每个节点的坐标x,y和指向另一个节点的指针。
class Node
{
private:
double x, y;
Node *n;
public:
Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {}
void setNode (Node *nn) : n(nn) {}
...
};
Class NodesList(继承自std :: vector)存储所有动态分配的节点
class NodesList : public std::vector<Node *>
{}
主程序:
int main()
{
Node *n1 = new Node(5,10,NULL);
Node *n2 = new Node(10,10,NULL);
Node *n3 = new Node(20,10,NULL);
n1->setNode(n2);
n2->setNode(n3);
n3->setNode(n2);
NodesList nl1;
nl1.push_back(n1);
nl1.push_back(n2);
nl1.push_back(n3);
//Copy contructor is used, how to write
NodesList nl2(nl1);
//OPerator = is used, how to write?
NodesList nl3 = nl1;
}
我不想创建每个节点的浅表副本,而是创建每个节点的深层副本。我可以问你一个带有复制构造函数的示例代码吗?
每个节点可以多次指向。让我们有这样的情况,当3节点n [1],n [2],n [3]存储在NodesList nl1中时:
n [1]指向n [2]
n [2]指向n [3]
n [3]指向n [2]
A]我们的复制构造函数处理节点n [1]。它创建一个新对象n [1] _new,由旧对象n [1] _old的副本表示。从n [1] _old指向的节点n [2]仍然不存在,因此必须也创建n [2] _new ...设置从n1_new到n2_new的指针。
B]然后处理第二个点n [2]。它不能创建两次,在[A]中创建了n [2] _new。但是指向节点n [3]不存在,因此创建了新对象n [3] _new作为旧对象n [3] _old的副本。设置从n2_new到n3_new的指针。
C]节点n [3] _new已经创建并且n [2] _new。设置从n3_new到n2_new的指针,不会创建其他对象......
因此,复制构造函数应该检查对象是否已在过去创建或者没有...
一些参考计数可能会有所帮助......
答案 0 :(得分:1)
我有解决问题的办法。添加了存储节点n的新版本的新数据成员n_ref:
class Node
{
private:
double x, y;
Node *n, *n_ref;
public:
Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {n_ref = NULL;}
Node * getNode() {return n;}
Node * getRefNode () {return n_ref;}
void setNode (Node *nn) {this->n = nn;}
void setRefNode (Node *nn) {this->n_ref = nn;}
复制构造函数创建节点的浅表副本:
Node (const Node *node)
{
x = node->x;
y = node->y;
n = node->n;
n_ref = node->n_ref;
}
NodesList的复制构造函数
NodesList::NodesList(const NodesList& source)
{
const_iterator e = source.end();
for (const_iterator i = source.begin(); i != e; ++i) {
//Node* n = new Node(**i);
//Node n still has not been added to the list
if ((*i)->getRefNode() == NULL)
{
//Create node
Node *node = new Node(*i);
//Add node to the list
push_back(node);
//Set this note as processed
(*i)->setRefNode(node);
//Pointed node still has not been added to the list
if ((*i)->getNode()->getRefNode() == NULL)
{
//Create new pointed node
Node *node_pointed = new Node ((*i)->getNode());
//Add node to the list
push_back(node_pointed);
//Set pointer to n
node->setNode(node_pointed);
//Set node as processed
((*i)->getNode())->setRefNode(node_pointed);
}
//Pointed node has already been added to the list
else
{
//Set pointer to node n
node->setNode((*i)->getRefNode());
}
}
//Node n has already been added to the list
else
{
//Get node
Node * node = (*i)->getRefNode();
//Pointed node still has not been added
if ((*i)->getNode()->getRefNode() == NULL)
{
//Create new node
Node *node_pointed = new Node ((*i)->getNode());
//Add node to the list
push_back(node_pointed);
//Set pointer to n
node->setNode(node_pointed);
//Set node as processed
((*i)->getNode())->setRefNode(node_pointed);
}
//Pointed node has already been added to the list
else
{
//Set pointer to n
node->setNode((*i)->getNode()->getRefNode());
}
}
}
}
答案 1 :(得分:0)
您不应该继承标准库容器(因为它们缺少虚拟析构函数)。相反,将它们作为成员变量包含在您的类中。
由于您需要深层复制,因此您需要:(三级规则)
Node(Node const& orig): x(orig.x), y(orig.y), n() {
if (orig.n) n = new Node(*orig.n);
}
Node& operator=(Node const& orig) {
// The copy-swap idiom
Node tmp = orig;
swap(tmp); // Implementing this member function left as an exercise
return *this;
}
~Node() { delete n; }
更好的想法可能是避免完全使用指针,只需将节点放在合适的容器中即可。
答案 2 :(得分:0)
我只想使用std :: list&lt; Node&gt;而不是NodesList。好吧,让我们编码......
NodesList::NodesList(const NodesList& source)
{
const_iterator e = source.end();
for (const_iterator i = source.begin(); i != e; ++i) {
Node* n = new Node(**i);
push_back(n);
}
}
答案 3 :(得分:0)
在NodeList::NodeList(const NodeList&)
中执行浅拷贝,您不必担心循环中断复制操作。免责声明:以下内容未经测试,不完整且可能存在错误。
class NodeList {
private:
typedef std::vector<Node*> Delegate;
Delegate nodes;
public:
NodeList(int capacity=16) : nodes() { nodes.reserve(capacity); }
NodeList(const NodeList& from);
virtual ~NodeList();
NodeList& operator=(const NodeList& from);
/* delegated stuff */
typedef Delegate::size_type size_type;
typedef Delegate::reference reference;
typedef Delegate::const_reference const_reference;
typedef Delegate::iterator iterator;
typedef Delegate::const_iterator const_iterator;
size_type size() const { return nodes.size(); }
iterator begin() { return nodes.begin(); }
const_iterator begin() const { return nodes.begin(); }
iterator end() { return nodes.end(); }
const_iterator end() const { return nodes.end(); }
// ...
};
NodeList::NodeList(const NodeList& from)
: nodes(from.size()), flags(NodeList::owner)
{
std::map<Node*, Node*> replacement;
Delegate::const_iterator pfrom;
Delegate::iterator pto;
// shallow copy nodes
for (pfrom=from.begin(), pto=nodes.begin();
pfrom != from.end();
++pfrom, ++pto)
{
replacement[*pfrom] = *pto = new Node(**pfrom);
}
// then fix nodes' nodes
for (pto = nodes.begin(); pto != nodes.end(); ++pto) {
(*pto)->setNode(replacement[(*pto)->getNode()]);
}
}
NodeList::operator=(const NodeList&)
可以使用复制交换习惯用法,与Tronic的Node::operator=(const Node&)
相同。
此设计存在潜在的内存泄漏,因为复制的NodeList
(初始)是引用其节点的唯一位置。如果临时NodeList
超出范围,则执行不良将泄漏所包含列表的Node
。
一种解决方案是宣称NodeList
拥有Node
s。只要您不将Node
添加到多个NodeList
(通过NodeList::push_back
,NodeList::operator[]
&amp; c),NodeList
的方法就可以必要时删除节点(例如在NodeList::~NodeList
,NodeList::pop_back
)。
NodeList::~NodeList() {
Delegate::iterator pnode;
for (pnode = nodes.begin(); pnode != nodes.end(); ++pnode) {
delete *pnode;
}
}
void NodeList::pop_back() {
delete nodes.back();
nodes.pop_back();
}
另一种解决方案是使用smart pointers,而不是Node*
。 NodeList
应存储shared pointers。 <{1}}应该是weak pointer以防止所有权周期。
答案 4 :(得分:0)
显然每个节点只允许指向同一列表中的另一个节点?否则,列表的“深层复制”需要更多定义。它不应该连接到原始的NodeList吗?它不应该连接到任何原始节点吗?不在列表中的节点副本是否被复制添加到其他列表或自由浮动?
如果节点到节点的所有指针都被限制在NodeList中,那么也许您应该存储索引而不是指针,那么就不需要特殊处理。