假设我有一个二叉树类,其目的是将实际间隔(a,b)归纳为许多小间隔,选择中点。注意:我实际上写的课程涉及平面中的三角形,但想法是一样的。
以下是该类在头文件中的样子:
class Tree
{
public:
Tree(double &a, double &b, int depth);
~Tree();
Tree getCopy() const;
private:
Tree(double *a, double *b, int depth, int maxDepth);
double *a, *b;
int depth, maxDepth;
Tree *leftChild, *rightChild;
};
请注意,树存储指向双打a和b的指针,而不是实际的双精度数。这样做的原因是为了节省内存(和速度?),观察a和b将被许多儿童树共享(我知道双重是很“轻”但在我的实际课堂上,我有一些“更重” “)。
现在这里是主要的构造函数:
Tree::Tree(double *a, double *b, int depth, int maxDepth) :
depth(depth), maxDepth(maxDepth)
{
if (depth == maxDepth)
{
this->a = new double(*a);
this->b = new double(*b);
}
else
{
this->a = a;
this->b = b;
}
if (depth == 0)
{
leftChild = 0;
rightChild = 0;
}
else
{
double * midpoint = new double((*a+*b)/2);
leftChild = new Tree(a, midpoint, depth - 1, maxDepth);
rightChild = new Tree(midpoint, b, depth - 1, maxDepth);
}
}
析构函数:
Tree::~Tree()
{
if (depth == 0)
{
delete b;
}
else
{
delete leftChild;
delete rightChild;
}
if (depth == maxDepth)
{
delete a;
}
}
我希望这两个功能都是正确的。请注意,构造函数是私有的,它是递归调用的构造函数。公共构造函数如下:
Tree::Tree(double &a, double &b, int depth)
{
*this = *(new Tree(&a, &b, depth, depth));
}
我知道这看起来很奇怪,我担心这可能会造成内存泄漏吗? 但另一方面,如果我写了:
*this = Tree(&a, &b, depth, depth);
这不会失败吗?让我试着通过考虑等效函数
来解释为什么我认为它可能会失败{
Tree T(&a, &b, depth, depth);
*this = T;
}
我认为只要退出此功能,对象T就会被销毁,因此会删除子项等。
复制功能同样令人担忧:
Tree Tree::getCopy() const
{
return Tree(a, b, depth, depth);
}
所以问题是:编写这些函数的正确方法是什么?我也很乐意听到关于我写这门课的方式的一般评论。提前谢谢!
答案 0 :(得分:1)
你在这里制造泄漏:
Tree::Tree(double &a, double &b, int depth)
{
*this = *(new Tree(&a, &b, depth, depth));
}
使用new分配新的Tree对象,不要在任何地方删除它。
此外,您不遵守三条规则。您创建了一个实现析构函数的类,并且三个规则表示(通常)在创建任何:copy构造函数,赋值运算符或析构函数时,通常需要所有这些。
我建议你将Tree(double * a,double * b,int depth,int maxDepth)设为静态函数,然后根据需要从公共构造函数中调用它。
答案 1 :(得分:1)
公共构造函数如下:
Tree::Tree(double &a, double &b, int depth)
{
*this = *(new Tree(&a, &b, depth, depth));
}
我知道这看起来很奇怪,我担心这可能会造成内存泄漏吗?
您(确实)正在创建内存泄漏。
一种解决方案是重写它:
new(this) Tree(&a, &b, depth, depth);
Placement-new不会分配内存但仍会拨打电话。我不确定这是否有任何隐藏的陷阱。
但解决方案仍然很奇怪。如果您正在使用C ++ 11,则应该根据move-constructor实现(如果可能,请转发构造函数调用)。否则,你应该用std::shared_ptr
(和/或pimpl成语)来实现。
编辑:另一个(更)优雅的解决方案是实施“三大半”(void Tree::swap(Tree& x);
和三大);然后,你可以写:
{
swap(Tree(&a, &b, depth, depth));
}
答案 2 :(得分:0)
我认为maxDepth
对于类的每个对象都是相同的,所以将其设为static
(并且只需要对其值进行一次初始化):
static int maxDepth;
然后我会在一个构造函数中重写私有和公共构造函数(你的实际公共构造函数会造成内存泄漏):
Tree::Tree(double &a, double &b, int depth)
{
// stuff from private constructor
}
尽量与您使用的类型保持一致:
double * midpoint = new double((*a + *b) / 2.0); // 2.0 is double; 2 is int
最后我真的没有得到:
Tree(&a, &b, depth, depth); // why ... depth, depth ... ???
你说过,你不想浪费内存,所以你使用了指针,但实际上指针也有一个大小......如果只有树的叶子包含a
和b
(但在我看来,这不是这种情况),你可以这样做:
class Tree
{
// functors declarations
bool isLeaf = false;
int depth, maxDepth;
Tree *leftChild, *rightChild;
};
Tree::Tree(double a, double b, int depth): depth(depth) {
if (depth == maxDepth) {
isLeaf = true;
this->leftChild = new double(a);
this->rightChild = new double(b);
}
}
然后如果isLeaf
为false
,则会找到孩子,如果是true
则查找值。