Функция друга и цикл включения
У меня проблема с функцией друга между двумя классами. Давайте посмотрим код:
Первый класс:
#ifndef _FIRST_H_
#define _FIRST_H_
//#include "Second.h"
#include <string>
class Second;
class First
{
friend void Second::fun();
std::string str = "Dziala\n";
public:
First();
~First();
};
#endif
и второй класс:
#ifndef _SECOND_H_
#define _SECOND_H_
#include<iostream>
#include "First.h"
class Second
{
First fObj;
public:
Second();
~Second();
void fun() { std::cout << fObj.str; }
};
#endif
Нет проблем, если я попытаюсь подружиться с КЛАССОМ. Проблема возникает, если я сделаю друга FUNCTION, как в примере выше. Я могу исправить это с помощью #include "Second.h" в первом классе, но тогда это будет цикл включения. У вас есть идеи, как это сделать?
3 ответа
Проблема возникает, если я сделаю друга FUNCTION, как в примере выше.
// #include "Second.h"
#include <string>
class Second;
class First
{
friend void Second::fun();
...
Первая строка - объявление класса Second
, Это предварительная декларация. Для класса Second
после его объявления и до его определения, он является неполным типом. Так Second
известен как тип класса, но члены, которые он содержит, неизвестны. Таким образом, вы не можете использовать член void Second::fun()
Вот.
friend class Second
работает нормально, так как никогда не пытается использовать элемент неполного типа.
но тогда это будет включать цикл.
Как сказал MadsMarquart, это не проблема, так как у вас уже есть защита заголовка.
есть идеи как это сделать?
Если вы хотите использовать friend void Second::fun()
как объявление друга, порядок объявления и определения важен, и необходима небольшая модификация вашего класса.
- Объявить класс
First
, - Определить класс
Second
с объявлением (не определением)fun()
,- Вы не можете использовать
First fObj
как член или попытатьсяnew First
сейчас, так какFirst
не определен и конструкторFirst
сейчас неизвестно. Указатель или ссылка будут в порядке. - Поскольку указатель или ссылка используются, конструктор ** также должен быть изменен.
- Вы не можете использовать
- Определить класс
First
с декларацией другаfun()
, - определять
fun()
,
Коды модифицированной базы на вашем примере,
class First;
class Second {
public:
Second(First& rfObj) : fObj(rfObj) {}
void fun();
private:
First& fObj;
};
class First {
friend void Second::fun();
public:
First() = default;
private:
std::string str = "Dziala\n";
};
void Second::fun() { std::cout << fObj.str; }
Невозможно объявить функцию-член как друга, если не видно полное определение класса. Разрешение иначе разрешило бы произвольному коду объявлять и определять члены класса, которые не предназначен создателю этого класса.
class Second // complete class definition, not just a forward declaration
{
public:
void fun();
};
class First
{
friend void Second::fun();
};
Следствием этого является то, что First
не может просто быть членом Second
, Чтобы принять это, компилятор должен иметь видимость полного определения Second
для определения First
компилировать, но также иметь видимость полного определения First
для определения Second
Скомпилировать. Это бесконечно рекурсивная зависимость, которая имеет тенденцию расстраивать компиляторы.
Единственными типами членов класса (или вообще вообще переменных), которые могут быть объявлены только с помощью предварительного объявления, являются указатели или ссылки.
Итак, это будет работать
class First;
class Second // complete class definition, not just a forward declaration
{
private:
First &fObj; // note this is a reference
public:
void fun();
Second();
~Second();
};
class First
{
friend void Second::fun();
};
Second::Second() : fObj(*(new First)) // assumes First has appropriate (not shown) constructor
{}
Second::~Second()
{
delete &fObj;
}
Однако обратите внимание, что как конструктор, так и деструктор Second
также не может быть скомпилировано, если определение First
виден компилятору заранее. Это связано с тем, что невозможно создать или уничтожить экземпляр типа класса только на основе предварительного объявления (т. Е. По той же причине, что и для исходной задачи).
Практически, я бы просто объявил класс Second
быть другом First
и покончим с этим. В конце концов, объявление одной функции-члена класса в качестве друга подтверждает, что функция-член всегда будет реализована так, чтобы работать так, как задумано. Существует очень немного обстоятельств, при которых можно доверять одной функции-члену класса для работы в соответствии с требованиями, но не доверять другим функциям-членам того же класса.
У вас есть идеи, как это сделать?
Вы не можете использовать friend
механизм, чтобы делать то, что вы пытаетесь.
Простое решение - выставить First::str
через функцию-член и не беспокоиться о friend
построить. Это более чистое решение в долгосрочной перспективе.
class First
{
public:
First();
~First();
std::string const& getString() const { return str; }
private:
std::string str = "Dziala\n";
};