Peculiar code behavior!

Jan 11, 2016 at 3:21am
Hey guys! I have two versions to represent the node of a linked list: a struct template and a class template, as shown below:

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
template<class elemType>
struct nodeType                           
{
    elemType info;
    nodeType<elemType> *link;
};



template<class elemType>
class nodeType
{
public:
	const nodeType<elemType>& operator=(const nodeType<elemType>&);
        void setInfo(const elemType& elem);
	elemType getInfo() const;
	void setLink(nodeType<elemType>* ptr);
	nodeType<elemType>* getLink() const;
	nodeType();
	nodeType(const elemType& elem, nodeType<elemType>* ptr);
	nodeType(const nodeType<elemType>& otherNode);
	~nodeType();
	
private:
	elemType info;
	nodeType<elemType>* link;
};



The definitions of the member functions of class nodeType are shown below:

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
template<class elemType>
const nodeType<elemType>& nodeType<elemType>::operator=(const nodeType<elemType>& otherNode)
{
	if (this != &otherNode)
	{
		if (link != NULL)
		    delete link;
		    
		info = otherNode.info;    
		link = new nodeType<elemType>; 
		link = otherNode.link;		
	}  
	return *this;
}


template<class elemType>
nodeType<elemType>::nodeType(const nodeType<elemType>& otherNode)
{
	if (link != NULL)
        delete link;
		    
	info = otherNode.info;    
	link = new nodeType<elemType>; 
	link = otherNode.link;		
}



template<class elemType>
nodeType<elemType>::~nodeType()
{
	delete link;
}


template<class elemType>
nodeType<elemType>::nodeType(const elemType& elem, nodeType<elemType>* ptr)
{
    info = elem;
    link = ptr;
}



template<class elemType>  
nodeType<elemType>::nodeType()
{
    info = elemType();
    link = NULL;
}


Then I have a class template linkedListType that uses either of the nodeType definitions above. The linkedListType is shown below, along with the definition of the pertinent function related to my question.

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
template<class elemType>   
class linkedListType
{
public:
    const linkedListType<elemType>& operator=(const linkedListType<elemType>&);      //overloading the assignment operator
    void initializeList();
    bool isEmptyList();
    void print();          //note the absence of const; although DSUC++ has a terminating const
    int length() const;	   //PATAD has no terminating const
    void destroyList();
    elemType front() const;
    elemType back() const;
    void insertLast(const elemType& newItem);   
    linkedListType();                                                  //default constructor   
    linkedListType(const linkedListType<elemType>& otherList);         //copy constructor
    ~linkedListType();    					       //destructor  
    const elemType& kthElement(int k); 								  
    void deleteKthElement(int k); 									 
    void divideMid(linkedListType<elemType>& sublist);    			   
    void divideAt(linkedListType<elemType>& sublist, elemType item);  
	     
protected:                                //declared protected to allow any d.c.(s) access to these pointers
    int count;                            //variable to store the nuber of elements in the list
    nodeType<elemType>* first;            //pointer (d.m.) to the first node of the list; 
    nodeType<elemType>* last;             //pointer (d.m.) to the last node of the list

private:
	void copyList(const linkedListType<elemType>& otherList);		
};



I have included below the definition of function linkedListType::insertLast(...) below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template<class elemType>
void linkedListType<elemType>::insertLast(const elemType& newItem)
{
     nodeType<elemType> *newNode;             //pointer to create the new node
     
     newNode = new nodeType<elemType>;        //create the new node               
     newNode->setInfo(newItem);               //store the new item in the node
     newNode->setLink(NULL);                  //set the link field of newNode to NULL
         
     if (this->first == NULL)                 //if the list is empty, ...
     {    
         this->first = newNode;               //...newNode is both the first and 
         this->last = newNode;                //...last node
         this->count++;						      //increment count
     }
     else               //the list is not empty, insert newNode after last
     {
         this->last->setLink(newNode);          //insert newNode after last
         this->last = newNode;                //make last point to the actual last node in the list
         this->count++;						      //increment count
     }
}



Finally, here is the main() function:

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
	linkedListType<int> myList; 


    myList.insertLast(11);
    //myList.insertLast(49);

	// wait until user is ready before terminating program
    system("PAUSE");
    return 0;
}



With the background above, here is my question:

Why is it that if the insertLast statement in line 6 of main() is the only statement, the program exits(or ends) properly; but as soon as I added the second insertLast statement of line 7 of main(), the program hangs up or doesn't exit(or end) properly?? It runs but just does not exit or terminate properly.

By the way, the above curious observation only affects the class version of nodeType; the struct version works fine!
Last edited on Jan 11, 2016 at 3:42am
Jan 11, 2016 at 5:47am
http://www.eelis.net/iso-c++/testcase.xhtml
Make sure your testcase is self-contained and actually reproduces the problem


> the program hangs up
interrupt it and perform a backtrace.

> only affects the class version of nodeType; the struct version works fine!
well, they are different.
The class one takes ownership of the next cell.

Also
1
2
3
4
5
6
7
8
9
10
template<class elemType>
nodeType<elemType>::nodeType(const nodeType<elemType>& otherNode)
{
	if (link != NULL) //you are in the constructor, `link' was never initialized so UB
        delete link;
		    
	info = otherNode.info;    
	link = new nodeType<elemType>; 
	link = otherNode.link;		
}



> DSUC++ has a terminating const
> PATAD has no terminating const
> pointer (d.m.)
Please, DEA
Last edited on Jan 11, 2016 at 5:47am
Jan 11, 2016 at 11:23am
Thanks ne555 for your response. Let me address some of the points you highlighted.

> Make sure your testcase is self-contained and actually reproduces the problem
In my OP, I was trying not to repel potential responders with a rather vast source code; that was why I included just the relevant portions that directly applied to my problem/question. I concede that you would not be able to exactly reproduce the problem I am having.

> interrupt it and perform a backtrace.
By a backtrace, do you mean troubleshooting or debugging? If yes, what tool can I use to debug a program? If no, what is a backtrace?

> The class one takes ownership of the next cell.
What do you mean by this please?

> you are in the constructor, `link' was never initialized so UB
What I'm doing here is to ensure that before the copy is performed, the pointer of the destination object is NULLed. I think this requirement is a standard segment of copy constructors I've seen. What does UB stand for?

The final three lines you quoted are comments that are merely personal referencing jargons/acronyms. By the way, what does DEA stand for??
Jan 11, 2016 at 5:48pm
> that was why I included just the relevant portions
http://www.eelis.net/iso-c++/testcase.xhtml
A testcase consisting of randomly copy&paste'd bits of code that you thought were relevant can obviously not reproduce the problem. Worse, such testcases force your perception of the problem upon us, preventing us from taking a fresh look at it and making an unbiased analysis.


It's good that you try to simplify the code, but you need to make sure that we can compile it nad run it and have the same errors as you.

> what tool can I use to debug a program?
a debugger.
By instance gdb, your IDE probably provides a nice interface to it.
When your program hangs you interrupt it (<C-c>) and it will show you the line that was executing.

You may also consider using valgrind to detect bad memory access.

> what is a backtrace?
http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_node/gdb_42.html
A backtrace is a summary of how your program got where it is.
the functions called and their arguments.

>> The class one takes ownership of the next cell.
> What do you mean by this please?
in the class' code, when a cell dies its neighbour is killed.
1
2
3
4
nodeType<elemType>::~nodeType()
{
	delete link;
}

that doesn't happen with the struct.


> ensure that before the copy is performed, the pointer of the destination object is NULLed
> I think this requirement is a standard segment of copy constructors I've seen.
The purpose of the constructor is to initialize its members, so the members are uninitialized there.
`link' points to garbage there, it cannot possible point to newly allocated memory. It isn't sure that it points to NULL either.

Perhaps you are confused with the assignment operator.


UB: undefined behaviour. You've got a logic error and the worst part is that your program may not crash, and even give the correct answer.
DEA: Don't Ever Abbreviate
Jan 11, 2016 at 11:48pm
Thx ne555; I really appreciate your answers/explanations to the questions/issues I raised! I'll take action on your suggestions and respond shortly.
Last edited on Jan 11, 2016 at 11:53pm
Topic archived. No new replies allowed.