我正在尝试用C ++实现minheap。但是,以下代码会引发错误,例如:
heap.cpp:24:4:错误:无法在作业中将“复杂 int”转换为“int”
L = 2I;
^
heap.cpp:25:4:错误:无法在作业中将“复杂 int”转换为“int”
R = 2I + 1;
^
heap.cpp:在成员函数'int Heap :: main()'中:
heap.cpp:47:16:错误:没有匹配函数来调用'Heap :: heapify(int [11],int&)'
heapify(a,i);
^
heap.cpp:47:16:注意:候选人是:
heap.cpp:21:5:注意:int Heap :: heapify(int)
int heapify(int i)// i是父索引,[]是堆数组
^
heap.cpp:21:5:注意:候选人需要1个参数,2个提供
make: * [heap]错误1
#include <iostream>
using namespace std;
#define HEAPSIZE 10
class Heap
{
int a[HEAPSIZE+1];
Heap()
{
for (j=1;j<(HEAPISZE+1);j++)
{
cin>>a[j];
cout<<"\n";
}
}
int heapify(int i) //i is the parent index, a[] is the heap array
{
int l,r,smallest,temp;
l=2i;
r=2i+1;
if (l<11 && a[l]<a[i])
smallest=l;
else
smallest=i;
if (r<11 && a[r]<a[smallest])
smallest=r;
if (smallest != i)
{
temp = a[smallest];
a[smallest] = a[i];
a[i]=temp;
heapify(smallest);
}
}
int main()
{
int i;
for (i=1;i<=HEAPSIZE;i++)
{
heapify(a,i);
}
}
}
答案 0 :(得分:3)
最终,这段代码的问题在于它是由跳过&#34; C ++ for Beginners&#34;的第1章,第2章和第3章的人编写的。让我们从一些基础开始。
#include <iostream>
using namespace std;
#define HEAPSIZE 10
这里,我们已经包含了I / O(输入输出)的C ++标头。一个好的开始。然后,我们发布了一个指令,说明&#34;将名称空间std
中的所有内容放入全局名称空间&#34;。这样可以节省一些输入,但这意味着所有数千个被精心划分为std::
的内容现在都可能与您希望在代码中使用的名称冲突。这是A Bad Thing(TM)。尽量避免这样做。
然后我们继续使用C-ism,一个#define。有时您仍然需要在C ++中执行此操作,但最好避免使用它。我们将回到这一点。
下一个问题,至少在您发布的代码中,是对C ++ class
的误解。
&#39; C&#39; C ++所基于的语言具有struct
的概念,用于描述数据项的集合。
struct
{
int id;
char name[64];
double wage;
};
重要的是要注意语法 - 尾随&#39 ;;&#39;。这是因为你可以描述一个结构并同时声明它的变量类型。
struct { int id; char name[64]; } earner, manager, ceo;
这声明了一个没有类型名称的结构,以及该类型的变量earner
,manager
和ceo
。分号告诉编译器我们何时完成此语句。在&#39;}之后需要分号时学习。需要一点时间;通常你不会,但在结构/类定义中你会这样做。
C ++为C添加了很多东西,但一个常见的误解是struct
和class
在某种程度上完全不同。
C ++最初扩展了struct
概念,允许您在结构的上下文中描述函数,并允许您将成员/函数描述为private
,protected
或{{1并允许继承。
当您声明public
时,默认为struct
。 public
只不过是一个class
,它开始是私有的。
struct
结果定义相同。
您的代码没有访问说明符,因此Heap类中的所有内容都是私有的。这导致的第一个也是最棘手的问题是:没有人可以调用你的任何函数,因为它们是私有的,它们只能从其他类成员调用。这包括构造函数。
struct
{
int id;
char name[64];
double wage;
};
class
{
public:
int id;
char name[64];
double wage;
};
上述代码无法编译,因为class Foo { Foo () {} };
int main()
{
Foo f;
return 0;
}
不是main
的成员,因此无法调用任何内容Foo
。
这给我们带来了另一个问题。在您发布的代码中,private
是main
的成员。 C ++程序的入口点是Foo
,而不是main
或Foo::main
或std::main
。只是,好老Foo::bar::herp::main
或int main(int argc, const char* argv[])
。
在C中,对于结构体,因为C不具有成员函数,所以你永远不会遇到直接使用struct-members但没有使用指针或成员引用作为前缀的情况,例如: int main()
或foo.id
。在C ++中,在成员函数中,成员变量可以像本地函数变量或参数一样被引用。这可能会导致一些混淆:
ptr->wage
有很多方法可以解决这个问题,但最常见的方法之一是使用class Foo
{
int a, b;
public:
void Set(int a, int b)
{
a = a; // Erh,
b = b; // wat???
}
};
为成员变量添加前缀。
你的代码与此相反,显然C中的原始代码将数组传递给heapify,并且数组位于局部变量m_
中。当你将a
变成一个成员时,保持变量名完全相同允许你不要错过你不再需要将它传递给对象的事实(事实上,你的heapify成员函数不再需要一个数组作为指针,导致你的一个编译错误。)
我们遇到的下一个问题是你的函数a
,而不是问题的直接部分。首先,它是私有的 - 您使用Heap()
并且尚未说class
。但其次,你已经错过了这个功能的重要性。
在C ++中,每个struct / class都有一个与定义同名的隐含函数。对于public
class Heap
。这是默认的构造函数&#39;。这是在没有任何参数的情况下有人创建Heap()
实例时执行的函数。
这意味着当编译器创建一个短期临时堆,或者当你创建一个Heap()的向量并分配一个新临时值时,它将被调用。
这些功能有一个目的:准备对象占用的存储空间。你应该尽量避免尽可能多的其他工作,直到以后。使用Heap
填充构造函数中的成员是您可以做的最糟糕的事情之一。
我们现在有了一个基础来开始以一种可行的方式编写代码的外壳。
最后一个改变是更换&#34; HEAPSIZE&#34;与类枚举。这是封装的一部分。您可以将std::cin
作为HEAPSIZE
离开,但是您应该在课程中公开它,以便外部代码不必依赖它,但可以改为#define
或{Heap::Size
{1}}等等。
heapInstance.size()
最后,我们遇到了代码中一个简单的基本问题。 C ++没有隐式乘法。 #include <iostream>
#include <cstdint> // for size_t etc
#include <array> // C++11 encapsulation for arrays.
struct Heap // Because we want to start 'public' not 'private'.
{
enum { Size = 10 };
private:
std::array<int, Size> m_array; // meaningful names ftw.
public:
Heap() // default constructor, do as little as possible.
: m_array() // says 'call m_array()s default ctor'
{}
// Function to load values from an istream into this heap.
void read(std::istream& in)
{
for (size_t i = 0; i < Size; ++i)
{
in >> m_array[i];
}
return in;
}
void write(std::ostream& out)
{
for (size_t i = 0; i < Size; ++i)
{
if (i > 0)
out << ','; // separator
out << m_array[i];
}
}
int heapify(size_t index)
{
// implement your code here.
}
}; // <-- important.
int main(int argc, const char* argv[])
{
Heap myHeap; // << constructed but not populated.
myHeap.load(std::cin); // read from cin
for (size_t i = 1; i < myHeap.Size; ++i)
{
myHeap.heapify(i);
}
myHead.write(std::cout);
return 0;
}
是带有后缀的数字2i
。它与2
不同。
2 * i
您的代码也有一个独特之处,表明您在基于0和基于1的实现之间进行混合。选择一个并坚持下去。
---编辑---
从技术上讲,这:
int l = 2 * i;
封装不良。我用这种方式编写它来绘制原始代码布局,但我想指出分离构造和初始化的一个原因是它允许初始化确保一切准备就绪。
因此,将heapify调用移动到load函数会更正确。毕竟,聚合更好的时间比添加新值更好,使整个列表按顺序排列。
myHeap.load(std::cin); // read from cin
for (size_t i = 1; i < myHeap.Size; ++i)
{
myHeap.heapify(i);
}
现在,您已经简化了课程api,用户不必了解内部机制。
for (size_t i = 0; i < Size; ++i)
{
in >> m_array[i];
heapify(i);
}