Doubly Linked List Copy Constructor and Overloading = operator

We were given Student class and Student Roll class (header files included below) The only things I cant figure out so far are how to overload the = operator for StudentRoll, and the copy constructor for studentRoll. I know that copy and swap would probably be the best thing to use here but this is for a school assignment and it has to pass very specific tests with only these functions. Heres what I have, any suggestions are appreciated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//Student.h 
#ifndef STUDENT_H
#define STUDENT_H

#include <string>

class Student {

 public:
  Student(const char * const name, int perm);

  int getPerm() const;
  const char * const getName() const;

  void setPerm(const int perm);
  void setName(const char * const name);

  Student(const Student &orig);
  ~Student();
  Student & operator=(const Student &right);

  std::string toString() const;

 private:
  int perm;
  char *name; // allocated on heap                                                                                                                                                   

};


#endif


//studentRoll.h
#ifndef STUDENTROLL_H
#define STUDENTROLL_H

#include <string>
#include "student.h"

class StudentRoll {

 public:
  StudentRoll();
  void insertAtTail(const Student &s);
  std::string toString() const;

  StudentRoll(const StudentRoll &orig);
  ~StudentRoll();
  StudentRoll & operator=(const StudentRoll &right);

 private:
  struct Node {
    Student *s;
    Node *next;
  };
  Node *head;
  Node *tail;
};


#endif

//studentRoll.cpp
#include <string>
#include "studentRoll.h"
#include <sstream>
#include <iostream>
StudentRoll::StudentRoll() {
  head = tail = NULL;
}

void StudentRoll::insertAtTail(const Student &s) {
    /*  tail -> next = new Node;
    tail = tail -> next;
    tail -> s = s;
    tail -> next = null;

    */
    Node* n = new Node;
    n -> s = new Student(s);

    n -> next = NULL;

    if (head == NULL){
        head = n;
    }
    if(tail != NULL){
        tail -> next = n;
    }
    n -> next = NULL;
    tail = n;
}

std::string StudentRoll::toString() const {
    std::ostringstream oss;
    oss << "[";
    Node *p = head;
    while (p){
        oss << p -> s -> toString();
        if(p != tail) oss << ",";
        p = p -> next;
    }
    oss << "]";
    delete p;
    return oss.str();

}

StudentRoll::StudentRoll(const StudentRoll &orig) {
    Node *p1 = 0;
    Node *p2 = 0;

    if (orig.head == NULL){
        head = NULL;
        tail = NULL;
    }
    else{

        head = new Node;
        head -> s = new Student(*(orig.head -> s));
        std::cout << "got here";
        p1 = head;
        p2 = orig.head -> next;
    }
    while(p2){
        p1 -> next = new Node;
        p1 = p1 -> next;
        p1 -> s = new Student(*(orig.head -> s));

        p2 = p2 -> next;
    }
    p1 -> next = NULL;
    tail = p1;
    delete p1;
    delete p2;

}

StudentRoll::~StudentRoll() {
    Node *current = head;
    while ( current != 0 ){
        Node* next =  current->next;
        delete current;
        current = next;
    }

}


StudentRoll & StudentRoll::operator =(const StudentRoll &right ) {
  // The next two lines are standard, and you should keep them.
  // They avoid problems with self-assignment where you might free up 
  // memory before you copy from it.  (e.g. x = x)

  if (&right == this) 
    return (*this);

  // TODO... Here is where there is code missing that you need to 
  // fill in...

        Node *p1 = 0;
        Node *p2 = 0;

        if (right.head == NULL){
                head = NULL;
                tail = NULL;
        }
        else{

                head = new Node;
                head -> s = new Student(*(right.head -> s));

                p1 = head;
                p2 = right.head -> next;
        }
        while(p2){
                p1 -> next = new Node;
                p1 = p1 -> next;
                p1 -> s = new Student(*(p2 -> s));

                p2 = p2 -> next;
        }
        p1 -> next = NULL;
        tail = p1;
        delete p1;
        delete p2;  
  // KEEP THE CODE BELOW THIS LINE
  // Overloaded = should end with this line, despite what the textbook says.
  return (*this); 

}
Regarding your copy constructor, you have an insert_at_tail, so just iterate the argument and insert the elements in the list:
1
2
3
4
StudentRoll::StudentRoll(const StudentRoll &orig) {
  for(Node* n = orig.head; n; n = n->next) 
    insertAtTail(*n);
}


Copy assignment operators usually follow one of a few patterns, and so are a little easier to write, if more complicated.

See this, and the posts linked from it:
http://www.cplusplus.com/forum/beginner/215692/#msg1001849

There are two steps to an assignment operator:
1.) Release the old resources; then
2.) Copy the new ones from the right-hand-side.
You release old resources in the destructor, and copy resources in the copy constructor. Don't duplicate code -- you can call them:
1
2
3
4
5
6
StudentRoll& operator=(StudentRoll const& rhs) {
  if (std::addressof(rhs) == this) return *this; // careful for self-assignment
  ~StudentRoll(); // release old resources 
  new(this) StudentRoll(rhs); // storage reuse -- copy from rhs
  return *this;
}


Preferably, copy-and-swap provides a strong exception guarantee by doing things in this order:
1.) Make a temporary copy of the new resources (which might throw)
2.) Switch your old resources with the temporary copy (which must not throw)
3.) Destroy the temporary copy of your old resources.
1
2
3
4
StudentRoll& operator=(StudentRoll rhs) { 
  swap(*this, rhs);
  return *this;
}
Last edited on
In regards to calling the destructor in the assignment operator, I was under the impression that you can't explicitly call a destructor.
Also for the copy constructor it is complaining and telling me no known conversion from argument Student* to const Student&. Thanks for the help!
You can explicitly call a destructor, although the uses for it are relatively rare -- usually it appears in around uses of placement-new, e.g., in the case of storage reuse like here, or wrapped in allocators. Regardless, the code I posted literally comes from an example in the standard document itself - from [basic.life]:
http://eel.is/c++draft/basic.life#8

no known conversion from argument Student* to const Student&

It looks like this part won't compile
1
2
  for(Node* n = orig.head; n; n = n->next) 
    insertAtTail(*n);

although hopefully you got the idea.
It's difficult for me to produce working code without enough of yours to compile. Still, I don't see where that error is coming from.
Last edited on
Topic archived. No new replies allowed.