Currently I'm trying to use multiple classes (each with their own .cpp and header .h file) and link them using a main .cpp. I want to make a temporary new video object pointer, pass in the arguments, insert it into the linked list, and delete the temporary pointer. Afterwards, I need to print each individual node of the list.
Currently there are 4 files: main.cpp, vlist.cpp, vlist.h, video.cpp, and video.h
I am using vlist as a way to construct a linked list which gets passed in a video object pointer with the insert function defined in the vlist.cpp file.
The first problem being that I'm not sure I am doing that correctly. At the moment, all I'm doing to be able to pass a video object in another class is by including video.h in the vlist.h file.
The second problem is that I cannot figure out how to properly access the individual video object attributes in each node because my getter functions (defined in video.h) won't work. They seem to return an address rather than a value. However, whenever I try to fix that, it tells me that I cannot use the getter function like this.
My third and final problem is that in vlist.cpp I cannot pass in m_vid when creating a new node but I can pass in m_head just fine. It won't compile if I don't use myVid (a publicly declared video object pointer in vlist.h).
Files below:
main.cpp
#include <iostream>
using namespace std;
#include "vlist.h"
#include "video.h"
int main()
{
//Create temporary video object pointer using Video * temp = new Video(arguments);
//Pass in the temp video pointer to the list and insert it with VList function
string firstLine, secondLine, thirdLine = "";
float fourthLine = 1.1;
int fifthLine = 2;
VList list;
Video * tempVid = new Video(firstLine, secondLine, thirdLine, fourthLine, fifthLine);
list.insert(tempVid);
delete tempVid;
list.print();
return 0;
}
video.cpp
#include "video.h"
#include <iostream>
using namespace std;
Video::Video(string title, string URL, string comment, float length, int rating) {
vidTitle = title;
vidURL = URL;
vidComment = comment;
vidLength = length;
vidRating = rating;
}
void Video::print(Video *myVid) {
cout << myVid->getTitle() << endl;
}
video.h
#ifndef VIDEO_H
#define VIDEO_H
#include <string>
#include <iostream>
using namespace std;
class Video
{
public:
Video(string title, string URL, string comment, float length, int rating);
int getRating() {
return vidRating;
}
float getLength() {
return vidLength;
}
string getTitle() {
return vidTitle;
}
string getURL() {
return vidURL;
}
string getComment() {
return vidComment;
}
void print(Video *myVid);
private:
string vidTitle, vidURL, vidComment, vidPreference;
float vidLength;
int vidRating;
};
#endif
vlist.cpp
#include <iostream>
using namespace std;
#include "vlist.h"
VList::VList() {
m_head = NULL;
}
VList::~VList() {
Node *ptr = m_head;
while (ptr != NULL) {
Node *temp;
temp = ptr;
ptr = ptr->m_next;
delete temp;
}
}
void VList::insert(Video *myVid) {
m_head = new Node(myVid, m_head);
}
void VList::print() {
Node *ptr = m_head;
while (ptr != NULL) {
cout << ptr->m_vid->getTitle();
ptr = ptr->m_next;
}
}
vlist.h
#ifndef VLIST_H
#define VLIST_H
#include "video.h"
class VList
{
public:
VList();
~VList();
void insert(Video *myVid);
void print();
Video *myVid;
private:
class Node
{
public:
Node(Video *myVid, Node *next) {
m_vid = myVid;
m_next = next;
}
Video *m_vid;
Node *m_next;
};
Node *m_head;
};
#endif
The first problem being that I'm not sure I am doing that correctly.
At the moment, all I'm doing to be able to pass a video object in
another class is by including video.h in the vlist.h file.
No, you are not doing it correctly, In the file main.cpp you are creating a pointer to Video(i.e, a Video*) and passing it to void VList::insert(Video *myVid) function and at the next line you are deleting the pointer before printing it. Remember that, when you create pointers and pass it to a method its lifecycle is not managed automatically like a magic, you yourself need to manage the pointers (which is the most common issue beginners face, I too). So there are two fixes to this problem
First Fix
Not deleting the pointer in the main, since it is deleted in the destructor of the VList.
#include <iostream>
using namespace std;
#include "vlist.h"
#include "video.h"
int main()
{
//Create temporary video object pointer using Video * temp = new Video(arguments);
//Pass in the temp video pointer to the list and insert it with VList function
string firstLine, secondLine, thirdLine = "";
float fourthLine = 1.1;
int fifthLine = 2;
VList list;
Video * tempVid = new Video(firstLine, secondLine, thirdLine, fourthLine, fifthLine);
list.insert(tempVid);
// delete tempVid; // don't delete this pointer right here, since I've found that you are deleting the pointer in the destructor of VList
list.print();
return 0;
}
Second Fix
You might like to use something called smart pointers as of C++11 these are standardized! See std::unique_ptr and std::shared_ptr. They will automatically delete the pointers and guarantees no memory leaks.
The second problem is that I cannot figure out how to properly access
the individual video object attributes in each node because my getter
functions (defined in video.h) won't work.
Your second problem is related to first one as your are deleting the pointer before using it which causes undefined behaviour and the output you might be getting is like a garbage. isn't it?
For the sake of simplicity I would recommend using simple Video reference not pointers. Pass them by value and all your problems will evaporate.
To answer my own question and that of anyone who might see this. I just needed to change it around a bit and set a temp object pointer inside of print and cout a get function onto that. It's very late so I apologize if there are any errors. I was indeed getting an address like I thought.
void VList::print() {
Node *ptr = m_head;
while (ptr != NULL) {
Video *tempPtr = ptr->m_vid;
cout << tempPtr->getTitle() << endl;
ptr = ptr->m_next;
}
}
Related
I am attempting to implement a linked list in C++, when I create an object pointer without the keyword new, the code does not display anything. I have done some research and found that by using the new keyword this fixed this issue, but I am not entirely sure as to why this fixes it, I read that the pointer object may go out of scope and this "new" prevents that, but I do not understand how it goes out of scope.
Here is the code:
#include <iostream>
using namespace std;
struct Node{
int val;
};
int main(){
Node *head;
//Node *head = new Node; fixed the issue
head->val = 200;
cout << head->val;
return 0;
}
I would like to save (serialize) an MFC tree control in a dialog and recall it to populate the tree when the dialog is initialized. I thought the way to approach that task would be to first code a program that creates a (preferably) vector representation of the tree, stores it in a text file, and then recreates the tree representation by deserializing from the saved file. I would also prefer to save the nodes as CStrings because that’s how I’m used to saving and reading text from files. However, not only can I not get to first base on this, I can’t even pick up the bat. The following minimal code to create a single node using std::string runs ok.
#include <string>
#include <vector>
// A node of N-ary tree
struct Node {
std::string key;
std::vector<Node*> child; // An array of pointers for children
};
// A utility function to create a new N-ary tree node
Node* newNode(std::string key)
{
Node* temp = new Node;
temp->key = key;
return temp;
}
// A utility function to create a tree
Node* createTree()
{
Node* root = newNode( "Root" );
return root;
}
int main()
{
Node* root = createTree();
return 0;
}
But if I change it to use CString instead,
#include <afx.h>
#include <tchar.h>
#include <vector>
struct Node {
CString key;
std::vector<Node*> child; // An array of pointers for children
};
Node* newNode(CString key)
{
Node* temp = new Node;
temp->key = key;
return temp;
}
Node* createTree()
{
Node* root = newNode( _T("Root") );
return root;
}
…when the program exits it reports a memory leak. Could someone please explain why, and what if anything I can do to correct it?
As the previous answer and comments noted, someone has to free all allocated memory.
When you use new, the responsibility is on you.
However, C++ provides smart pointers that can manage memory allocation and freeing for you; please see https://en.cppreference.com/w/cpp/memory/unique_ptr.
Your sample code will look like this:
#include <atlstr.h>
#include <tchar.h>
#include <vector>
#include <memory>
struct Node {
CString key;
std::vector<std::unique_ptr<Node>> child;
};
std::unique_ptr<Node> newNode(CString key)
{
std::unique_ptr<Node> temp = std::make_unique<Node>();
temp->key = key;
return temp;
}
std::unique_ptr<Node> createTree()
{
std::unique_ptr<Node> root = newNode(_T("Root"));
root->child.push_back(newNode(_T("Child")));
return root;
}
APPENDED per question in the comment:
CString encode(std::unique_ptr<Node>& root)
{
if (root == nullptr)
return _T("");
{
CString sRep = root->key;
for (auto& temp : root->child)
sRep += encode(temp);
return sRep += _T("|");
}
}
It appears you have a memory leak in your original iteration as well (without using CString). You allocate memory on the heap for a new Node in newNode(std::string), but you never call delete on that pointer anywhere.
Simply delete root; somewhere before main() exits to fix this first memory leak.
Next, you'll find that once you populate the vector<Node*> child with pointers, those will need to be deleted somehow as well. I suggest adding a destructor to your struct Node that iterates through vector and explicitly calls delete on each pointer in there.
A note on CString
A quick search about how CString works[1] (because I've never dealt with it before) indicates that when you make a copy of a CString (such as by using the copy assignment operator), a new object isn't created, but a reference counter is incremented in the original CString object. The object is only destroyed once that reference counter reaches zero.
Since you're never calling delete on your Node pointer, the CString object in the Node object is never deleted, and this reference number is never decreased. Calling delete should fix the problem, but please report back whether or not it does.
I'm getting a segmentation fault on this program, and I know it has something to do with a null pointer being dereferenced, but I'm not exactly sure which one is causing the error. I'm just not certain as to how to fix the error while maintaining the purpose of the original program - it will compile, but at runtime I get the segfault I was just talking about.
main:
#include "link.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
link * head_pointer = new link(NULL, NULL) ;
for (int i = 0; i < 10; i++) {
string new_string;
getline(cin, new_string);
string* pointer_to_input = new string(new_string);
link * current_link = new link(head_pointer, pointer_to_input );
head_pointer = current_link;
}
head_pointer -> printAll(*head_pointer);
return 42;
}
link:
#include <string>
#include <iostream>
#include "link.h"
using namespace std;
link::link(link * pointer_to_link, string * pointer_to_string)
{
next = pointer_to_link;
value = pointer_to_string;
}
link::~link() {
delete value;
delete next;
}
link * link::getNext() {
return next;
}
string * link::getString() {
return value;
}
int link::printAll(link link_to_print) {
cout << *link_to_print.getString() << endl;
if (link_to_print.next != NULL) {
return printAll(*link_to_print.getNext());
} else {
return 0;
}
}
Your destructor does look like an error, you shouldn't delete in destructor if you didn't allocate that in constructor:
link::~link() {
}
You should post your link.h to get more detailed explanation.
Without link.h it's not clear what else is wrong, however, there are also other problems:
link::printAll looks like a static method and should be called as: link::printAll(head_pointer);
you printAll should take by pointer, otherwise it it will create a copy of your link and delete it.
printAll has multiple issues as well. Probably it should have been something as follows:
void link::printAll(link *link_to_print)
{
if (!link_to_print)
return;
if (link_to_print->getString())
cout << *link_to_print->getString() << endl;
printAll(link_to_print->next);
}
and your main:
int main()
{
link * head_pointer = new link(NULL, NULL);
for (int i = 0; i < 10; i++) {
string new_string = str;
getline(cin, new_string);
string* pointer_to_input = new string(new_string);
link * current_link = new link(head_pointer, pointer_to_input);
head_pointer = current_link;
}
link::printAll(head_pointer);
return 42;
}
In short to avoid errors you shouldn't store pointers to strings in your link, you should just store strings themselves. Your links perhaps shouldn't assume ownership of other links:
struct link
{
link *next;
string value;
link(link *next, const std::string& value) : next(next), value(value) {}
link * getNext();
const std::string& getString() const;
static void printAll(link *link_to_print);
};
link * link::getNext() {
return next;
}
const string& link::getString() const {
return value;
}
void link::printAll(link *link_to_print)
{
if (!link_to_print)
return;
cout << link_to_print->getString() << endl;
printAll(link_to_print->next);
}
and your main:
int main()
{
link * head_pointer = new link(NULL, "");
for (int i = 0; i < 10; i++) {
string new_string;
getline(cin, new_string);
link * current_link = new link(head_pointer, new_string);
head_pointer = current_link;
}
link::printAll(head_pointer);
// TODO: now you need to walk head_pointer and delete all links manually.
return 42;
}
Once you learn how memory management works in general you should most likely redesign your link using some kind of smart pointer helper class, such as unique_ptr or shared_ptr. And off course, once you master linked list you should start using std::list.
link::printAll takes its argument by value, which has two important effects:
The argument inside the function is a second link object created by making copies of the same value and next pointer.
The copy has automatic storage duration and is destroyed at the end of the function call.
Therefore, you have double frees going on. In particular, both the copy made in the recursive call and the sub-link of the original link share the same value pointer, and both try to delete it. The second deletion causes undefined behavior.
The solution is to respect the rule-of-three and not allow shallow copies of raw pointers. There are two possible approaches for managing objects owned by pointer:
Write a copy constructor to go with your destructor, so the two deletes mentioned above act on two different copies of the value.
OR
Use a smart pointer, such as std::shared_ptr, so you don't have to write a destructor by hand at all.
Note that you need a pointer to implement the connection between objects in the linked list, but you do not need a pointer to store the data. Having a data member of type std::string, instead of std::string *, would be just fine and do the right thing when copied (It makes sense to think of std::string as a smart pointer to an array of characters, that just happens to also have some extra string-manipulation functions tacked on).
I am learning list in C++ independently, and i have searched many websites about it. However, almost every approach to create a list is the same.
They usually create a struct as the node of a class. I want to create a class without using struct. So I created a class name ListNode which contains an int data and a pointer.
The main member functions of my class are AddNode and show.
Although, this program compiles successfully, it still does not work as I wish.
Here is the header file:
#ifndef LISTNODE_H_
#define LISTNODE_H_
#pragma once
class ListNode
{
private:
int data;
ListNode * next;
public:
ListNode();
ListNode(int value);
~ListNode();
void AddNode(ListNode* node,ListNode* headNode);
void show(ListNode* headNode);
};
#endif
Here is the implementation:
#include "ListNode.h"
#include<iostream>
ListNode::ListNode()
{
data = 0;
next = NULL;
}
ListNode::ListNode(int value)
{
data = value;
next = NULL;
}
ListNode::~ListNode()
{
}
void ListNode::AddNode(ListNode* node,ListNode* headNode) {
node->next = headNode;
headNode =node;
}
void ListNode::show(ListNode* headNode) {
ListNode * traversNode;
traversNode = headNode;
while (traversNode != NULL) {
std::cout << traversNode->data << std::endl;
traversNode = traversNode->next;
}
}
Main function:
#include"ListNode.h"
#include<iostream>
int main()
{
using std::cout;
using std::endl;
ListNode* head = new ListNode();
for (int i = 0;i < 3;i++) {
ListNode* Node = new ListNode(i);
head->AddNode(Node, head);
}
head->show(head);
return 0;
}
As far as I am concerned, the output should be
2
1
0
However, the output is a single zero. There must be something wrong in the AddNode and show function.
Could you please tell me what is wrong with these two functions?
When you call head->AddNode(node, head) you´re passing the memory directions which the pointers point, when the function arguments receive those directions, they are now pointing to the same directions, but those are another pointers, no the ones you declared in main. You could see it like this:
void ListNode::AddNode(ListNode* node,ListNode* headNode) {
/*when the arguments get their value it could be seen as something like:
node = Node(the one from main)
headNode = head(the one from main)*/
node->next = headNode;
/*Here you are modifying the new inserted node, no problem*/
headNode = node;
/*The problem is here, you´re modifying the memory direction
headNode points to, but the headNode argument of the function, no the one declared in main*/
}
So the pointer head in main() always points to the same first node you also declared in main().
In order to fix this you should change your code this way:
void ListNode::AddNode(ListNode* node,ListNode** headNode) {
/* second paramater now receives a pointer to apointer to a node */
node->next = *headNode;//the same as before but due to pointer syntaxis changes a bit
*headNode = node;//now you change the real head
}
And when you call it:
head->AddNode(Node, &head);//you use '&' before head
Now the real head, no the one in the function, will point to the last node you inserted.
This realization of linked list is broken. Address of nodes[0].next doesn't match the nodes[1] address. So nodes[1].next is NULL (as default value). I added some address printing to the search method. It looks like the nodes[1] wasn't initialized?
#include <iostream>
#include <vector>
using namespace std;
typedef struct Node_T {
int data;
Node_T *next;
} Node;
class LinkedList{
public:
vector<Node> nodes;
LinkedList(){
}
void insert(int data) {
Node temp_node;
temp_node.data = data;
temp_node.next = NULL;
size_t len = nodes.size();
nodes.push_back(temp_node);
if (len > 0) {
nodes[len - 1].next = &nodes[len];
}
}
int search(int val){
if (nodes.empty())
return -1;
Node *node_ptr = &nodes[0];
// Debug
cout << &nodes[1] << "\n";
cout << &nodes[0].next << "\n";
int i = 0;
do {
if (node_ptr->data == val) return i;
i++;
} while((node_ptr = node_ptr->next) != NULL);
return -1;
}
};
int main()
{
LinkedList llist;
llist.insert(1);
llist.insert(2);
llist.insert(3);
llist.insert(4);
llist.insert(5);
cout << llist.search(3) << "\n";
return 0;
}
It shows me: 0x8e6a060 0x8e6a05c -1
When you add elements to a vector, references to (and hence addresses of) vector elements are invalidated. You must therefore not use values such as &nodes[0] or &nodes[len], as they are meaningless.
The point with an exercise like this is to get the hang of the internal structure in a linked list. You have replaced that internal structure with a vector<Node>.
Instead of a vector, the idea is to have a
private:
Node* head;
As you data member.
In your insert function you are supposed to dynamically allocate memory for the Node with
Node* newNodePointer = new Node;
And manipulate the pointer with next and such.
It is worth to point out, that this is fine as an exercise, but your "real" code should use standard library facilities.
First, Your printout is incorrect: this line
cout << &nodes[0].next << "\n";
prints the address of next, rather than printing the next itself. Changing to
cout << nodes[0].next << "\n";
gives the correct printout (demo).
However, the main issue is that you keep pointers to elements of std::vector. These become invalid after the first write, because new storage gets allocated for the growing vector.
You can certainly work around this by reserving sufficient space upfront (call nodes.reserve(1000) from the constructor of your list; demo) but that is merely a hack: you should use new and delete to allocate elements of your linked list manually. That is the whole point of this exercise.
But I still need a container to ensure that nodes will be live as expected?
No, you do not. Your class is a container. By referencing the whole chain of nodes from the head pointer it can ensure that the entire chain is kept "live".