How to properly use protected in singly linked lis

2019-09-02 13:01发布

I've successfully programmed a singly linked list by the following program: The header file is:

#ifndef SLL_H_
#define SLL_H_

#include <iostream>
class node {
    protected:

    public:
        int key;
        node *next;
        node();
        ~node();
};

class SLL : public node{
    private:
        node *Head = NULL;
        int SLL_SIZE = 0;
    public:
        //Constructor
        SLL();
        //SLL(int n);
        //Destructor
        ~SLL();
        //Modifiers
        void Push_Front(int a);
        void Push_Back(SLL A,int b);
        void Traverse();
        //Access function
        int SLL_size();
        int Get(node* p);
        //Iterator
        node* Begin();
        node* End();
        //void Search(int a);

};

#endif

SLL.cpp

#include "SLL.h"
#include <iostream>

using namespace std;

node::node(){
    cout << "Empty constructor of node is being called;" << endl;
}

node::~node(){
    cout << "Empty destructor of node is being called;" << endl;
}

SLL::SLL():node(){
    cout << "Empty constructor of SLL is being called;" << endl;
}

SLL::~SLL(){
    cout << "Empty destructor of SLL is being called." << endl;
}

//Insert element at the front of the list
void SLL::Push_Front(int k){
    node *temp = new node [1];
    temp->key = k;
    temp->next = Head;
    Head = temp;
    SLL_SIZE = SLL_SIZE + 1;
}

//Insert element at the end of the list
void SLL::Push_Back(SLL A, int m){
    node *temp1 = A.End();
    node *temp2 = new node [1];
    temp2->key = m;
    temp1->next = temp2;
    temp2->next = NULL;
    SLL_SIZE = SLL_SIZE + 1;
}

//Insert element at a given position

//Return the number of elements in the linked list
int SLL::SLL_size(){
    return SLL_SIZE;
}

//Traverse the list (print the list)
void SLL::Traverse(){
    node *temp;
    temp = Head;
    while(temp!=NULL){
        cout << temp->key << " ";
        temp = temp->next;
    }
    cout << endl;
}

//Get key given pionter
int SLL::Get(node* pt){
    if(pt!=NULL){
        node* temp = pt;
        return temp->key;
    }
    else {
        cout << "Null pointer points to nowhere!" << endl;
        return 0;
    }
}

//Return the pointer at the beginning of the list
node* SLL::Begin(){
    return Head;
}

//Return the pointer at the end of the list
node* SLL::End(){
    node* temp = Head;
    while(temp->next!=NULL){
        temp = temp->next;
    }
    return temp;
}

main.cpp

#include <iostream>
#include "SLL.h"

using namespace std;

int main()
{
    SLL A;
    A.Push_Front(1);
    A.Push_Front(2);
    A.Push_Front(5);
    A.Push_Front(6);
    A.Push_Back(A,3);
    A.Traverse();
    cout << A.SLL_size() << endl;
    cout << A.Get(A.Begin()) << endl;
    cout << A.Get(A.End()) << endl;
    return 0;
}

One error is, for example :

SLL.h||In member function 'void SLL::Push_Front(int)':|
SLL.h|7|error: 'int node::key' is protected|
SLL.cpp|25|error: within this context|
SLL.h|8|error: 'node* node::next' is protected|
SLL.cpp|26|error: within this context|
SLL.h||In member function 'void SLL::Push_Back(SLL, int)':|
SLL.h|7|error: 'int node::key' is protected|
SLL.cpp|35|error: within this context|
SLL.h|8|error: 'node* node::next' is protected|
LL.cpp|36|error: within this context|
SLL.h|8|error: 'node* node::next' is protected|
SLL.cpp|37|error: within this context|

Similar error for the other member functions that employed key and next.

This program works very well now. However, after I moved the 2 lines in node class, int key; node *next; under protected, then it gives me error such as "node::key is protected".

First, please don't blame me for doing something stupid :P . I know if I struct for the node then life will be much easier. I am trying to practice inheritance and to understand protected. That's why.

By definition, protected members can be accessed by derived class, right? I don't know where I am doing it wrong.

Hope you can help me out. Thanks!

2条回答
狗以群分
2楼-- · 2019-09-02 13:30

The protected keyword allows an inheriting class to see the protected members. That means instances of the inheriting class can see the protected members of themselves, as well as the protected members of other instances of that same inheriting class. It does not extend this access through pointers to the parent class type, because that access isn't safe.

Let's make that concrete. Consider the following example:

class A
{
    protected:
        int a_int;
};

class B : public A
{
    public:

        int good()
        {
            return a_int;  // OK:  Protected member of this instance
        }

        int bad( A *a_ptr )
        {
            return a_ptr->a_int; // BAD:  Can't access the protected member
                                 //       through a pointer to the parent class type.
        }

        int also_good( B *b_ptr )
        {
            return b_ptr->a_int; // OK:  Pointer to the same class type as this
                                 //      class is safe.
        }
};

The errors in your code look like the second case. So why is the second case illegal, but third case OK?

The second case is illegal because the compiler doesn't know the actual type of object the A* points to. It could be any descendent of A, and may not even be convertible to B*. Therefore, the access extended by the protected clause isn't guaranteed to be safe or meaningful. For example, suppose you had

class C : public A { ... };
class D : public C { ... };

and you passed a C* or a D* into method bad() above. It doesn't seem reasonable that B should be able to see the protected members that were exposed to C, since C and B aren't directly related. The same holds true for D.

But, in the third case, the compiler knows for certain it has a pointer to a B or a class derived from B, so it knows that the access extended by the protected keyword is safe and meaningful. By that, I mean that the protected fields are managed in the way B expects them to be managed. In fact, without that access, you would have a hard time writing binary operators involving two instances of B

Make sense?

In case you're still not convinced: Suppose I made two parallel classes, both which inherit from node:

// plain singly linked list
class normal_sll : public node { };

// singly linked list that stores all of its elements negated
class negative_sll : public node { };

Sure, it's a contrived example, but bear with me. Because both classes derive from node, you could pass either class through a node *. So, you could pass an instance of negative_sll to normal_sll or vice versa.

C++'s access control prevents either class from looking at the protected fields through that node *, though. And that's good, because negative_sll manages them differently than normal_sll.

But, you can't pass an instance of negative_sll through a normal_sll* or vice versa. So, you know if you have a normal_sll* inside one of normal_sll's methods, you know it's safe to access the protected members.

Sure, it's a contrived example. I'm sure you could think of a better one. Make sense, though?

Now you could make B a friend of A and override this control. But, this would let B see the private members of A, totally bypassing the protected concept. A better solution would be to rewrite your SLL code so that you pass SLL*, not node* to its methods.

查看更多
神经病院院长
3楼-- · 2019-09-02 13:49

First of all, I don't see, why you want to derive SLL from Node at all, as a List is not a specialization of a Node. What you probably want to do instead is to make SLL a friend of Node:

class node {
protected:
    int key;
    node *next;
public:
    friend SLL; //This allows SLL to access node's private and protected members

    node();
    ~node();
};

As far as your question - why SLL can't access Node's protected members - is concerned:

--------- EDIT: My explanation was just wrong ------------

查看更多
登录 后发表回答