Understanding String as member of class and linked list

I know this topic is not a new one since I've been stumped by it before, but I'm still having trouble understanding exactly what the issue is and what to do to resolve it. I'm going to try to describe the problem instead of posting all the necessary code. The goal of this project is to write a Linked List with a search function.

First I created a simple Employee class, two data items, one int and one string. Second I created a Node class that has one data member, an Employee object. After populating an instance of the Employee class, it is placed in a Node which is added to my Linked List. Everything is good to this point. I can see the Nodes on the list and can see the data in the Employee objects in the Nodes.

Now to my search function. The function should search the list, looking for matching Employee information and then return a pointer to the Employee object. It does find the correct node, but here's the problem. When I get that pointer back, the Employee object has good data in the int member but nothing in the string data.

From past investigations of a similar problem, I know that this has to do with the nature of strings and how they are stored but I can't seem to wrap my head around the "Why?" and how to properly fix this problem.

Any help/instruction/education on this would be greatly appreciated.

Life Long Programming Learner

Post the definition of Node and the code that copies the Employee into the Node.
Code for Employee, Node and LinkedList are below. Everything seems to work find except the returned object from the findData function, near the bottom of the LinkeList class. Lastly is the code from main where I use the above classes, insert data and then search for it.

Employee Class
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
#pragma once
#include <iostream>
using namespace std;

class Employee
{
public:
	string mName;
	int mEmpId;

public:
	Employee()
	{
		//default constructor
	}
	~Employee()
	{
		//default deconstructor
	}
	void setName(string name)
	{
		mName = name;
	}
	void setID(int id)
	{
		mEmpId = id;
	}

	bool operator<(const Employee other)
	{
		if (this->mEmpId < other.mEmpId)
			return true;
		else
			return false;
	}

	bool operator>(const Employee other)
	{
		if (this->mEmpId > other.mEmpId)
			return true;
		else
			return false;
	}

	bool operator==(const Employee other)
	{
		if (this->mEmpId == other.mEmpId)
			return true;
		else
			return false;
	}

	friend ostream& operator<<(ostream& os, const Employee emp)
	{
		os << emp.mName << ": " << emp.mEmpId << endl;
		return os;
	}

};


Node Class
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
#pragma once
template <class T>
class NodeT
{
private:
	T mData;
	NodeT<T>* mNext = nullptr;

public:

	template <typename> friend class LLT;


	NodeT()
	{
		mData = 0;
		mNext = nullptr;
	}

	~NodeT()
	{}

	NodeT(T data)
	{
		mData = data;
	}

	NodeT(int data, NodeT* next)
	{
		mData = data;
		mNext = next;
	}

};


Linked List Class
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
193
194
195
196
197
198
199
200
#pragma once

#include "NodeT.h"
#include <iostream>
using namespace std;

template <class T>
class LLT
{
private:
	NodeT<T>* mHead;
	NodeT<T>* mTail;
public:

	LLT()
	{
		mHead = nullptr;
		mTail = nullptr;
	}

	~LLT()
	{}

	void addToHead(T data)
	{
		if (isEmpty())
		{
			mHead = new NodeT<T>(data);
			mTail = mHead;
		}
		else
		{
			NodeT<T>* tmp = new NodeT<T>(data);
			tmp->mNext = mHead;
			mHead = tmp;
		}
	}

	void removeFromHead()
	{
		NodeT<T>* tmp = mHead;
		mHead = mHead->mNext;
		delete tmp;
	}

	T getHeadData()
	{
		return mHead->mData;
	}

	void addToTail(T data)
	{
		if (isEmpty())
		{
			mHead = new NodeT<T>(data);
			mTail = mHead;
		}
		else
		{
			NodeT<T>* tmp = mTail;
			mTail = new NodeT<T>(data);
			tmp->mNext = mTail;
		}
	}

	void insertData(T data)
	{
		if (isEmpty())
		{
			mHead = new NodeT<T>(data);
			mTail = mHead;
		}
		else if (data < mHead->mData)
		{
			addToHead(data);
		}
		else if (data > mTail->mData)
		{
			addToTail(data);
		}
		else
		{
			NodeT<T>* tmp = mHead;					//start at head
			while (data > tmp->mNext->mData)	//compare data to next data
			{
				tmp = tmp->mNext;				//move to next node
			}
			NodeT<T>* newNode = new NodeT<T>(data);		//create new node
			newNode->mNext = tmp->mNext;		//new node point to orig. next
			tmp->mNext = newNode;				//prev node point to new node
		}
	}

	void deleteData(T data)
	{
		if (data < mHead->mData || data > mTail->mData)
		{
			//do nothing
		}
		else
		{
			NodeT<T>* tmp = mHead;
			while (data != tmp->mNext->mData)
			{
				tmp = tmp->mNext;
			}
			tmp->mNext = tmp->mNext->mNext;
		}
	}

	void displayList()
	{
		cout << endl << "List Contents" << endl;
		NodeT<T>* tmp = mHead;
		while (tmp != nullptr)
		{
			cout << tmp->mData << endl;
			tmp = tmp->mNext;
		}
	}

	NodeT<T>* findNTL()
	{
		NodeT<T>* tmp = mHead;
		NodeT<T>* prev = nullptr;

		while (tmp != nullptr)
		{
			if (tmp->mNext != nullptr)
			{
				prev = tmp;
			}
			tmp = tmp->mNext;
		}
		return prev;
	}


	void removeFromTail()
	{
		NodeT<T>* prev = findNTL();
		NodeT<T>* tmp = mTail;		//save pointer to original tail so it can be deleted
		mTail = prev;
		prev->mNext = nullptr;
		delete tmp;
	}



	T* findData(NodeT<T>* tmp, T data)
		//Call with nullptr to start at Head
		//Uses recursion
	{
		if (tmp == nullptr)
			tmp = mHead;

		if (tmp->mData == data)
		{
			return &tmp->mData;
		}
		else
		{
			if (tmp->mNext == nullptr)
			{
				T notFound;
				return notFound;
			}
			else
			{
				findData(tmp->mNext, data); //recursive call
			}
		}
	}


	bool isEmpty()
	{
		if (mHead == nullptr && mTail == nullptr)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	bool isOneNodeT()
	{
		if (mHead == mTail && mHead != nullptr)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

};


Main code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main()
{
	LLT<Employee> empList;

	Employee emp1;
	emp1.mName = "Joe";
	emp1.mEmpId = 111;

	empList.insertData(emp1);

	Employee empID;
	empID.mEmpId = 222;

	Employee* tmpEmp = empList.findData(nullptr, empID);

//this is where the problem occurs, the pointer to the returned Employee object has the int value but the string value is empty
}
Last edited on
1. On line 170 you're discarding the result of the recursive call and then the function continues down a code path without a return statement.
2. I honestly don't understand why the compiler is not giving you an error. On line 166 you're returning a T from a function that returns a T *.
3. This isn't as important, but NodeT appears to assume that T = int in a couple places.
- removeFromHead() should set tail if it removes the last item.
- insertData() will fail if the new item is a duplicate of the head or tail.
- deleteData() will crash if the list is empty or has 1 item. Also it needs to set head and/or tail.
- removeFromTail() will crash if the list has 0 or 1 item. Also it needs to set head if it removes the last item.

Since mHead == nullptr is a boolean expression. isEmpty() could simply be
1
2
3
4
bool isEmpty()
{
     return mHead != nullptr;
}

The same sort of thing applies to the comparison operators.

You can simplify the code a lot by creating a special "find" method:
1
2
3
4
    // Find T in the list, or find where it should go.
    // Return a reference to the pointer to that place
    // (mHead, or some node's next pointer).
    NodeT < T > * &find(const T &data)


With this, insertData(), deleteData and findData() become very simple.
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#include <iostream>
using namespace std;

class Employee
{
  public:
    string mName;
    int mEmpId;

  public:
      Employee()
    {
	//default constructor
    }
    Employee(const string &name, int id) : mName(name), mEmpId(id) {}
    
     ~Employee()
    {
	//default deconstructor
    }
    void setName(string name)
    {
	mName = name;
    }
    void setID(int id)
    {
	mEmpId = id;
    }

    bool operator<(const Employee other)
    {
	return (this->mEmpId < other.mEmpId);
    }

    bool operator>(const Employee other)
    {
	return (this->mEmpId > other.mEmpId);
    }

    bool operator==(const Employee other)
    {
	return (this->mEmpId == other.mEmpId);
    }

    friend ostream & operator<<(ostream & os, const Employee emp)
    {
	os << emp.mName << ": " << emp.mEmpId << endl;
	return os;
    }

};

template < class T > class NodeT {
  private:
    T mData;
    NodeT < T > *mNext = nullptr;

  public:

    template < typename > friend class LLT;

    NodeT() {
	mData = 0;
	mNext = nullptr;
    }

    ~NodeT() {
    }

    NodeT(T data) {
	mData = data;
    }

    NodeT(int data, NodeT * next) {
	mData = data;
	mNext = next;
    }

};

#include <iostream>

template < class T > class LLT {
private:
    NodeT < T > *mHead;
    NodeT < T > *mTail;

    // Find T in the list, or find where it should go.
    // Return a reference to the pointer to that place
    // (mHead, or some node's next pointer).
    NodeT < T > * &find(const T &data)
    {
	NodeT<T> **pp, *p;
	for (pp = &mHead; (p = *pp); pp = &p->mNext) {
	    if (! (p->mData < data)) break;
	}
	return *pp;
    }

    
public:

    LLT() {
	mHead = nullptr;
	mTail = nullptr;
    }

    ~LLT() {
    }

    void addToHead(T data)
    {
	if (isEmpty()) {
	    mHead = mTail = new NodeT < T > (data);
	} else {
	    NodeT < T > *tmp = new NodeT < T > (data);
	    tmp->mNext = mHead;
	    mHead = tmp;
	}
    }

    void removeFromHead()
    {
	NodeT < T > *tmp = mHead;
	mHead = mHead->mNext;
	if (mHead == nullptr) mTail = nullptr;
	delete tmp;
    }

    // list must not be empty
    T getHeadData()
    {
	return mHead->mData;
    }

    void addToTail(T data)
    {
	if (isEmpty()) {
	    mHead = mTail = new NodeT < T > (data);
	} else {
	    NodeT < T > *tmp = mTail;
	    mTail = new NodeT < T > (data);
	    tmp->mNext = mTail;
	}
    }

    void insertData(T data)
    {
	NodeT < T > *newNode = new NodeT < T > (data);
	NodeT<T> * &pos = find(data);

	// Link it in
	newNode->mNext = pos;
	pos = newNode;
	
	// If it went at the beginning then pos pointed to mHead
	// so mHead was updated, but if it's the new tail, then
	// you have update mtail.
	if (mTail == nullptr || &mTail->mNext == &pos) {
	    mTail = newNode;
	}
    }

    void deleteData(T data)
    {
	NodeT<T> * &pos = find(data);
	if (pos == nullptr) return; // not found

	if (pos == mTail) {
	    mTail = findNTL();
	}
	NodeT<T> *tmp = pos;
	pos = pos->mNext;
	delete tmp;
    }

    void displayList()
    {
	cout << endl << "List Contents" << endl;
	for (NodeT < T > *tmp = mHead; tmp; tmp = tmp->mNext) {
	    cout << tmp->mData << endl;
	}
    }

    // Find next to last item.
    NodeT < T > *findNTL() {

	NodeT < T > *prev = nullptr;

	for (NodeT < T > *tmp = mHead; tmp != nullptr; tmp = tmp->mNext) {
	    if (tmp->mNext != nullptr) {
		prev = tmp;
	    }
	}
	return prev;
    }

    void removeFromTail()
    {
	if (mTail) {
	    deleteData(mTail->data);
	}
    }

    T *findData(T data)
    {
	NodeT<T> * &pos = find(data);
	return (pos ? &pos->mData : nullptr);
    }

    bool isEmpty()
    {
	return (mHead == nullptr);
    }

    bool isOneNodeT()
    {
	return (mHead == mTail && mHead != nullptr);
    }

};

int
main()
{
    LLT < Employee > empList;

    Employee emp1("Joe", 111);
    empList.insertData(emp1);

    Employee emp2("Jane", 222);
    empList.insertData(emp2);
    Employee dummy;
    dummy.mEmpId = 222;
    empList.displayList();

    Employee *tmpEmp = empList.findData(dummy);
    if (tmpEmp) {
	cout << *tmpEmp << '\n';
    } else {
	cout << "Not found\n";
    }
}
Thanks for all the feedback, some suggestions for good improvements to my code, but I don't think anyone answered my original question.

At lines 150-170 in my LinkedList class, the function returns a pointer to the T data type. The problem is that when T is an Employee object, the returned pointer points to an Employee object that does in fact have the correct int data but the string member is empty or points to a string that is no longer available.

Given all the other great suggestions for my code, can someone provide me with some insight into why this happens and what do do about it?

Regards,

CORRECTION to my problem statement:

The Employee object that is returned by pointer from the findData function contains no valid data. I'm sure that this is somehow related to the object being deleted but I can't seem to identify why/where that happens.
FOUND MY PROBLEM

helios (above) hit the nail on the head

It's a simple oversight in my code:

1. On line 170 you're discarding the result of the recursive call and then the function continues down a code path without a return statement.

I failed to have the recursive call to findData return the pointer to the object.

Thanks again for all the suggestions. I will definitely make some of those changes.


Topic archived. No new replies allowed.