同じメンバーで異なる名前の2つの構造体は、良いアイデアですか?


49

私は極座標とデカルト座標の両方を扱うプログラムを書いています。

それがポイント、との1の種類ごとに二つの異なる構造体を作成しても意味がないXし、Yメンバーと1はであるRThetaメンバー。

それとも、あまりにも多くのことをされ、でちょうど1構造体を持っている方が良いですfirstし、secondメンバーとして。

私が書いているのは簡単で、あまり変わりません。しかし、私はデザインの観点から何が良いのか興味があります。

最初のオプションの方が良いと思います。より読みやすく、型チェックの利点が得られます。


11
私は常に目的ごとに新しい構造体/クラスを作成します。3Dベクトルが必要です。3つのfloatを持つ3d_vector構造体を作成します。uvw表現が必要です。3つのフロートを持つstruct texture_coordsを作成します。3D環境での位置が必要です。3つのフロートで構造体の位置を作成します。ポイントを取得します。同じものを全面的に使用するよりもはるかに読みやすくなります。同じものを複数回定義することに煩わされる場合は、基本の3-float-structを使用し、同じ構造になるように複数の名前を定義します。
ケビン

彼らがいくつかの一般的な方法を持っているなら、たぶん1つでしょう。あなたは2つの平等を比較する必要がありますか?
パパラッチ

8
@Sidneyあなたが絶対に機能を必要としない限り、私はそうしません。2つの表現間で変換するには、sin / arcsin操作を行う必要があります。これにより、変換を行うたびに最下位ビットにビットビットが少し導入されます。イベントの頻度とイベント間の時間(xと1 / x)の両方の表現を提供するクラスを処理しようとしたときに経験したのと同じような痛みを感じることはほぼ確実です。クラス内でどの表現が標準的であるかを追跡し、すべての丸めの頭痛に対処することは、もう一度やりたいことではありません。
ダン・ニーリー

3
多くのものを表すことができるデータ型の良い例は文字列ですが、「stringy typed」はアンチパターンです。あなたの例について。両方の座標系をサポートするタイプのドット積を実装してください。
ネイサンクーパー

1
型の安全性の利点を得るために、必要に応じて異なる型を使用するように努力する必要があります-これにより、間違った型を関数に送信したり、関数から送信したりできなくなります(プログラミング言語に応じて、コンパイル時の正確性チェックを使用)。あるタイプのすべての真の用途を見つけることができるため、メンテナンスが容易になります(タイプが混同されることによる誤った使用を取得することなく)。
エリックエイド

回答:


17

私は両方のソリューションを見てきましたので、間違いなくコンテキストに依存しています。

読みやすくするために、提案されているように複数の構造体を持つことは非常に効果的です。ただし、環境によっては、これらの構造体に一般的な操作を行いたい場合があり、matrix * vector操作などのコードの複製を見つけます。特定の操作がベクターのフレーバーで使用できない場合、誰もそこに移植していないため、イライラする可能性があります。

究極の解決策(最終的に提供しました)は、関数get <0>()get <1>()およびget <2>()を使用してCRTPテンプレートベースクラスを作成し、一般的な方法で要素を取得することです。これらの関数は、この基本クラスから派生するデカルトまたはポーラー構造体で定義されます。それはすべての問題を解決しますが、かなり馬鹿げた代償を伴います:テンプレートのメタプログラミングを学ぶ必要があります。ただし、テンプレートメタプログラミングがプロジェクトにとって既に公正なゲームである場合は、適切な一致である可能性があります。


1
あなたの答えはとても興味深いです。例を提供することは可能ですか?
全能の

1
私がやったことの例として、極、デカルト、およびそれらのベクトルをフィルタリングするFIRがあります。数学は非常によく似ていて、サンズアングル(アン)ラッピング、コードはパフォーマンス上の理由からとにかく複製された部分があり、同じ場所にはテンプレートを使用しました。すべてのものに異なる名前を使用しました。Cortの「極端な解決策」は、いくつかの重複を節約できたかもしれませんが、それらのほとんどすべてが重複しているわけではありません。
ユージンリャブツェフ

1
私の最初の反応は、このような状況はキャストによってより良く解決されるということでしたが、ややリスクがあることが判明しました。
200_success

114

はい、それは非常に理にかなっています。

構造体の値は、便利な名前でデータをカプセル化するだけではありません。値は、意図を成文化するため、コンパイラはいつかそれらに違反しないことを検証するのに役立ちます(たとえば、極座標セットをデカルト座標セットに間違えます)。

人々はこのような微妙な詳細を覚えるのは苦手ですが、大胆で独創的な計画を立てるのは得意です。コンピューターは細部を微調整するのが得意で、創造的な計画が得意ではありません。そのため、できるだけ多くの詳細なメンテナンスをコンピューターに移し、大まかな計画に自由に取り組むことを常にお勧めします。


6
+1コンピューターを使用してコンピューターの得意なことを実行し、自分の仕事に脳を集中させることの完全な説明。
ブライアン

8
「プログラムは、人々が読むために、そして偶然にマシンが実行するためだけに書かれるべきです。」-AbelsonとSussmanによる「コンピュータープログラムの構造と解釈」から。
hlovdal

18

はい。デカルト座標と極座標はどちらも(その場所では)非常に賢明な座標表現スキームですが、理想的には混同しないようにする必要があります(デカルト座標{1,1}がある場合、極座標{1,1とは非常に異なる点です) })。

必要に応じて、それはまた、同様の方法で、コーディネートのインタフェースを実装する価値があるかもしれX()Y()Displacement()およびAngle()(または可能性Radius()Theta()、に応じて)を。


デカルト座標と極座標の操作は異なるため、OPがこれらの構造体からクラスを作成している場合はさらに重要です。
マインドウィン

1
最後の段落に+1、それが理想的な解決策です。ポイントはスペースであり、オブジェクトです。そのポイントの内部表現は重要ではありません。もちろん、実際の懸念事項(パフォーマンス、丸め誤差)が問題になる場合があります。それはすべてこれが何に使用されているかに依存します。
BlueRaja-ダニーPflughoeft

また、この例では、変更することはほとんどありませんが、それらが他の2つのクラスである場合、ある時点で分岐する可能性があることを示すものはありません。
dyesdyes

8

最後に、プログラミングの目標は、トランジスタビットを切り替えて有用な作業を行うことです。しかし、そのような低いレベルで考えると、手に負えない狂気につながります。そのため、複雑さを隠すのに役立つ高レベルのプログラミング言語があります。

firstおよびという名前のメンバーで1つの構造体を作成する場合second、名前は何の意味もありません。基本的にはメモリアドレスとして扱います。これは、高レベルのプログラミング言語の目的に反します。

さらに、それらがすべてaとして表現できるからといって、doubleそれらを同じ意味で使用できるというわけではありません。たとえば、θは無次元の角度ですが、yには長さの単位があります。 型は論理的に置換可能でないため、2つの互換性のない構造体にする必要があります。

本当にメモリ再利用のトリックをプレイする必要がある場合(そして、ほぼ確実にそうすべきではありません)union、Cの型を使用して意図を明確にすることができます。


最高の答え、私見
ディーンラドクリフ

2

まず、@ Kilian-fothの完全に適切な答えによると、両方を明示的に持っています。

ただし、追加したいのは次のとおりです。

質問:のペアと見なされた場合、両方に一般的な操作が本当にありdoubleますか?これは、独自の条件で両方に適用される操作があるということと同じではないことに注意してください。たとえば、 'plot(Coord)'は、Coord極座標かデカルト座標かを考慮します。一方、ファイルに永続化すると、データがそのまま扱われます。あなたは本当に一般的な操作を持っていない場合は、基本クラスを定義する、または変換器を定義するのいずれかを検討しstd::pair<double, double>たりtuple、またはものは何でもあなたの言語を持っています。

さらに、1つのアプローチは、1つのCoordinateタイプをより基本的なものとして扱い、もう1つのCoordinateタイプを単にユーザーまたは外部の対話のサポートとして扱うことです。

したがって、すべての基本操作がコーディングされていることを確認してCartesianから、への変換PolarをサポートできCartesianます。これにより、多くの操作の異なるバージョンを実装する必要がなくなります。


1

言語に応じて、両方のクラスに同様のメソッドと操作があることがわかっている場合、可能な解決策は、クラスを一度定義し、型エイリアスを使用して明示的に型に異なる名前を付けることです。

これには、クラスがまったく同じである限り、1つだけを維持できるという利点がありますが、変更が必要になるとすぐに、タイプが既に使用されているため、それらを使用してコードを変更する必要がありません遠い。

ポリモーフィズムなどが必要な場合は、クラスの使用法に応じて別のオプションとして、両方の新しいタイプへのパブリック継承を使用することで、両方が表す共通タイプと同じパブリックインターフェイスを使用します。これにより、タイプを個別に進化させることもできます。


両方のクラスのメンバーの名前は同じではありません。実際には、名前は2つのクラスの唯一の違いです
モハ全能のラクダ

@ Mhd.Tahawi適切な名前でゲッターとセッターを実装し、使用しているクラスが同じであることを保証しますが、使用する操作に適切な名前を提供します。これはもう少し冗長になりますが、コードを複製する必要が少なくなります。
-Svalorzen

0

この場合、同じメンバー名を使用するのは悪い考えだと思います。コードがエラーになりやすいからです。

シナリオを想像してください。2つのデカルトポイント、pntAとpntBがあります。次に、何らかの理由で、極座標で表現する方が良いと判断し、宣言とコンストラクタを変更します。

ここで、すべての操作が次のようなメソッド呼び出しだけであった場合:

double distance = pntA.distanceFrom(pntB);

大丈夫です しかし、メンバーを明示的に使用するとどうなりますか?比較する

double leftMargin = abs(pntA.x - pntB.x);
double leftMargin = abs(pntA.first - pntB.first);

最初の場合、コードはコンパイルされません。エラーがすぐに表示され、修正できます。ただし、同じメンバー名を持っている場合、エラーは論理レベルでのみ発生し、検出がはるかに困難になります。

非オブジェクト指向言語で記述する場合、間違った構造体を関数に渡すのはさらに簡単です。次のコードを書くのを止めるのは何ですか?

double distance = calculate_distance_polar(cartesianPointA, polarPointB);

一方、異なるデータ型を使用すると、コンパイル中にエラーを見つけることができます。

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