为什么以下代码的输出始终为suck
。如何获得happy
作为输出?为什么happy
分支无法访问?
public class HowToMakeStackoverflowBetter {
private static final int HUMAN_PATIENCE = 10;
private List<Member> members = new ArrayList<>();
private int atmosphere = -10;
private Random r = new Random();
public HowToMakeStackoverflowBetter(int size) {
for (int i = 0; i < size; i++) { members.add(new Member()); }
}
public Member pick() { return members.get(r.nextInt(members.size())); }
public class Member {
private int patience = HUMAN_PATIENCE;
private Question question = null;
public Member() { patience = r.nextInt(patience+1) + atmosphere; }
public void vote(Question q) {
if (patience >= 0) {
voteUp(q);
} else {
voteDown(q);
}
}
public void ask() {
question = new Question();
for (Member member : members) {
member.vote(question);
}
}
private void voteUp(Question q) { ++q.vote; }
private void voteDown(Question q) { --q.vote; }
public String toString() {
return (question.vote >= 0)? "Happy!" : "Suck!";
}
}
public class Question { private int vote; }
public static void main(String[] args) {
HowToMakeStackoverflowBetter stackoverflow = new HowToMakeStackoverflowBetter(100);
Member me = stackoverflow.pick();
me.ask();
System.out.println(me);
}
}
经过1000次循环后,它给了我们1000次吮吸。我记得2或3年前,事实并非如此。改变了一些事情。
答案 0 :(得分:2)
两个问题。第一:
linkedList::linkedList(){
*sentinel.last=sentinel;
*sentinel.next=sentinel;
sentinel.str="I am sentinel!!";
};
sentinel
是您的成员变量,.last
是指向另一个节点的指针。这尚未初始化,因此尝试使用它是未定义的行为。在实践中,它有效地指向内存中(或外部)的随机地址,并且您尝试取消引用指针,然后将整个Sentinel对象复制到想象的指向地址的节点上:即您尝试复制3个指针在sentinel
节点成员变量中到内存中的随机地址。
你可能想这样做:
linkedList::linkedList()
{
sentinel.last = &sentinel;
sentinel.next = &sentinel;
sentinel.str = "I am sentinel!!";
}
其次,您显式调用了LinkedList的析构函数,当执行编译器排列的销毁时,当对象离开它所创建的堆栈作用域时(即main()
的末尾),会导致未定义的行为。
我建议您将node.str
更改为std::string
,就像您希望能够处理变量文本的任何实际程序一样,而不只是指向(常量)字符串文字。如果你混合使用字符串文字和免费存储分配的字符数组,你将无法知道何时调用delete[]
来释放内存。您可以通过始终使用new[]
创建要存储的字符串数据的新副本来解决此问题,但使用std::string
更安全,更容易。
答案 1 :(得分:1)
您忘记初始化sentinel
在下面的代码中,您尝试使用sentinel(同样的事情)初始化sentinel(尚未构建)。因此,您必须将一些内容传递给构造函数,该构造函数可用于初始化您的成员变量sentinel
*sentinel.last=sentinel;
也不需要像这样调用析构函数。一旦myList
超出范围,就会调用析构函数。
myList.~linkedList();
答案 2 :(得分:1)
由于您将其分配为本地变量,因此退出main时将自动销毁mylist
。由于您已经显式调用了它的析构函数,这会导致未定义的行为(尝试两次销毁同一个对象)。
作为一个快速指南,显式调用析构函数的仅时间与placement new结合使用。如果你还不知道那是什么,那很好;只是把它作为你不应该调用析构函数的标志。
答案 3 :(得分:0)
程序可能会崩溃,但是:
*sentinel.last=sentinel;
*sentinel.next=sentinel;
sentinel未初始化,我在堆栈上有随机值。
答案 4 :(得分:0)
当您尚未初始化成员变量last
时,您尝试取消引用指针next
和sentinel
。
这些去引用*sentinel.last=sentinel
*sentinel.next=sentinel
导致崩溃,因为没有为指针赋值,你正在改变指针指向的值。
你可以这样做
sentinel.last=&sentinel;
sentinel.next=&sentinel;
正如其他明确的析构函数调用所指出的那样,这里不需要。