在学习C#的过程中,我发现重新实现List或LinkedList之类的东西很有趣,只是为了理解它是如何工作的以及实现它时可能遇到的潜在问题。
在学习C ++的同时,由于我在C#方面有一些经验,所以我决定挑战自己并尝试实现比章节活动要求更高级的代码。所以,我最终试图在C ++中实现一个非通用列表来试用它,但最终收到了一个非常奇怪的seg错误。
关于代码的小免责声明,在尝试修复它时,我最终重构并删除了东西(但没有一个改变了错误)所以一两个函数没有用,但几个小时后试图理解问题,我不想删除或更改任何内容,并意外地解决问题。无论如何,这是代码。
class List {
private:
int *ListData;
size_t ListSize;
size_t Pos;
std::stack<size_t> NullList;
size_t InternalNull();
inline size_t PosOnly();
void Check();
size_t (List::*NextNumber)();
public:
List(bool InternalNullHandle);
List(size_t DefaultSize, bool InternalNullHandle);
~List();
void Add(const int SalesRef);
int GetCopy(size_t Pos);
int Get();
};
List::List(bool InternalNullHandle) {
NextNumber = (InternalNullHandle) ? &List::InternalNull : &List::PosOnly;
ListSize = 32;
ListData = new int[32];
Pos = 0;
}
List::List(size_t DefaultSize, bool InternalNullHandle) {
NextNumber = (InternalNullHandle) ? &List::InternalNull : &List::PosOnly;
ListSize = DefaultSize;
ListData = new int[DefaultSize];
Pos = 0;
}
List::~List() {
delete[] ListData;
}
void List::Check() {
if (Pos >= ListSize) {
size_t OldSize = ListSize;
ListSize*=2;
int *Buffer = new int[ListSize];
memcpy(Buffer, ListData, sizeof(int)*OldSize);
if (ListData != NULL) {
delete[] ListData; //POINT OF INTEREST ONE
ListData = NULL;
}
else {
std::cerr<<"ListData is null."<<std::endl;
}
ListData = Buffer;
}
}
size_t List::InternalNull() {
if (NullList.size() != 0) {
size_t ToReturn = NullList.top();
NullList.pop();
return ToReturn;
}
return PosOnly();
}
inline size_t List::PosOnly() {
size_t Old = Pos;
++Pos;
Check();
return Old;
}
inline void List::Add(const int SalesRef) {
//size_t Value = (this->*NextNumber) ();
//ListData[Value] = SalesRef;
//if the above code is utilised instead, everything works fine
ListData[ (this->*NextNumber) () ] = SalesRef; //POINT OF INTEREST TWO
}
inline int List::GetCopy(size_t Pos) {
return ListData[Pos];
}
我通常不发帖,但谷歌广泛。我最终安装并运行了valgrind,当使用Point of Interest Two时,它在Point of Interest One处给出了读写错误。但是,当Point of Interest Two被注释掉并且使用了注释行时,没有给出任何问题。
问题仅在128次迭代后出现,这意味着它正确地加倍到64和128以及正确删除数组。
另外,作为一个注释,代码在使用g ++编译的Windows上运行得非常好。
我尝试使用单独的类重现错误,但它完全正常。
同样,我知道我应该使用标准容器(我会)但我喜欢理解一切,而且我无法弄明白这一事实非常烦人。再加上我甚至无法重现它并且必须复制这个不完整且设计糟糕的代码这一事实只会让它变得更糟。感谢您的帮助!
轻微编辑,如果它真的很难读,我会添加注释并尝试清理代码而不会破坏(好吧,修复,而不是)它。它经过测试的操作系统是带有mingw(有效)的Windows 7和带有g ++的Debian(只能使用未注释的注释行)。
答案 0 :(得分:4)
声明的问题
ListData[ (this->*NextNumber) () ] = SalesRef; //POINT OF INTEREST TWO
是在获取字段ListData
的值和调用NextNumber
指向的成员函数之间没有序列点。因此编译器非常乐意在函数调用之前执行该加载,然后在调用之后对其进行索引。但是,该调用可能会导致重新分配ListData,因此它在调用之前获得的指针现在悬空(它指向刚刚删除的数组)并且发生了不好的事情。
使用注释掉的代码,您可以在获取ListData
之前强制执行函数调用,以便在调整大小重新分配后获取将始终获得正确的值。