Antwort und Beispiellösung
Content |
Antwort zur Frage
Die Zuweisung old->next = nullptr; ist notwendig, weil sonst rekursiv die gesamte Liste freigegeben wird. Wir wollen aber nur das ganz oben liegende Objekt des Typs IntegerMember freigeben.
Beispiellösung
#include <cassert> #include <iostream> class IntegerMember { public: IntegerMember(int member, IntegerMember* next) : member(member), next(next) { } IntegerMember(const IntegerMember& other) : member(other.member), next( other.next? new IntegerMember(*other.next) : nullptr ) { } ~IntegerMember() { delete next; } IntegerMember& operator=(const IntegerMember& other) { delete next; member = other.member; if (other.next) { next = new IntegerMember(*other.next); } else { next = nullptr; } return *this; } int member; IntegerMember* next; }; class IntegerStack { public: IntegerStack() : top(nullptr) { } IntegerStack(const IntegerStack& other) : top( other.top? new IntegerMember(*other.top) : nullptr ) { } ~IntegerStack() { delete top; } IntegerStack& operator=(const IntegerStack& other) { delete top; if (other.top) { top = new IntegerMember(*other.top); } else { top = nullptr; } return *this; } void push(int member) { top = new IntegerMember(member, top); } bool empty() { return top == nullptr; } int pop() { assert(top != nullptr); int member = top->member; IntegerMember* old = top; top = top->next; old->next = nullptr; delete old; return member; } private: IntegerMember* top; }; void print(const char* name, IntegerStack& s) { std::cout << name << ":"; while (!s.empty()) { std::cout << " " << s.pop(); } std::cout << std::endl; } int main() { IntegerStack a; a.push(1); a.push(2); a.push(3); { IntegerStack b{a}; print("b", b); } { IntegerStack c; c = a; print("c", c); } print("a", a); }
heim$ diff -U 2 simple-stack2.cpp simple-stack-fixed.cpp --- simple-stack2.cpp 2018-05-03 14:51:49.807947596 +0200 +++ simple-stack-fixed.cpp 2018-05-07 10:03:50.277913512 +0200 @@ -7,7 +7,26 @@ member(member), next(next) { } + IntegerMember(const IntegerMember& other) : + member(other.member), + next( + other.next? + new IntegerMember(*other.next) + : + nullptr + ) { + } ~IntegerMember() { delete next; } + IntegerMember& operator=(const IntegerMember& other) { + delete next; + member = other.member; + if (other.next) { + next = new IntegerMember(*other.next); + } else { + next = nullptr; + } + return *this; + } int member; IntegerMember* next; @@ -18,7 +37,24 @@ IntegerStack() : top(nullptr) { } + IntegerStack(const IntegerStack& other) : + top( + other.top? + new IntegerMember(*other.top) + : + nullptr + ) { + } ~IntegerStack() { delete top; } + IntegerStack& operator=(const IntegerStack& other) { + delete top; + if (other.top) { + top = new IntegerMember(*other.top); + } else { + top = nullptr; + } + return *this; + } void push(int member) { top = new IntegerMember(member, top); heim$ g++-7.2 -Wall -o simple-stack-fixed simple-stack-fixed.cpp heim$ valgrind ./simple-stack-fixed ==15236== Memcheck, a memory error detector ==15236== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==15236== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info ==15236== Command: ./simple-stack-fixed ==15236== b: 3 2 1 c: 3 2 1 a: 3 2 1 ==15236== ==15236== HEAP SUMMARY: ==15236== in use at exit: 72,704 bytes in 1 blocks ==15236== total heap usage: 10 allocs, 9 frees, 72,848 bytes allocated ==15236== ==15236== LEAK SUMMARY: ==15236== definitely lost: 0 bytes in 0 blocks ==15236== indirectly lost: 0 bytes in 0 blocks ==15236== possibly lost: 0 bytes in 0 blocks ==15236== still reachable: 72,704 bytes in 1 blocks ==15236== suppressed: 0 bytes in 0 blocks ==15236== Rerun with --leak-check=full to see details of leaked memory ==15236== ==15236== For counts of detected and suppressed errors, rerun with: -v ==15236== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) heim$