3

クラス フレンドシップは C++ の最後の手段の 1 つですが、このパターンは理にかなっていますか?

class Peer
{
public:
    friend class Peer;
    void GetSecret(const Peer& other)
    {
        const std::string& secret = other.GiveSecret();
        std::cout << secret << std::endl;
    }

private:
    const std::string& GiveSecret() const
    {
        return "secrety stuff";
    }
};

int main(int argc, char* argv[])
{
    Peer peerA;
    Peer peerB;
    peerA.GetSecret(peerB);
    return 0;
}

このパターンの理由は、ピアがすべて同じランクにあり、互いに知識を共有する必要があるためです。ただし、この知識は秘密です。ピア以外の誰も使用しないか、プログラムが無効になるためです。 .

これの非常に現実的な例の 1 つは、あるピアが別のピアからコピー構築されている場合、そのソース ピアからの秘密情報にアクセスする必要があるということです。

4

8 に答える 8

17

この場合、友達は必要ありません。クラスのオブジェクトは、同じタイプの他のオブジェクトのプライベートメンバーにアクセスできます。フレンド宣言がなくても問題なく動作するはずです。

于 2009-02-23T12:11:10.757 に答える
3

標準の c++ では、private 句にはクラス スコープがあると書かれています。これは、すべてのピアが他のピアの非公開部分にアクセスできることを意味します。これは実装固有ではありません

于 2009-02-23T12:51:02.050 に答える
2

この場合、友情は必要ないことを発見しましたが、上記の意見に反して、協力するクラス間の友情には原則として何の問題もないとだけ言っておきます。実際、友情はカプセル化を破るのではなく、実際にカプセル化を促進する可能性があります。

プライベート データへのアクセサ メソッドを作成する別の方法を検討してください。これを行うと、フレンドとして宣言された限られたクラス/関数のセットだけでなく、すべてのクライアントのプライベート データへのアクセスを効果的に許可することになります。フレンド クラスの 1 つのメソッドだけが内部にアクセスする場合、パブリック アクセサー メソッドを提供した場合とまったく同じ量だけカプセル化が減少します。ただし、パブリック アクセサー メソッドを指定すると、メソッドを使用するクライアントがはるかに多くなります。

作成するアプリケーション クラスごとに、クラスの単体テストであるシャドー クラスがあります。単体テスト クラスは、クラスのメソッドを頻繁に呼び出してから、クラスの内部を調べたり、プライベート メソッドを呼び出したりする必要があるため、アプリケーション クラスの仲間です。フレンド クラスであることにより、カプセル化が維持されます。

とにかく、良い議論については、ここを参照してください: http://www.ddj.com/cpp/184401197

于 2009-02-23T14:27:37.527 に答える
1

継承に関する私の記事を述べたので、ここであなたの本当の質問、つまり友情に関する潜在的な問題を回避する方法を助けるかもしれないものがあります.

これを行う方法は、「友達」と共有したいデータにアクセスするための純粋なインターフェイスを作成することです。次に、このインターフェイスをプライベートに実装して、誰も直接アクセスできないようにします。最後に、インターフェイスへの参照を、許可したい選択クラスにのみ渡すことができるメカニズムがあります。

例えば:

// This class defines an interface that allows selected classes to
// manipulate otherwise private data.
class SharedData
{
public:
    // Set some shared data.
    virtual void SettorA(int value) = 0;

    // Get some shared data.
    virtual bool GettorB(void) const;
};


// This class does something with the otherwise private data.
class Worker
{
public:
    void DoSomething(SharedData & data)
    {
        if (data.GettorB() == true)
        {
            data.SettorA(m_id);
        }
    }

private:
    int m_id;
};

// This class provides access to its otherwise private data to
// specifically selected classes.  In this example the classes
// are selected through a call to the Dispatch method, but there
// are other ways this selection can be made without using the
// friend keyword.
class Dispatcher
    : private SharedData
{
public:
    // Get the worker to do something with the otherwise private data.
    void Dispatch(Worker & worker)
    {
        worker.DoSomething(*this);
    }

private:
    // Set some shared data.
    virtual void SettorA(int value)
    {
        m_A = value;
    }

    // Get some shared data.
    virtual bool GettorB(void) const
    {
        return (m_B);
    }

    int    m_A;
    bool   m_B;
};

この例では、SharedData は、データに対して何ができるか (つまり、何が設定可能で、何が取得専用か) を決定するインターフェイスです。Worker は、この特別なインターフェイスへのアクセスが許可されているクラスです。Dispatcher はインターフェイスを非公開で実装するため、Dispatcher インスタンスにアクセスしても特別な共有データにアクセスできるわけではありませんが、Dispatcher にはワーカーがアクセスできるメソッドがあります。

于 2009-02-23T16:31:07.747 に答える
0

このパターンは意味がありますか

あまりない。詳細を教えていただけますか?

  • 通常、異なるクラス間で友情を築きます。これは構文的に有効です。なぜピアはピアの友達になるのでしょうか?
  • プライベート関数は、メンバー関数のみが使用できます。クライアントコードからそれらを呼び出すのは間違っています。

友情がカプセル化を破るかどうかという質問については、FAQをご覧ください。

申し訳ありませんが、ロバート、名前を読み間違えました。

于 2009-02-23T12:08:50.997 に答える
0

健康的ではありません、それはカプセル化のベストプラクティスを破ります

ええ、はい、いいえ。

必要なデータにアクセスするためのメソッドを使用しない場合にのみ、カプセル化を破ることができます。

言うまでもなく、カプセル化は、実装の詳細を変更した場合に、外部クラスを書き直さなければならないのを防ぐための方法にすぎません。実装を変更した場合でも、とにかくクラスを作り直す必要があります。

于 2009-02-23T12:34:48.203 に答える
0

健全ではありません。カプセル化のベスト プラクティスに違反しています

于 2009-02-23T12:05:16.293 に答える
0

最初に言いたいのは、継承では、適切なクラス階層は、コピー コンストラクターや演算子のオーバーロードなどを機能させるためにプライベート データを公開する必要がないということです。例えば:

class Sub
    : public Base
{
public:
    Sub(const std::string & name)
        : Base(),
          m_name(name)
    {
    }

    Sub(const Sub & src)
        : Base(src),
          m_id(src.m_name)
    {
    }

    Sub & operator=(const Sub & rhs)
    {
        if (&rhs != this)
        {
            Base::operator=(rhs);

            m_name = rhs.m_name;
        }

        return (this);
    }

protected:
    virtual Debug(void)
    {
        std::cout << "Sub [m_name = " << m_name << "]" << std::endl
                  << "+- ";

        Base::Debug();
    }

private:
    std::string m_name;
};

class Base
{
public:
    Base(int id)
        : m_id(id)
    {
    }

    Base(const Base & src)
        : m_id(src.m_id)
    {
    }

    Base & operator=(const Base & rhs)
    {
        if (&rhs != this)
        {
            m_id = rhs.m_id;
        }

        return (this);
    }

protected:
    virtual Debug(void)
    {
        std::cout << "Base [m_id = " << m_id << "]" << std::endl;
    }

private:
    int m_id;
};

この例では、サブクラスはコピー コンストラクターと代入演算子の両方で基本メンバーをインスタンス化できます。過度に工夫された Debug メソッドでさえ、基本クラスのプライベート メンバーにアクセスすることなく機能します。

于 2009-02-23T15:58:12.787 に答える