コンストラクターを構造体に追加する必要がありますか?


13

メンバメソッドを備えた完全なモジュールにできるクラスではなく、c ++構造体を使用してデータ構造を定義することがよくあります。今、私たちはそれらが両方とも同じであることがわかります(大まかに言って)。

構造体をデータのみのエンティティとして頻繁に使用/処理するという事実は、デフォルトのコンストラクタも追加しないという衝動を引き起こします。しかし、コンストラクターは常に優れており、物事を単純化し、エラーを排除するのに役立ちます。

デフォルトのコンストラクタをデータ構造に追加すると、眉をひそめますか?

他の基準が満たされている場合、デフォルトコンストラクターを実装すると、構造体は非POD(プレーンな古いデータ型)になりますか?

物事を視野に入れるために、簡単な例を考えてみましょうが、実際には構造体ははるかに大きくなります。

struct method
{
    char    name[32];
    float   temperature;
    int     duration;
};

メソッドを作成するたびに、値を設定するのを忘れた場合、(控えめに言っても)心配する必要があります。temperatureメソッドを設定してシステムに適用するのを忘れてしまったことを想像してください。このメソッドはランダムに高い値になり、混乱を引き起こします。または、設定するのを忘れたためduration、メソッドが未知の高期間にわたって適用されます。

オブジェクトを保証するコンストラクタを実装するのではなく、毎回オブジェクトを初期化する責任を負うべきなのはなぜですか?


特定の値のみを許可するように強制する必要がある場合、単純な古いデータ型はありません。構造体を初期化する便利な方法が必要な場合は、単純な古い関数がそれを行います。
ドーバル14

これらのコンストラクタが何をしているかに依存します。基本的な方法でフィールド値を設定するだけの場合、単純な構造体にコンストラクターを作成することは完全に合理的だと思います。
ロボットを

@Dovalそれは問題ではありません、私は投稿を更新しました。スティーブン:はい、コンストラクターは単にデフォルト値を割り当てるだけです。
ザダネ14

@StevenBurnap:コンストラクターが基本的な方法でフィールド値を設定する以上のことを行う場合は、コンストラクターを持つ方が適切です。構造体の上でも。
ジャン・ヒューデック

2
つまり、コンストラクターで複雑なロジックを見つけ始めたら、おそらくそれをクラスにする必要があるでしょう。(私見)しかし、間の唯一の実際の差として、それは実際には単なるスタイルの問題だstructとはclassプライベートとパブリックの他の1つはデフォルトということです。
ロボット

回答:


13

コンストラクターを構造体に追加することが適切な場合とそうでない場合があります。

コンストラクター(任意のコンストラクター)を構造体に追加すると、その構造体で集計初期化子を使用できなくなります。そのため、デフォルトのコンストラクターを追加する場合、値を初期化するデフォルト以外のコンストラクターも定義する必要があります。ただし、常にすべてのメンバーを初期化するようにする場合は適切です。

コンストラクター(任意のコンストラクター)を追加すると、非PODになりますが、C ++ 11では、以前はPODのみに適用されていたルールのほとんどが標準レイアウトオブジェクトに適用されるように変更されており、コンストラクターの追加はそれを壊しません。したがって、失うのは基本的に集約初期化子だけです。しかし、それはしばしば大きな損失でもあります。



-1

素早い回答:

何を達成したいかによります。

長く、長く、退屈な答え:

釘を打ちます。

私は通常、「C ++」で「Struct(s)」でメソッドを宣言できることを嫌います。できれば、必要なメソッドには明示的な「クラス(es)」を使用し、フィールドのみにはPOD「構造(s)」を使用してください。

それでも、次のようないくつかの基本的な簡単な操作に同意します。

  • 初期値を割り当てます(「コンストラクター」)
  • 構造のコピーを作成します(「コピーコンストラクター」)
  • 既存の構造に値を割り当てます(「オーバーロード割り当て演算子」)

必要であり、そのような状況では、構造のための方法は理にかなっています。

提案

別の潜在的な解決策は、POD構造を使用することですが、それでも概念的にそれらをクラスおよびオブジェクトとして扱います。

これらの宣言を名前空間でラップし、最も重要なアクションのためにグローバル関数を追加します。

コード宣言は次のようになります。

namespace Customers
{
  struct CustomerStruct
  {
    char[255] FirstName;
    char[255] LastName;
    int Age;
    bool IsAlive;
    bool IsMarried;
  }; // struct

  CustomerStruct* CreateCustomer
  (
    char* NewFirstName;
    char* NewLastName;
    int NewAge;
    bool NewIsAlive;
    bool NewIsMarried;
  )
  {
    CustomerStruct* NewCustomer = new CustomerStruct();
      NewCustomer->FirstName = NewFirstName;
      NewCustomer->LastName = NewLastName;
      NewCustomer->Age = NewAge;
      NewCustomer->IsAlive = NewIsAlive;
      NewCustomer->IsMarried = NewIsMarried;
    return NewCustomer;
  } // CustomerStruct* CreateCustomer (...)

} // namespace

ソリューションを適用するコードは、次のようなものです。

#include <Customers>

using Customers;

int main (...)
{
   int ErrorCode = 0;

   CustomerClass* ThisCustomer =
     Customers::CreateCustomer
      ("John", "Doe", 23, true, true);

   // do something with "ThisCustomer"

   delete ThisCustomer;

   return ErrorCode;
} // int main(...)

この代替アプローチは、データの巨大なメモリ割り当てが必要な場合、または他の低レベル共有ライブラリと対話する場合に適しています。

このアプローチは、いくつかの変更を加えて、ゲーム開発に適用されます。

追加

個人的には、「C ++」の構文拡張、またはこの問題を解決する新しい「C ++」ベースのPLを検討しています。

// "Plain Old Data" Structure
// No Methods, No "Functors", allowed
strict struct CustomerStruct
{
  char[255] FirstName;
  char[255] LastName;
  int Age;
  bool IsAlive;
  bool IsMarried;
}; // strict struct

// Object Oriented "Plain Old Data" Structure
// Yes, Methods and "Functors" allowed
relaxed struct CustomerStruct
{
  char[255] FirstName;
  char[255] LastName;
  int Age;
  bool IsAlive;
  bool IsMarried;

  public void Foo();
  public void Bar();

  public (void*) (SomeFunctor) ();
}; // relaxed struct

// Class and Object Oriented
class CustomerClass
{
  public char[255] FirstName;
  public char[255] LastName;
  public int Age;
  public bool IsAlive;
  public bool IsMarried;

  public void Foo();
  public void Bar();
}; // class

乾杯。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.