会员作为朋友的功能:Lippman 5th的书错了吗?

时间:2018-03-19 04:29:37

标签: c++

Lippman第五名 ISBN-13:978-0321714114

第280-281页,它说:

  

让会员成为朋友

     

而不是让整个Window_mgr类成为朋友,Screen可以   而是指定只允许clear成员访问。什么时候我们   声明一个成员函数成为朋友,我们必须指定类的   哪个功能是成员:

class Screen {
    // Window_mgr::clear must have been declared before class Screen
    friend void Window_mgr::clear(ScreenIndex);
    // ... rest of the Screen class
};
     

使成员函数成为朋友需要仔细构建我们的   程序,以适应声明和之间的相互依赖关系   定义。在这个例子中,我们必须按如下方式订购我们的程序:

     
      
  • 首先,定义Window_mgr类,它声明但无法定义,清除。屏幕必须在清除之前声明才能使用   屏幕成员。
  •   
  • 接下来,定义类屏幕,包括明确的朋友声明。
  •   
  • 最后,定义clear,现在可以引用Screen中的成员。
  •   

问题是:类Window_mgr有一个依赖于类的数据成员 屏幕定义。参见:

class Window_mgr {
public:
    // location ID for each screen on the window
    using ScreenIndex = std::vector<Screen>::size_type;
    // reset the Screen at the given position to all blanks
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{Screen(24, 80, ' ')};
};

因此,在不定义Screen的情况下首先定义Window_mgr是不可能的 先前! 而与此同时,没有我们就不可能定义屏幕 定义了Window_mgr !!!

如何解决这个问题? 这本书错了吗?

我会在这里粘贴代码,以便您可以使用a重复此问题 最小代码:

#include <iostream>
#include <string>
#include <vector>

class A
{
    friend void B::hello();

public:
    A(int i) : number{i} {}

private:
    void f() {
        std::cout << "hello" << std::endl;
    }
    int number;
};

class B {
private:
    std::vector<A> x{A(10)};

public:
    void hello()
    {
        for(A &elem : x)
        {
            elem.f();
        }
    }
};


int main()
{
    A x;

    return 0;
}

如果我编译此代码,结果是: 错误:使用未声明的标识符&#39; B&#39;      friend void B :: hello();

如果我反转位置(A&lt; - &gt; B),我有: 错误:使用未声明的标识符&#39; A&#39;      std :: vector x {A(10)};

有没有正确的方法呢?

谢谢!

编辑:

谢谢你,Craig Young

解决方案:

#include <iostream>
#include <string>
#include <vector>

class A;

class B {
private:
    std::vector<A> x;

public:
    B();
    void hello();

};

class A
{
    friend void B::hello();

public:
    A(int i) : number{i} {}

private:
    void f() {
        std::cout << "hello" << std::endl;
    }
    int number;
};

B::B() : x{A(10)}
{

}

void B::hello()
{
    for(A &elem : x)
    {
        elem.f();
    }
}

int main()
{


    return 0;
}

结论:

  • 这本书不完整,因为它没有首先暴露出进行A类前向声明​​的必要性,而且在这种情况下不可能进行类内初始化。
  • 我没有注意到问题是A(10),而不是矢量!也就是说,当我们将它用作向量的模板参数时,我们可以使用不完整的类型A(只有声明,没有定义)(因为它不会创建一个对象本身),但是在定义一个对象时我们不能使用不完整的类型A.对象,例如:A(10);

2 个答案:

答案 0 :(得分:1)

您必须拥有较早的声明,但不能使用早期的定义

添加

class A;
class B;
前面的

告诉编译器“A”和“B”指的是类。这应该足以让其他人去推理。

答案 1 :(得分:1)

首发

嗯,你没有正确遵循指导。

  

首先,定义Window_mgr类,它声明但不能定义,清除。必须在clear之前声明屏幕才能使用Screen的成员。

您必须在B之前声明A

  

接下来,定义类屏幕,包括明确的朋友声明。

现在宣布AB::hello()为好友。

  

最后,定义clear,现在可以引用Screen中的成员。

B:hello()可以使用A的私人成员。

此处已涵盖此内容:C++ Forward declaration , friend function problem

您添加了并发症

此外,您希望B的声明引用A。要实现这一目标,您需要转发声明A,以便B知道它的存在。

了解您只能“部分”访问A非常重要。您无法在A的声明中“完全使用”B。因此B中的以下行是错误的。

//You're trying to create A when you only know it exists.
//You don't have a full definition of A yet.
std::vector<A> x{A(10)};

//Replace the above with...
std::vector<A> x;

当然,你必须找到另一种方法来初始化x

示例代码

#include <iostream>
#include <vector>

class A;

class B
{
private:
    std::vector<A> x;
public:
    void hello();
};

class A
{
    friend void B::hello();
public:
    A(int i): number(i) {}

private:
    void f() { std::cout << "hello" << std::endl; }
    int number;
};

void B::hello()
{     
    for(A &elem : x)
    {
        elem.f();
    }
}

int main()
{
    A a{5};
    return 0;
}