多くのtypedef
sを含む次のようなC++コードを見ました。
C++プリミティブを使用するのではなく、このようにtypedef
を多数使用する利点は何ですか?これらの利点も達成できる別のアプローチはありますか?
結局、データはすべてメモリに保存されるか、ビットおよびバイトとしてネットワーク経由で送信されますが、それは本当に重要ですか?
types.h:
typedef int16_t Version;
typedef int32_t PacketLength;
typedef int32_t Identity;
typedef int32_t CabinetNumber;
typedef int64_t Time64;
typedef int64_t RFID;
typedef int64_t NetworkAddress;
typedef int64_t PathfinderAddress;
typedef int16_t PathfinderPan;
typedef int16_t PathfinderChannel;
typedef int64_t HandsetSerialNumber;
typedef int16_t PinNumber;
typedef int16_t LoggingInterval;
typedef int16_t DelayMinutes;
typedef int16_t ReminderDelayMinutes;
typedef int16_t EscalationDelayMinutes;
typedef float CalibrationOffset;
typedef float AnalogValue;
typedef int8_t PathfinderEtrx;
typedef int8_t DampingFactor;
typedef int8_t RankNumber;
typedef int8_t SlavePort;
typedef int8_t EventLevel;
typedef int8_t Percent;
typedef int8_t SensorNumber;
typedef int8_t RoleCode;
typedef int8_t Hour;
typedef int8_t Minute;
typedef int8_t Second;
typedef int8_t Day;
typedef int8_t Month;
typedef int16_t Year;
typedef int8_t EscalationLevel;
オーバーフローを回避するために、特定の事柄に常に同じ型を使用するようにすることは理にかなっているように見えますが、 "int"がほとんどどこでも使用されているコードがよく見られます。 typedef
ingは、次のようなコードにつながることがよくあります。
DoSomething(EscalationLevel escalationLevel) {
...
}
次に、どのトークンが実際にパラメータを記述しているのか疑問に思います:パラメータタイプまたはパラメータ名?
パラメータの名前は、それが何を意味するかを説明する必要があります-あなたの場合、エスカレーションレベル。タイプは値がどのように表されるかです。例のようにtypedefを追加すると、関数シグネチャのこの部分が難読化されるため、お勧めしません。
Typedefはテンプレートに役立ちます。たとえば、32ビットから64ビットのプラットフォームに移行するときなど、特定のパラメーターに使用するタイプを変更する場合にも役立ちます。
最初は「どうしてだろう」と思ったのですが、そのような長さで型を分けようとすると、言語を上手く利用できるようになりました。エイリアスを使用する代わりに、実際にタイプを定義します。
class AnalogueValue
{
public:
// constructors, setters, getters, etc..
private:
float m_value;
};
次の間にパフォーマンスの違いはありません。
typedef float AnalogueValue;
AnalogValue a = 3.0f;
CallSomeFunction (a);
そして:
AnalogValue a (3.0f); // class version
CallSomeFunction (a);
また、パラメーターの検証とタイプセーフを追加できるという利点もあります。たとえば、プリミティブ型を使用してお金を扱うコードを考えてみましょう:
float amount = 10.00;
CallSomeFunction(amount);
丸めの問題の他に、浮動小数点に変換できるすべての型も許可されます。
int amount = 10;
CallSomeFunction(amount);
この場合は大した問題ではありませんが、暗黙的な変換はバグの原因を突き止めることが困難な場合があります。 typedef
はタイプエイリアスにすぎないため、ここでは役に立ちません。
新しい型を使用することは、キャスト演算子をコーディングしない限り、暗黙的な変換がまったくないことを意味します。これは、暗黙的な変換を許可するため、特に悪い考えです。追加のデータをカプセル化することもできます。
class Money {
Decimal amount;
Currency currency;
};
Money m(Decimal("10.00"), Currency.USD);
CallSomeFunction(m);
それを実現するためのコードを記述しない限り、他には何もその関数に適合しません。偶発的な変換は不可能です。必要に応じて、面倒な作業をせずに、より複雑な型を記述することもできます。
このようなプリミティブ型にtypedefを使用すると、Cスタイルのコードのように見えます。
C++では、たとえばEventLevel
やHour
などの関数をオーバーロードしようとするとすぐに、興味深いエラーが発生します。そのため、追加の型名はほとんど役に立たなくなります。
私たち(私たちの会社)は、C++で多くのことを行っています。コードの理解と維持に役立ちます。これは、チーム間で人を移動したり、リファクタリングを行ったりするときに便利です。例:
_typedef float Price;
typedef int64_t JavaTimestmap;
void f(JavaTimestamp begin, JavaTimestamp end, Price income);
_
表現型から次元名にtypedefを作成することは良い習慣だと私たちは信じています。この新しい名前は、ソフトウェアの一般的な役割を表します。パラメータの名前はlocal roleです。 _User sender, User receiver
_のように。 void register(User user)
など一部の場所では冗長になる可能性がありますが、問題とは見なしていません。
後で、予約の特別な丸め規則のために、float
は価格を表すのに最適ではないという考えを持っているかもしれません。そのため、BCDFloat
(2進化10進数)型をダウンロードまたは実装し、typedefを変更します。 float
からBCDFloat
への検索および置換作業はありません。これは、コードにさらに多くの浮動小数点が存在する可能性があるという事実によって強化されます。
これは特効薬ではなく、独自の注意事項がありますが、使用するよりも使用する方がはるかに良いと考えています。
typedef
は基本的にtype
のエイリアスを与えることを可能にします。
これにより、長い入力を回避する柔軟性が得られますtype names
何度も繰り返し、type
をより読みやすくします。エイリアス名はtype
の意図または目的を示します。
プロジェクトでtypedef
を使用して読みやすい名前を付けたい場合は、選択の問題です。
通常、タイプするのに異常に長い場合を除いて、プリミティブタイプでtypedef
を使用することは避けます。パラメータ名をわかりやすくします。
このようなtypedefの使用は、それらを使用する人がいる限り、問題ありません基礎となる表現について何も知る必要はありません。たとえば、PacketLength
オブジェクトをprintf
またはscanf
に渡す場合、正しい変換指定子を選択できるように、実際の型を知る必要があります。このような場合、typedefは、何も見返りに購入せずに、難読化のレベルを追加するだけです。オブジェクトをint32_t
として定義したばかりかもしれません。
各型に固有のセマンティクス(許容範囲や値など)を適用する必要がある場合は、typedefを作成するだけでなく、その型を操作するための抽象データ型と関数を作成する方がよいでしょう。
私はこのようなことは決してしません。それらがすべて同じサイズであることを確認することは1つのことですが、その場合は、それらを整数型として参照するだけで済みます。