0

I am new to C++ and have been studying data structures lately. I have created linked lists in the following way:

class Random{
private:
   struct Node{
          int data;
          Node* next;
         };
} 

But I came across a piece of code that is doing the same thing in the following way:

template<Typename T>
struct Listnode {
      T data;
      shared_ptr<ListNode<T>> next;
};

I looked it up and found that we use templates when we want to have multiple data types. Like now we can use int, double, float instead of "T". Whereas in the former case we could only use int. However, I don't understand how:

Node* next

is the same as:

shared_ptr<ListNode<T>> next

and how will these be called, I know for the former we use the:

Node->next = new Node;
Node->data = randomdata;

How does it work for the former way. Another thing of the two implementations, which one is better and why?

sfjac
  • 7,119
  • 5
  • 45
  • 69
Muneeb Rehman
  • 135
  • 2
  • 10
  • It's not the same. A `shared_ptr` is not a plain pointer. It's an another change unrelated to making it a template. – Karoly Horvath Nov 03 '15 at 15:38
  • first of all you have a typo at `shared_ptr>`, it should've been `shared_ptr >` (pay attention to the space at `> >`), second, a shared pointer is a pointer to the specified type(with additional cool features)!! so you only have to change the name to `Node` if this bothers you.. – ThunderWiring Nov 03 '15 at 15:38
  • 3
    @ThunderWiring: you don't need that extra space since C+11. – Karoly Horvath Nov 03 '15 at 15:39
  • @Karoly Horvath actually I didn't check it in c++11, but is it considered a syntax error to add the space?! – ThunderWiring Nov 03 '15 at 15:45
  • @ThunderWiring: Of course not, you can litter you C++ code with as many whitespaces as you want. – Karoly Horvath Nov 03 '15 at 15:53
  • Possible duplicate of [What is a smart pointer and when should I use one?](http://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use-one) – Jonathan H Nov 03 '15 at 23:14

4 Answers4

1

The T* ptr; form is the basic method declaring a pointer to a memory holding a value of type T. This kind of pointer is initialized either by the base adress of an array T[], by new T(), by new T[] or something else.

As you can see by now there are many ways to allocate the memory the pointer is pointing to. This is one of the pitfalls when it comes to freeing the memory used. should you use delete, delete[], or are we pointing to a memory not even allocated by us? What if we forget to free the allocated memory, or try to access memory already freed?

=> with raw pointers, bugs can be occur easily!

Here smartpointers come to the rescue! Smartpointers like std::unique_ptr, and std::shared_ptr encapsulate these raw pointers for us and handle typesafe memory management. Thus when going out of scope, the memory in a unique_ptr is automatically freed. The same is valid for shared_ptr if no references to it exists.

I would always recommend to use c++'s smart pointers where possible! Which kind of smart pointer you should use depends on the kind of linked list you want to implement (e.g. if circular lists are supported too). btw. have you thought about std::vector or std::list?

pocketbroadcast
  • 169
  • 1
  • 1
  • 8
0

As Karoly Horvath said, it's not the same thing:

  • T* is a "plain" pointer to objects of type T, it stores an address in memory, and implicitly the type of the object that we can expect to find at this address (which is useful to know e.g. the size of the target memory).
  • std::shared_ptr<T> is an object that belongs to the category of "smart-pointers", which are called "smart" because they can manage the memory that is being pointed, by keeping track of how many references exist to that memory location. This means in practical terms that dynamically allocated memory will be released for you when it is no longer used by your code at runtime.

I would say that for a simple linked-list (singly or doubly linked), there is no need to use shared_ptrs. It might be useful e.g. for graphs with dynamic recurrent structure. For genericity sake though, it is better to use a templated version of your node:

template <typename T>
struct ListNode
{
    T data;
    ListNode<T> *next;
};
Jonathan H
  • 7,591
  • 5
  • 47
  • 80
0

The second form is a type of "smart" pointer. Most code using modern c++ should be using them.

Using raw (non smart) pointers you have to remember to do the pairing of new/delete or new[]/delete[] when the object goes out of scope. In the simplistic case of a constructor/destructor its not that much of a burden. But, when you are using pointers in a function and that function throws an exception, it gets a bit tricky to free things up.

There are more than one type of smart pointer. unique, shared and weak. Uniques are for one off object that are only used in one place (like an object or function). Shared are for cases where multiple objects are using the same pointer/resource and you only want to call the allocated object's destructor when the last owner of the pointer goes out of scope. Weaks are for cases where the resource is managed by someone else and the pointed to resource should live on after the object with the weak pointer goes out of scope (they are also needed for avoiding cyclical allocations that prevent GC and cause memory leaks).

Smart pointers are a good thing, you should read up on them (Stroustrups book is great). There are few cases now were naked pointers are needed.

James Anderson
  • 589
  • 2
  • 8
  • "Most code using modern c++ should be using them. [...] There are few cases now were naked pointers are needed." Nonsense, how can you justify this? – Jonathan H Nov 04 '15 at 11:05
-1

First lets get some junk out of the way:

  1. It is good to learn and understand the raw implementations of linked lists as you will encounter them (for many good or bad reasons) in production code.
  2. IF you ~have~ to use 'invasive' linked lists in your code, templates and 'smarter pointers' are going to save you headaches. (IMO).
  3. You are almost always served better by collection classes/templates.

With the caveats above:

The std::shared_ptr is a 'smarter pointer' that wraps a raw pointer (typically produced by calling operator new) and adds RAII style semantics to the mix along with a contract that allows multiple holders of the underlying object to reference the content without it going away. (When used properly.)

A linked list is 'just' a convention for a program to follow (hopefully, but not required to be) homogeneous types without moving the data around. With ('old school', not bad) linked lists ALL of the link management is your responsibility. It is extra easy to either forget, or get distracted and 'forget' to free resources. This is a cause of many nights of debugging and pesky things called 'memory leaks'.

The hybrid 'template link list' is 'better' in that the resource management responsibility is reduced. It is NOT eliminated. It will help reduce a type of memory leak that is forgetting to delete an allocated node. It will NOT eliminate 'memory leaks' that are caused by circular references. (This is case where an 'external actor' is required to break the circular chain and can be extraordinarily complex in 'real' code.)

std::shared_ptr has operators defined that allow you to 'pretend' that you are interacting with WHAT the std::shared pointer is managing. In that way, visually the code looks mostly the same, but the class is(/may be) hiding a little complexity on your behalf. (Pointer checking, etc.)

Which is better? IMO neither. However given a choice between ONLY those two, I would absolutely prefer the 'smarter pointer' version over the 'manual do it your self' style in the VAST majority of cases.

If it was ME I would pick another container. However, if your intent is to learn about the fundamentals of how those containers are implemented (a good thing!) this isn't exactly the answer you want to hear. :-)

ErnieE
  • 143
  • 1
  • 6
  • "It is extra easy to either forget, or get distracted and 'forget'": difference here? "(Pointer checking, " WRONG, shared pointers do not check pointer validity for you unless you ask for it explicitly. "Which is better? IMO neither." How does that answer the OP? – Jonathan H Nov 04 '15 at 11:02
  • 1: Forget: just writing poor code. Distracted: have every intent of writing the code, but because of an interruption you 'remember the intent' as the actual doing. The effect is the same: memory leak. 2: Notice the parenthetical. Are you aware of **EVERY** compiler vendors implementation? Does checking against nullptr 'count' as 'valid'. I am not a 'spec watcher' so I won't claim the behavior it isn't specified. I'd be surprised if it was. 3: The following sentence answers _YOUR_ question. :-) 4: I might have suggested `std::auto_ptr` as an alternate, but that was also not the question. – ErnieE Nov 04 '15 at 14:45
  • 1. The distinction does not bring anything towards the answer, and they both sound like an omission to me. Avoid digressions. 2. The signature is `element_type* std::shared_ptr::get() const noexcept;`. You would expect an omission of `noexcept` if there was a validity check. 3. So this sentence is fluff and should be removed (this applies to most sentences in your answer, go to the point). – Jonathan H Nov 04 '15 at 15:35