如何正确使用受保护的单链表 [英] How to properly use protected in singly linked list

查看:113
本文介绍了如何正确使用受保护的单链表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已通过以下程序成功编程了一个单独链接的列表:
头文件是:

  #ifndef SLL_H_ 
#define SLL_H_

#include< iostream>
class node {
protected:

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

类SLL:public node {
private:
node * Head = NULL;
int SLL_SIZE = 0;
public:
//构造函数
SLL();
// SLL(int n);
//析构函数
〜SLL();
//修饰符
void Push_Front(int a);
void Push_Back(SLL A,int b);
void Traverse();
//访问函数
int SLL_size();
int Get(node * p);
//迭代器
节点* Begin();
node * End();
// void Search(int a);

};

#endif

SLL.cpp

  #includeSLL.h
#include< iostream>

using namespace std;

node :: node(){
cout<< 正在调用节点的空构造函数; << endl;
}

node ::〜node(){
cout<< 正在调用节点的空析构函数; << endl;
}

SLL :: SLL():node(){
cout< 正在调用SLL的空构造函数; << endl;
}

SLL ::〜SLL(){
cout< 正在调用SLL的空析构函数。 << endl;
}

//在列表前面插入元素
void SLL :: Push_Front(int k){
node * temp = new node [1] ;
temp-> key = k;
temp-> next = Head;
Head = temp;
SLL_SIZE = SLL_SIZE + 1;
}

//在列表末尾插入元素
void SLL :: Push_Back(SLL A,int m){
node * temp1 = A.结束();
node * temp2 = new node [1];
temp2-> key = m;
temp1-> next = temp2;
temp2-> next = NULL;
SLL_SIZE = SLL_SIZE + 1;
}

//在给定位置插入元素

//返回链表中的元素数量
int SLL :: SLL_size ){
return SLL_SIZE;
}

//遍历列表(打印列表)
void SLL :: Traverse(){
node * temp;
temp = Head;
while(temp!= NULL){
cout<< temp>键<< ;
temp = temp-> next;
}
cout<< endl;
}

//获取key给定pionter
int SLL :: Get(node * pt){
if(pt!= NULL){
node * temp = pt;
return temp-> key;
}
else {
cout<< 空指针指向无处! << endl;
return 0;
}
}

//返回列表开头的指针
node * SLL :: Begin(){
return Head;
}

//返回列表末尾的指针
node * SLL :: End(){
node * temp = Head;
while(temp-> next!= NULL){
temp = temp-> next;
}
return temp;
}



main.cpp

  #include< iostream> 
#includeSLL.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;
}

一个错误是,例如:


$在成员函数'void SLL :: Push_Front(int)':| | | | | | | | | | | | | | | | | | | | | | | |
SLL.h | 7 | error:'int node :: key'is protected |
SLL.cpp | 25 |错误:在此上下文中
SLL.h | 8 | error:'node * node :: next'is protected |
SLL.cpp | 26 |错误:在此上下文中
SLL.h ||在成员函数'void SLL :: Push_Back(SLL,int)':|
SLL.h | 7 | error:'int node :: key'is protected |
SLL.cpp | 35 |错误:在此上下文中
SLL.h | 8 | error:'node * node :: next'is protected |
LL.cpp | 36 |错误:在此上下文中
SLL.h | 8 | error:'node * node :: next'is protected |
SLL.cpp | 37 |错误:在此上下文中

使用key和next的其他成员函数的类似错误。



这个程序现在工作得很好。但是,在我移动节点类中的两行之后, int key; node * next; 在 protected 下,然后它会给我错误,如node :: key is protected。



首先,请不要怪我做一些蠢事:P。我知道如果我 struct 为节点,那么生活将会容易得多。我试图练习继承和理解受保护。这就是为什么。



根据定义,受保护的成员可以被派生类访问,对吧?我不知道我在哪里做错了。



希望你能帮助我。谢谢!

解决方案

protected 关键字允许继承类看到 protected 成员。这意味着继承类的实例可以看到自己的受保护成员,以及同一继承类的其他实例的受保护成员。它不通过指向父类类型的指针来扩展此访问,因为该访问不安全。



让我们具体化。考虑下面的例子:

  class A 
{
protected:
int a_int;
};

class B:public A
{
public:

int good()
{
return a_int; // OK:此实例的受保护成员
}

int bad(A * a_ptr)
{
return a_ptr-> a_int; // BAD:无法通过指向父类类型的指针访问受保护的成员
//。
}

int also_good(B * b_ptr)
{
return b_ptr-> a_int; // OK:指向与此类型相同的类类型
//类是安全的。
}
};

代码中的错误看起来像第二种情况。那么为什么第二种情况是非法的,但第三种情况是正确的?



第二种情况是非法的,因为编译器不知道对象的实际类型 A * 指向。它可以是 A 的任何后代,甚至不能转换为 B * 。因此,由 protected 子句扩展的访问不能保证是安全或有意义的。例如,假设您有

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

且您传递了 C * D * 到上面的方法 bad() B 应该能够看到暴露给 C 的受保护成员似乎是不合理的,因为 C B 不直接相关。这同样适用于 D



但是,在第三种情况下,编译器知道它肯定有指向 B 或从 B 派生的类的指针,因此它知道由 protected 关键字是安全有意义的。通过这个,我的意思是保护字段的管理方式 B 期望他们被管理。事实上,没有这种访问,你将很难编写涉及两个实例 B的二进制运算符



感觉?



如果你仍然不相信:假设我做了两个并行类,两个继承自 node

  //纯单链表
class normal_sll:public node {};

//单向链表,存储其所有元素否定
class negative_sll:public node {};

当然,这是一个假设的例子,但是忍受我。因为两个类派生自 node ,所以你可以通过 node * 传递任何一个类。因此,您可以将 negative_sll 的实例传递给 normal_sll ,反之亦然。



C ++的访问控制防止任何一个类通过 node * 查看受保护的字段。这很好,因为 negative_sll 管理它们不同于 normal_sll



但是,您不能通过 normal_sll * 传递 negative_sll 的实例,反之亦然。所以,你知道如果你在 normal_sll 的方法中有一个 normal_sll * ,你知道它是安全的访问受保护的成员。



当然,这是一个假设的例子。我相信你可以想到一个更好的。



现在你可以使 B a 朋友 of A 并覆盖此控件。但是,这会让 B 看到 A 私人成员,完全绕过 protected 概念。更好的解决方案是重写 SLL 代码,以便传递 SLL * ,而不是节点* 到它的方法。


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!

解决方案

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.

这篇关于如何正确使用受保护的单链表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆