Двусторонние функции-друзья в C ++: как заставить его скомпилироваться? - PullRequest
2 голосов
/ 11 февраля 2020

У меня есть следующие файлы C ++:

А *


#ifndef A_H
#define A_H

#include "B.h"

class A
{
    private:
        int m_a;
    public:
        A();

        void a(const B &b);

    friend void B::b(const A &a);
};

#endif // A_H

A. cpp


#include "A.h"
#include "B.h"
#include 

A::A()
{
    m_a = 100;
}

void A::a(const B &b)
{
    std::cout << b.m_b << ".\n";
}

Bh


#ifndef B_H
#define B_H

class A;

class B
{
    private:
        int m_b;
    public:
        B();
        void b(const A &a);

    friend void A::a(const B &b); // error: invalid use of incomplete type 'class A'
};

#endif // B_H

B. cpp


#include "B.h"
#include 
#include "A.h"

B::B()
{
    this->m_b = 101;
}

void B::b(const A &a)
{
    std::cout << a.m_a << ".\n";
}

Когда я пытаюсь это скомпилировать, выдается:

ошибка: недопустимое использование неполного типа 'класс A'.

Как мне это исправить?

Ответы [ 4 ]

3 голосов
/ 11 февраля 2020

Вы пытаетесь настроить циклическую зависимость. Это ужасная вещь, с которой нужно справиться, и она просто запрещена во многих случаях использования. В C ++ правило состоит в том, что вы можете использовать любой объект, как только он объявлен , даже если он будет полностью определен позже. Проблема в том, что члены объявляются только определением класса. Поэтому, если нужно использовать один класс (скажем, B), это определение членов другого класса (скажем, A), то A нужно определить до B. Так как вы не можете определить A до B и B до A в то же время вы не сможете достичь того, чего хотите в C ++.

Если вы попытаетесь настроить члена A как друга в B и члена B как друга в A, тогда у вас есть общая проблема дизайна. Если это имеет смысл (с точки зрения модели), то это можно решить, сделав один полный класс другом (это ответ @ zmb) или изменив функции-члены на не-члены. Но я настоятельно советую вам go отступить на один шаг и попытаться построить иерархическую модель вместо круговой. К сожалению, это становится вопросом дизайна, а не программированием, поэтому я боюсь, что не смогу больше помочь вам в SO ...

3 голосов
/ 11 февраля 2020

На самом деле в вашем случае вы создаете циклическую зависимость.

Этого обычно избегают с помощью прямого объявления, но, насколько я знаю, C ++ не позволяет нам делать прямое объявление для функций-членов, так что кажется теперь это невозможно для меня.

Но можно нарушить циклическую зависимость, когда вы объявляете class как friend.

ах

#ifndef A_H
#define A_H

#include "b.h"

class B;

class A
{
    private:
        int m_a;
    public:
        A();

        void a(const B &b);

    friend class B;
};

#endif // A_H

a. cpp

#include "a.h"
#include "b.h"
#include <iostream>

A::A()
{
    m_a = 100;
}

void A::a(const B &b)
{
    std::cout << b.m_b << ".\n";
}

чч

#ifndef B_H
#define B_H

class A;

class B
{
    private:
        int m_b;
    public:
        B();
        void b(const A &a);

    friend class A;
};

#endif // B_H

ч. cpp

#include "b.h"
#include "a.h"
#include <iostream>

B::B()
{
    this->m_b = 101;
}

void B::b(const A &a)
{
    std::cout << a.m_a << ".\n";
}
3 голосов
/ 11 февраля 2020

Невозможно иметь функцию члена B, друга A и функцию члена A, друга B.

Есть несколько способов преодолеть это.

  1. Напишите одну или обе функции как не-члены. По необходимости сделайте их друзьями обоих классов.
  2. Сделайте весь класс другом другого класса. Если это слишком широкое разрешение, извлеките меньший класс для использования в качестве друга.
  3. Превратите оба класса в шаблоны классов (параметр шаблона не имеет значения).

    enum unit {u};
    
    template <unit X> class A;
    
    template <unit X> class B
    {
        static void foo() {}
        static void bar() { A<X>::foo(); }
        friend void A<X>::bar();
    };
    
    template <unit X> class A
    {
        static void foo() {}
        static void bar() { B<X>::foo(); }
        friend void B<X>::bar();
    };
    
    using AX = A<u>;
    using BX = B<u>;
    
1 голос
/ 11 февраля 2020

Вы используете неизвестный тип:

friend void A::a(const B &b);

Компилятор знает только, что класс A существует, но больше ничего. Вместо этого вы можете объявить весь класс A другом:

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