来自C背景,我试图自学C ++。我一直在尝试实现UnionFind数据结构。我有一个课程如下: -
class UnionFind
{
private:
int count;
UnionFind* parent;
int val;
public:
UnionFind(int _val) : count(1), parent(this), val(_val) {
printf("UnionFind(): parent=%p this=%p\n", parent, this);
}
~UnionFind() {
printf("~UnionFind()\n");
}
int getcount()
{
if (parent == this)
return count;
return Find()->count;
}
int getval() { return val; }
void setparent(UnionFind* _parent) { parent = _parent; }
void setcount(int _count) { count = _count; }
// Does a union of 2 sets
void Union(UnionFind* u)
{
printf("Union(%d: this=%p, %d: u=%p)\n", val, this, u->getval(), u);
UnionFind* this_parent = Find();
UnionFind* u_parent = u->Find();
printf("this_parent=%p u_parent=%p\n", this_parent, u_parent);
if (this_parent == u_parent)
return;
if (this_parent->getcount() > u_parent->getcount()) {
this_parent->setcount(this_parent->getcount() + u_parent->getcount());
u_parent->setparent(this_parent);
} else {
u_parent->setcount(this_parent->getcount() + u_parent->getcount());
this_parent->setparent(u_parent);
}
}
// Returns the parent
UnionFind* Find()
{
//printf("%d: Find(%p) = ", val, this);
UnionFind* temp = parent;
while (temp != temp->parent)
temp = temp->parent;
//printf("%p\n", temp);
return temp;
}
};
稍后我尝试使用UnionFind
的一些实例填充向量,如下所示: -
vector<UnionFind> vecs;
for (int i = 0; i < N; i++)
vecs.push_back(UnionFind(i));
运行此代码显示UnionFind对象都具有相同的地址。我怀疑(并确认)这是因为在循环的每次迭代中都会调用UnionFind的析构函数。
我的问题是: -
vector<UnionFind*>
来解决这个问题,但这真的是要走的路吗?我读到与C不同的是,有可能在不使用new / malloc或delete / free的情况下解决C ++中的许多问题。解决这个问题的C ++方法是什么?[编辑]
Typo纠正。
[编辑2]
我试图实现一个不相交集/联合查找数据结构。 &#34;父&#34;最初会指向自己,但是当工会发生时,父母可能会指向不同的对象。
[编辑3]
以完整代码添加。
答案 0 :(得分:2)
这里有一些效果,你误解了发生了什么。创建vector<UnionFind *>
并使用动态内存分配将处理问题的症状,但不会导致根本原因。
首先,在循环的每次迭代中,
vecs.push_back(UnionFind(i));
构造一个临时的UnionFind
对象,将其副本存储在向量中,然后销毁临时对象。虽然没有保证,但没有什么能阻止临时人员拥有相同的地址。
其次,当使用int
参数调用构造函数时,每个对象都在其parent
成员中存储自己的地址。如果临时对象是在同一地址创建的,则所有临时对象都将parent
成员相等。另请注意,程序的输出仅包含有关main()
创建的临时文件的信息。
第三,当向量的push_back()
附加它接收的对象的COPY时,它会复制对象(通常使用复制构造函数)。您的代码尚未实现复制构造函数,因此编译器会自动生成一个。编译器生成的复制构造函数(和赋值运算符)按VALUE复制所有成员。因此,在复制UnionFind
对象以创建另一个对象时,新创建的对象将使其parent
成员指向原始对象。因此,假设循环中的每个对象具有相同的地址,则每个COPIED对象将具有相等的parent
成员。
第四,由于您只实现了一个构造函数,因此您的输出仅包含有关在main()
中创建的临时值的信息。您将看不到有关存储在向量中的临时对象的COPIES的信息。
同样的效果也可以看出,更简单地用;
UnionFind a(42);
UnionFind b(a);
创建了两个对象(b
作为a
的COPY),但您只会看到有关a
输出的信息(即有两个对象,但只有一个对象的信息)他们是印刷的)。如果你想看到有关b
的信息,你需要实现一个复制构造函数来完成这项工作,例如;
UnionFind(const UnionFind &rhs) : count(rhs.count), parent(this), val(rhs.val)
{
printf("UnionFind(): parent=%p this=%p\n", parent, this);
}
如果实现了复制构造函数,通常还需要实现其他成员函数。查找“三个规则” - 一个需要手工制作的复制构造函数的类,就像你的一样,也经常需要一个手工制作的赋值操作符和一个手工制作的析构函数。另外,我在上面的描述中过度简化,因此(在C ++ 11及更高版本中)查找“5的规则”,因为您可能需要手动实现移动构造函数以及正确跟踪对象。 / p>
然而,更一般地说,如果你正在尝试学习C ++,你最好避免通过类比C来避免这样做。在C语言中有效的技术在C ++中通常是无效的,反之亦然。学习C ++ I / O而不是坚持使用C I / O(你可以在C ++中使用C I / O这并不意味着它在C ++中和在C中一样有效)。避免在C ++中使用malloc()
和free()
,因为它们不会使用具有非平凡构造函数和析构函数的C ++类类型。事实上,在C ++中完全避免使用动态内存分配 - 在大多数情况下,有更安全的替代方案。
答案 1 :(得分:1)
您的联合查找对象并非在向量中都具有相同的地址。您正在堆栈上构造一个堆栈对象具有相同地址的对象。然后它被复制构造并且副本被放置在向量中。
std :: vector中的append函数在哪里?
错误:'std :: vector&gt;'
中没有名为'append'的成员