Проблемы с предварительным объявлением — функции друзей и классы линий/точек

У меня есть демонстрационная программа для понимания функции друга. Думаю, я застрял с ошибками, связанными с предварительным объявлением.

У меня есть класс точек, который имеет координаты x и y. Класс линий имеет два объекта класса точек. Теперь у меня есть функция в классе линий, которая будет вычислять наклон линии.

Это моя программа:

#include <iostream>
using namespace std;

class point
{
    int x,y;
public:
    point(int,int);
    point();
    friend float line::slope();
};

point::point(int a, int b)
{
    x=a;
    y=b;
}

point::point()
{
}

class line
{
    point p1,p2;
public:
    line(point,point);
    float slope();
};

line::line(point p1, point p2)
{
    this->p1=p1;
    this->p2=p2;
}

float line::slope()
{
    float s;
    s=((float)p2.y-p1.y)/(p2.x-p1.x);
    return s;
}

int main()
{
    float sl;
    point obj(5,10);
    point obj1(4,8);
    line obj3(obj,obj1);
    sl=obj3.slope();
    cout<<"\n slope:"<<sl;
    return 0;
}

Это дает мне ошибки компилятора в отношении предварительных объявлений из-за следующего:

  1. Когда я сначала пытаюсь определить свой класс линий, он не знает о классе точек. Даже если я объявляю точечный класс, этого будет недостаточно, потому что для создания объектов точечного класса компилятор должен знать размер точечного класса и, следовательно, самого класса в целом. Понял это через объяснение в этом ответе: https://stackoverflow.com/a/5543788

  2. Если я сначала определяю класс точки, ему нужно знать наклон функции друга и, следовательно, линию класса. Поэтому я попытался предоставить предварительное объявление для класса линий и функции наклона, как это, прежде чем определять класс точек:

линия класса;

float line::slope();

class point
{
    int x,y;
public:
    point(int,int);
    point();
    friend float line::slope();
};

Теперь это дает мне следующие ошибки:

friend1.cpp:5: error: invalid use of incomplete type ‘struct line’
friend1.cpp:4: error: forward declaration of ‘struct line’
friend1.cpp:13: error: invalid use of incomplete type ‘struct line’
friend1.cpp:4: error: forward declaration of ‘struct line’
friend1.cpp: In member function ‘float line::slope()’:
friend1.cpp:9: error: ‘int point::y’ is private
friend1.cpp:43: error: within this context
friend1.cpp:9: error: ‘int point::y’ is private
friend1.cpp:43: error: within this context
friend1.cpp:9: error: ‘int point::x’ is private
friend1.cpp:43: error: within this context
friend1.cpp:9: error: ‘int point::x’ is private
friend1.cpp:43: error: within this context

.3. Затем я попытался выделить класс точек в point.h и point.cpp и класс линий в line.h и line.cpp. Но все же здесь есть зависимость друг от друга.

Хотя теоретически это должно быть возможно, я не могу понять, как заставить его работать.

Ищем ответы.

Спасибо,

Радж

PS: эта программа представляет собой попытку продемонстрировать использование только функций друзей. Там, где дружественные функции бывают двух типов, мы пытаемся справиться со вторым из них:

  1. Дружественные функции, которые независимы.
  2. Дружественные функции, которые являются членами другого класса.

Таким образом, использование дружественных классов в этом случае исключено.


person Raj Pawan Gumdal    schedule 02.08.2012    source источник
comment
Нет смысла делать функцию-член другого класса своим другом. Просто подружитесь со всем классом: friend class line;.   -  person n. 1.8e9-where's-my-share m.    schedule 02.08.2012
comment
Согласен, но этот пример мне понадобился для пояснения функций друзей, которые бывают двух типов: 1. Функции друзей, которые независимы. 2. Дружественные функции, являющиеся членами другого класса. Выше приведен пример для второго типа.   -  person Raj Pawan Gumdal    schedule 02.08.2012
comment
Объявление friend class просто означает, что все функции-члены класса являются дружественными.   -  person n. 1.8e9-where's-my-share m.    schedule 02.08.2012


Ответы (5)


Добавьте line как friend, а не просто метод:

 friend class line;

Другие замечания:

  • отдельные объявления от реализаций в заголовочных файлах и файлах реализации.
  • предпочтите полную квалификацию директивам using (т. е. удалите using namespace std; и вместо этого используйте std::cout.
  • предпочитайте передачу по ссылке для сложных типов - измените line(point,point); на line(const point&, const point&);

РЕДАКТИРОВАНИЕ В образовательных целях. Вы не можете объявить эту конкретную функцию как дружественную, как сейчас в коде, потому что нет полного определения класса line. Следовательно, единственным подходом является следующее:

class point;
class line
{
    point *p1,*p2;
public:
    line(point,point);
    float slope();
};

class point
{
    int x,y;
public:
    point(int,int);
    point();
    friend float line::slope();
};

Вы заранее объявляете point и меняете point членов в line на point* (поскольку точка еще не является полным типом). В point теперь у вас есть полное определение класса line, так что вы можете объявить метод как дружественный.

РЕДАКТИРОВАТЬ 2: Для этого конкретного сценария невозможно использовать объекты point внутри line, потому что вам понадобится полный тип. Но line также должно быть полностью определено, чтобы объявить его элемент как friend.

person Luchian Grigore    schedule 02.08.2012
comment
Мне нужна была именно эта программа, чтобы объяснить, в частности, функции друга. Классы друзей — следующая тема, поэтому мне нужно исправить это только для функций друзей. - person Raj Pawan Gumdal; 02.08.2012
comment
@Raj, вам нужно внести некоторые изменения в остальную часть кода (например, заменить . на ->, где это применимо). Кроме того, это только в образовательных целях. На практике вы обычно объявляете полный класс как friend, и даже это следует делать редко, так как это нарушает инкапсуляцию. - person Luchian Grigore; 02.08.2012
comment
Да, согласен, что это нарушает инкапсуляцию, но, как утверждает автор, есть несколько сценариев, в которых это может быть полезно. Например, вместо того, чтобы делать некоторые экземпляры общедоступными только по той причине, что они должны быть изменены только в некоторых сценариях и рискуют быть измененными без необходимости, были бы полезны дружественные функции и классы. Но я не могу решить выше. - person Raj Pawan Gumdal; 02.08.2012
comment
В общем, нет решения для этого? Как это возможно? Должен быть способ, не так ли? Я имею в виду, помимо использования указателя, о котором вы упомянули. Что, если по какой-то причине мне нужны объекты, а не указатели на эти объекты внутри моего линейного класса? - person Raj Pawan Gumdal; 02.08.2012
comment
Кто-нибудь знает, каковы преимущества и недостатки различных типов дружбы? Я только когда-либо видел классы друзей на практике. - person Sarien; 02.08.2012
comment
@Raj для этих двух классов, нет. Потому что, чтобы объявить функцию как друга, эту функцию необходимо объявить в первую очередь. Чтобы функция была объявлена ​​первой, должен быть определен класс. Но вы не можете определить line до point и по-прежнему использовать объекты point внутри line. - person Luchian Grigore; 02.08.2012
comment
Спасибо, указательный способ - лучший подход. @Corporal - мы должны воздерживаться от использования дружественных классов и функций по той причине, что это каким-то образом нарушает инкапсуляцию. Но это также полезно в некоторых сценариях, как я упоминал в комментарии выше, когда вам не нужно понижать экземпляр/метод до общего доступа из-за его ограниченного доступа где-то и, таким образом, рискуя изменить его из многих других мест. Но да, всегда полезно знать. - person Raj Pawan Gumdal; 02.08.2012
comment
Я просто пытаюсь понять, для чего нужны функции друзей, раз уж вы так непреклонны в их использовании. Может быть неразумно учить кого-то дружественным функциям на сложном примере, если они все равно не очень полезны. - person Sarien; 02.08.2012
comment
@CorporalTouchy - Да, я согласен, этот пример был выбран, потому что мы построили одни и те же классы линий и точек для небольших концепций. Таким образом, это похоже на постепенный способ изучения новых концепций по той же старой программе. Но да, как и в этом случае, он усложняется. И если мы посмотрим на положительные стороны этого, мы поймем больше таких вещей, как использование указателей, и какие именно ошибки были, и о предварительных объявлениях и т. д. - person Raj Pawan Gumdal; 02.08.2012

Просто сделайте Лайн другом Пойнта

class point
{
 friend class line;
 ...
};

Очень мало смысла в том, чтобы объявлять отдельные методы друзьями.

person jahhaj    schedule 02.08.2012
comment
Мне нужна была именно эта программа, чтобы объяснить, в частности, функции друга. Классы друзей — следующая тема, поэтому мне нужно исправить это только для функций друзей. - person Raj Pawan Gumdal; 02.08.2012

Вы можете создать вспомогательный функтор для вычисления наклона. Это позволяет вам сделать метод функтора другом point, не привлекая line.

class point;
class line;

struct slope {
    float operator () (const point &, const point &) const;
    float operator () (const line &) const;
};

class point {
    int x,y;
public:
    point(int a,int b) : x(a), y(b) {}
    point() {}
    friend float slope::operator ()(const point &, const point &) const;
};

class line {
    point p1,p2;
public:
    line(point a,point b) : p1(a), p2(b) {}
    float slope() { return ::slope()(*this); }
    friend float slope::operator ()(const line &) const;
};

С реализациями:

float slope::operator () (const point &p1, const point &p2) const {
    float s;
    s=((float)p2.y-p1.y)/(p2.x-p1.x);
    return s;
}

float slope::operator () (const line &l) const {
    return (*this)(l.p1, l.p2);
}
person jxh    schedule 02.08.2012
comment
@LuchianGrigore: Спасибо. Отредактировано. С уважением - person jxh; 02.08.2012

При нормальных обстоятельствах я бы вообще не использовал здесь friend.

Предпочитаю добавить функцию к point:

float slope_to(const point& other)
{// Not checked, just translated from your implementation
    return ((float)other.y-y)/(other.x-x);
}

И реализовать:

float line::slope()
{
    return p1.slope_to(p2);
}

Таким образом, line не заботится о реализации point, и его не нужно будет менять при реализации 3D points.


Вот демонстрация friend свободной функции (1.):

#include <iostream>

class point
{
    int x, y;
public:
    point(int, int);
    friend std::ostream& operator <<(std::ostream& os, const point& p);
};

std::ostream& operator <<(std::ostream& os, const point& p)
{
    return os << '(' << p.x << ", " << p.y << ')';
}

point::point(int a, int b)
    :x(a)
    ,y(b)
{
}

int main()
{
    point obj(5, 10);
    std::cout << "\n point " << obj;
}

Вот пример, основанный на вашем собственном коде, который содержит friend функции-члена (2.).

#include <iostream>
#include <memory>

using namespace std;

class point;

class line
{
    std::auto_ptr<point> p1, p2;
public:
    line(point&, point&);
    float slope();
};

class point
{
    int x, y;
public:
    point(int, int);
    friend float line::slope();
};

point::point(int a, int b)
    :x(a)
    ,y(b)
{
}

line::line(point& p1, point& p2)
    :p1(new point(p1))
    ,p2(new point(p2))
{
}

float line::slope()
{
    return ((float)p2->y - p1->y) / (p2->x - p1->x);
}

int main()
{
    point obj(5, 10);
    point obj1(4, 8);
    line obj3(obj, obj1);

    cout << "\n slope:" << obj3.slope();
}

Чтобы сослаться на point из line, я использую (auto_) указатели и ссылки, поскольку в этот момент класс объявлен, но не определен. line::slope() объявлен вовремя, чтобы я мог сослаться на него как на friend из point. Эта циклическая зависимость — ужасный запах кода, и ее следует избегать.

person Johnsyweb    schedule 02.08.2012
comment
Виноват! Цель этой программы состояла в том, чтобы объяснить функции друзей. Я должен был упомянуть об этом в своем вопросе. Есть много способов избежать этого, но это нужно для объяснения. Пожалуйста, смотрите мое дополнение к вопросу. Спасибо. - person Raj Pawan Gumdal; 02.08.2012
comment
Вы пытаетесь ввести циклическую зависимость между point и line. Если вы хотите понять friends, попробуйте пример без циклической зависимости. - person Johnsyweb; 02.08.2012
comment
@Raj: я обновил свой ответ, чтобы продемонстрировать оба упомянутых вами типа. - person Johnsyweb; 02.08.2012

вы ссылаетесь на класс линии в friend float line::slope(); до того, как класс будет определен.

просто добавьте строку class line; перед определением класса Point

также измените friend float line::slope(); на friend class line;

person John Corbett    schedule 02.08.2012
comment
Я сделал это, это был мой пункт 2. Но это дает мне ошибки. Мой пост не был отформатирован для пункта 2 ранее. - person Raj Pawan Gumdal; 02.08.2012