0

我有一个了解朋友功能的演示程序。我想我遇到了与前向声明相关的错误。

我有一个点类,它有 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. 作为另一个类的成员的朋友函数。

因此,在这种情况下排除了使用朋友类。

4

5 回答 5

2

添加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成员更改linepoint*(因为 point 还不是完整的类型)。你现在point有了line类的完整定义,所以你可以将方法声明为朋友。

编辑 2:对于这种特殊情况,不可能point在内部使用对象,line因为您需要完整类型。但line也必须完全定义才能将其成员声明为friend.

于 2012-08-02T06:07:57.143 回答
1

在正常情况下,我会完全避免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's 的实现,并且在实现 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()及时声明,以便我将其引用friendpoint. 这种循环依赖是一种可怕的代码气味,应该避免。

于 2012-08-02T06:15:03.320 回答
1

让 Line 成为 Point 的朋友

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

将单个方法声明为朋友几乎没有什么目的。

于 2012-08-02T06:07:35.403 回答
1

您可以创建一个辅助仿函数来计算斜率。这使您可以在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);
}
于 2012-08-02T06:20:20.373 回答
0

friend float line::slope();您在定义类之前引用行类。

class line;只需在 Point 类的定义前添加一行

friend float line::slope();改为friend class line;

于 2012-08-02T06:04:46.880 回答