2

私は、互いに独立しCContainerているいくつかのメンバーを持つCMemberXクラスと、を使用する他のクラスを持っています。CMemberYCClientACClientBCContainer

#include "MemberX.h"
#include "MemberY.h"

class CContainer
{
public:
    CMemberX & GetX() const { return m_x; }
    CMemberY & GetY() const { return m_y; }

private:
    CMemberX m_x;
    CMemberY m_y;
};

前方宣言ととの動的割り当てを使用しCClientてクラスの1つを変更するときに、すべてのクラスを再コンパイルする必要がないようにしたいと思います。CMemberm_xm_y

最初に、メンバーにポインターを作成しました。

// Container.h
class CMemberX;
class CMemberY;

class CContainer
{
public:
    CContainer();
    ~CContainer();

    CMemberX & GetX() const { ASSERT(m_pX != NULL); return *m_pX; }
    CMemberY & GetY() const { ASSERT(m_pY != NULL); return *m_pY; }

private:
    CMemberX* m_pX;
    CMemberY* m_pY;
};

// Container.cpp
#include "Container.h"
#include "MemberX.h"
#include "MemberY.h"

// Allocate members on heap
CContainer::CContainer() : m_pX(new CMemberX()), m_pY(new CMemberY()) {}
CContainer::~CContainer() { delete m_pX; delete m_pY; }

次に、ポインタの代わりに参照を使用することもできると思ったので、元のコードのように見えます。

// Container.h
class CMemberX;
class CMemberY;

class CContainer
{
public:
    CContainer();
    ~CContainer();

    CMemberX & GetX() const { return m_x; }
    CMemberY & GetY() const { return m_y; }

private:
    CMemberX & m_x;
    CMemberY & m_y;
};

// Container.cpp
#include "Container.h"
#include "MemberX.h"
#include "MemberY.h"

// Allocate members on heap
CContainer::CContainer() : m_x(*new CMemberX()), m_y(*new CMemberY()) {}
CContainer::~CContainer() { delete &m_x; delete &m_y; }

ポインターメンバーについて私が気に入らないのは、ポインターが実行可能であるか、実行時にオブジェクトが置き換えられる可能性があるように見えることですが、そうではありません。NULL

参照について私が気に入らないのは、CTorとDTorのコードが少しハックに見えることです。

どちらのアプローチが望ましいですか?より良い解決策はありますか?

コピー/割り当てに関する注意:クラスのインスタンスは、CContainerいかなる状況でも相互にコピーまたは割り当てられません。

4

7 に答える 7

5

それがconst変数の目的だと思います:

CMember * const m_x;

初期化後にm_xを変更することはできません。

于 2010-01-04T12:55:06.173 に答える
4

所有権のセマンティクスがあるときに参照を使用するのは少し意外だと思います。すべてのことを考慮して、必ずしもそれが悪い考えになるわけではありませんが、それは不利になります。

両方の場合にのみ、参照をメンバーとして使用したことがあると思います。

  • オブジェクトがコンストラクターに提供されます。これは、このオブジェクトより長く存続するために必要です。
  • とにかく割り当ては禁止されています。

したがって、たとえば、ファクトリやサービス オブジェクトなどの注入された依存関係が適している可能性があります。それに対して、C++ では、多くの場合、オブジェクトではなくテンプレート パラメーターを使用して依存関係を注入することを好むため、問題は発生しない可能性があります。

また、C++ を長く使用するほど、よほどの理由がない限り、型を Assignable にしたいという気持ちが強くなります。必要な方法でコンパイル時の依存関係を減らすための通常のトリックは、理由により、「Rimpl」ではなく「Pimpl」です。オブジェクト メンバーから参照メンバーに切り替えることで、以前はおそらくコピー可能だったクラスを、デフォルトでコピー不可にすることになります。この実装の詳細は、クラスのインターフェースを制約するべきではありません。Pimpl を使用すると、割り当てとスワップをきれいに実装できます。これらの参照では、両方のメンバーを割り当てるか交換する必要があります。2 番目のスワップが失敗した場合、強力な例外保証が失われます。ただし、CMemberX および CMemberY クラスに失敗しない割り当てとスワップがある場合、これは問題ではありません。

したがって、この場合の参照は気に入らないと思いますが、残りのコードは見ていません。おそらく、割り当てに関する懸念がまったく当てはまらない理由があるのでしょう。たとえば、CContainer 自体が RAII クラスである場合、通常、サポートする必要があるライフサイクル操作は構築と破棄だけです。

于 2010-01-04T13:51:39.400 に答える
2

ここでは、参照をメンバーとして使用することの望ましさについて多くの質問があり(たとえば、メンバーデータ内のポインターまたは参照を優先する必要がありますか?)、多数意見(これも私のものです)は-しないでください。ポインタを変更したくない場合は、ポインタをconstにします-コードを考えると、ポインタがNULLになる可能性がある方法がわかりません。

于 2010-01-04T12:54:02.177 に答える
1

動的割り当ては、CClientとCContainerを再コンパイルする必要がないという点で何もしません。

前方宣言を使用する場合に許可される唯一の使用法は、前方宣言型へのポインターを宣言することです。

メソッドまたは前方宣言型のメンバーを使用するとすぐにコンパイルされません。コンパイラは、使用しているものの完全な型を認識している必要があります。

つまり、再コンパイルする必要がない[前方宣言型へのポインターを宣言するだけです]か、実際にCContainerを使用する場合は、常に再コンパイルする必要があります。

于 2010-01-04T12:54:25.563 に答える
1

Steve Jessop はすでに pImpl イディオムについて言及していますが、まだこのイディオムに出くわしていない場合は、これを確認する必要があると思います: Compilation Firewalls

于 2010-01-04T17:27:14.840 に答える