Почему этот производный класс должен быть объявлен как друг? - PullRequest
2 голосов
/ 11 июня 2009

Я изучаю C ++ самостоятельно, и я подумал, что хороший способ испачкать руки - это конвертировать некоторые проекты Java в C ++, посмотрите, где я упал. Так что я работаю над реализацией полиморфного списка. Работает нормально, за исключением одной странной вещи.

Способ, которым я печатаю список, заключается в том, чтобы класс EmptyList возвращал "null" (строка, а не указатель), а NonEmptyList возвращает строку, в которой их данные объединены с результатом вызова tostring() on все остальное в списке.

Я помещаю tostring() в секцию protected (это казалось уместным), и компилятор жалуется на эту строку (s - это stringstream, который я использую для накопления строки):

s << tail->tostring();

Вот ошибка от компилятора:

../list.h: In member function 'std::string NonEmptyList::tostring() [with T = int]':
../list.h:95:   instantiated from here
../list.h:41: error: 'std::string List::tostring() [with T = int]' is protected
../list.h:62: error: within this context

Вот большая часть list.h:

template <class T> class List;
template <class T> class EmptyList;
template <class T> class NonEmptyList;

template <typename T>
class List {
public:
    friend std::ostream& operator<< (std::ostream& o, List<T>* l){
        o << l->tostring();
        return o;
    }
    /* If I don't declare NonEmptyList<T> as a friend, the compiler complains
     * that "tostring" is protected when NonEmptyClass tries to call it
     * recursively.
     */
    //friend class NonEmptyList<T>;

    virtual NonEmptyList<T>* insert(T) =0;
    virtual List<T>* remove(T) =0;
    virtual int size() = 0;
    virtual bool contains(T) = 0;
    virtual T max() = 0;

    virtual ~List<T>() {}
protected:
    virtual std::string tostring() =0;
};

template <typename T>
class NonEmptyList: public List<T>{
    friend class EmptyString;
    T data;
    List<T>* tail;
public:
    NonEmptyList<T>(T elem);
    NonEmptyList<T>* insert(T elem);
    List<T>* remove(T elem);
    int size() { return 1 + tail->size(); }
    bool contains(T);
    T max();
protected:
    std::string tostring(){
        std::stringstream s;
        s << data << ",";

        /* This fails if List doesn't declare NonEmptyLst a friend */
        s << tail->tostring();

        return s.str();
    }
};

Таким образом, объявление NonEmptyList другом List устраняет проблему, но кажется странным объявить производный класс в качестве друга базового класса.

Ответы [ 3 ]

7 голосов
/ 11 июня 2009

Поскольку tail является List<T>, компилятор сообщает вам, что вы не можете получить доступ к защищенному члену другого класса. Как и в других C-подобных языках, вы можете получить доступ только к защищенным членам в вашем экземпляре базового класса, но не к другим людям.

Извлечение класса A из класса B не дает классу A доступ к каждому защищенному члену класса B во всех экземплярах, которые относятся к классу типа B или являются производными от этого типа.

Эта статья MSDN о ключевом слове C ++ protected может быть полезна для разъяснения.

Обновление

Как подсказал Магнус в своем ответе , в этом случае простым исправлением может быть замена вызова на tail->tostring() оператором <<, который вы реализовали для List<T> обеспечить такое же поведение, как tostring(). Таким образом, вам не понадобится декларация friend.

5 голосов
/ 11 июня 2009

Как сказал Джефф, метод toString () защищен и не может быть вызван из вашего класса NonEmptyList. Но вы уже предоставили std :: ostream & operator << для класса List, так почему бы вам не использовать его в NonEmptyList? </p>

template <typename T>
class NonEmptyList: public List<T>{
// ..
protected:
    std::string tostring(){
        std::stringstream s;
        s << data << ",";

        s << tail; // <--- Here :)

        return s.str();
    }
};
0 голосов
/ 11 июня 2009

Исходя из вашего кода, вы объявляете List * tail как член класса NonEmptyList, поэтому вы не можете получить к нему доступ. Если вы хотите получить доступ к защищенному методу из базового класса, вам нужно вызвать base-> tostring ();

Вы делаете композицию, НЕ наследование здесь.

И я не уверен, почему вы заканчиваете этот дизайн класса преобразованием java-проекта, если вы наследуете от базового класса, вы обычно не хотите объявлять другой экземпляр базового класса в качестве переменной-члена. Этот подход не выглядит очень гладким для меня. Я не думаю, что вам даже нужно делать это на Java.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...