简体   繁体   中英

Accessing class member pointers

Suppose I have the following definition of List and Node:


template <class T> 
class List {
    public:
        class Iterator;
        class ConstIterator;

        //Constructors and Destructors.
        List() : head(NULL), tail(NULL), size(0) {}
        List(const List& list);
        ~List();

        //Methods
        Iterator begin();
        ConstIterator begin() const;
        Iterator end();
        ConstIterator end() const;
        void insert(const T& data);
        void insert(const T& data, const Iterator& iterator);
        void remove(const Iterator& iterator);
        int getSize() const;
        Iterator find(const T& item);
        ConstIterator find(const T& item) const;
        void sort();

        //Operators
        List operator = (const List& list);


    private:
        class Node;
        Node* head;
        Node* tail;
        int size;
};


template <class T>
class List<T>::Node
{
    public:
        //Constructors and destructors
        Node(const T& _data, const Node* _next) : data(_data), next(_next) {}       
        ~Node(); //Destructor 

        //Methods


        //Operators
        Node operator = (const Node& node);

    private:
        T data;
        Node* next;
};

I'm writing a function to insert data into a list like this:


    template<class T>
    void List<T>::insert(const T& data)
    {
    Node newNode = new Node(data, NULL);

    if (head == NULL)
    {
        head = &newNode;
        tail = &newNode;
    }
    else
    {
        (*tail)->next = &newNode;
        tail = &newNode;
    }
    size++;
}

However what I find strange is that if I swap (*tail)->next = &newNode; to (*tail).next = &newNode; it still compiles. Why, and what is the correct way of doing it?

The definitions of your classes can be (for the purposes of this question) simplified into:

class List {
    ...
    private:
        Node* head;
        Node* tail;
};

class Node {
    ...
    private:
        Node* next;
};

Now in your List::insert method:

Node newNode = new Node(data, NULL);
(*tail)->next = &newNode;

...when you use new expression, the result will be pointer to the newly allocated memory.
What you should do is:

Node* newNode = new Node(data, NULL);
tail->next = newNode;                // <-- equivalent to (*tail).next = newNode;

The -> operator will automatically derefference a pointer for you then call the method to the right. So:

tail->next

would also work but

tail.next

wouldn't because tail is a pointer. To use the . operator you have to defrence the pointer first as in

(*tail).next

(*tail)

turns your pointer into an object. At that point you can use either -> or .

A . will not work on a pointer but -> will.

Generally, just for easy of typing I use -> because it is shorter then using (*) to turn a pointer into an object just so I can use a dot but they are equivalent operations.

Using Node->tail is short form of writing (*Node).tail. Both forms are valid. Strangeus is the fact that you say that (*Node)->tail compiles. To this happens, Node must be defined as a double pointer, ie:

Node **tail;

But your code has some others bugs in. In this line:

Node newNode = new Node(data, NULL);

you are define a local object and assing a dynamic memory to it. The correct way is:

Node *newNode = new Node(data, NULL);  // defining it as a pointer

and instead of assing as:

head = &newNode;

do:

head = newNode;

As a final note, consider using smart pointer instead of raw pointer. The former is safer than the last

You have noticed that (*tail)->next = &newNode and (*tail).next = &newNode both compile, which strikes you as odd.

But somehow you might also have noticed that this line also compiles!

Node newNode = new Node(data, NULL);

That is the thing that you should give you pause.

You are inside of a template here. Lots of things "compile".

Did you try instantiating the template?

ADDENDUM:

Here just to show you how crazy things can be, check out this program:

#include <iostream>
using namespace std;

template <class T>
class C {
  void f();
};

template <class T>
void C<T>::f() {
  int x = new int;
}

int main() {
  std::cout << "Hello, world\n";
}

Now check this out:

$ g++ template-example.cpp && ./a.out 
Hello, world

But now notice

#include <iostream>
using namespace std;
int main() {
  int x = new int;
  std::cout << "Hello, world\n";
}

which yields:

$ g++ hello.cpp
hello.cpp: In function ‘int main()’:
hello.cpp:4: error: invalid conversion from ‘int*’ to ‘int’

TL;DR: WHEN YOU ARE IN A TEMPLATE, THINGS THAT SHOULD NOT COMPILE SOMETIMES "DO"! (They're not really compiling -- YET!)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM