ポインタを格納する以外に選択肢がほとんどない場合がいくつかあります。明らかなものの1つは、バイナリツリーのようなものを作成している場合です。
template <class T>
struct tree_node {
struct tree_node *left, *right;
T data;
}
この場合、定義は基本的に再帰的であり、ツリーノードが持つ可能性のある子孫の数を事前に知ることはできません。ポインタの格納(少なくともいくつかのバリエーション)と、必要に応じて子孫ノードの割り当てにかなり悩まされています。
親オブジェクトに単一のオブジェクト(またはオブジェクトの配列)しかない動的文字列のような場合もありますが、そのサイズは、必要なだけの十分な範囲で変化する可能性があります(少なくとも可能性を提供します)。動的割り当てを使用します。文字列の場合、小さいサイズが一般的であるため、かなり広く使用されている「短い文字列の最適化」があります。この場合、文字列オブジェクトには、ある制限までの文字列用の十分なスペースと、文字列がそれを超えた場合に動的に割り当てることができるポインタが含まれます。サイズ:
template <class T>
class some_string {
static const limit = 20;
size_t allocated;
size_t in_use;
union {
T short_data[limit];
T *long_data;
};
// ...
};
サブオブジェクトを直接格納する代わりにポインタを使用する理由はあまり明白ではありませんが、例外の安全性のためです。明らかな例の1つとして、親オブジェクトにポインタのみを格納する場合、保証を提供swap
するオブジェクトにを提供することは簡単になります(通常はそうなります)。nothrow
template <class T>
class parent {
T *data;
void friend swap(parent &a, parent &b) throw() {
T *temp = a.data;
a.data = b.data;
b.data = temp;
}
};
いくつかの(通常は有効な)仮定のみで:
- ポインタは最初から有効であり、
- 有効なポインタを割り当てると、例外がスローされることはありません
...このスワップが無条件に保証を与えることは簡単ですnothrow
(つまり、「スワップはスローされません」とだけ言うことができます)。ポインタの代わりにオブジェクトを直接格納する場合parent
、条件付きでのみ保証できます(たとえば、swap
Tのコピーコンストラクタまたは代入演算子がスローされた場合にのみスローされます。」)
C ++ 11の場合、このようなポインターを頻繁に使用すると(通常は?)、非常に効率的な移動コンストラクターを簡単に提供できます(これによりnothrow
保証も提供されます)。もちろん、データ(のほとんど)へのポインターを使用することは、高速移動構造への唯一の可能なルートではありません-しかし、それは簡単なものです。
最後に、質問をしたときに頭に浮かんだと思われるケースがあります。関連するロジックが、自動割り当てと動的割り当てのどちらを使用する必要があるかを必ずしも示していない場合です。この場合、それは(明らかに)判断の呼びかけです。純粋に理論的な観点からは、これらの場合に使用するものにはおそらくまったく違いはありません。ただし、実用的な観点からは、かなりの違いが生じる可能性があります。CまたはC++標準のどちらも保証していませんが(またはヒントさえ)どんな種類のものでも、現実には、ほとんどの一般的なシステムでは、自動割り当てを使用するオブジェクトがスタックに配置されます。ほとんどの一般的なシステム(Windows、Linuxなど)では、スタックは使用可能なメモリのごく一部に制限されています(通常、1桁から2桁未満のメガバイトのオーダー)。
これは、任意の時点で存在する可能性のあるこれらのタイプのすべてのオブジェクトが数メガバイト(またはそれ以上)を超える可能性がある場合、データ(少なくともほとんど)が自動的ではなく動的に割り当てられるようにする必要があることを意味します。これを行うには2つの方法があります。使用可能なスタックスペースを超える可能性がある場合に親オブジェクトを動的に割り当てるようにユーザーに任せるか、または割り当てる比較的小さな「シェル」オブジェクトをユーザーに操作させることができます。ユーザーに代わって動的にスペースを空けます。
それが問題になる可能性が非常に高い場合は、ほとんどの場合、ユーザーに強制するのではなく、クラスが動的割り当てを処理することをお勧めします。これには2つの明らかな良い点があります。
- ユーザーはスタックベースのリソース管理(SBRM、別名RAII)を使用できるようになり、
- 限られたスタックスペースの効果は、デザイン全体に「浸透」するのではなく、制限されます。
結論:特に、格納されているタイプが事前にわからないテンプレートの場合、ポインターと動的割り当てを好む傾向があります。サブオブジェクトの直接ストレージは、主に、格納されているタイプが(ほぼ?)常に非常に小さいことがわかっている状況、または動的割り当てが実際の速度の問題を引き起こしていることをプロファイリングが示している状況に予約します。operator new
ただし、後者の場合、そのクラスのオーバーロードなどの代替案に少なくともいくつかを与えます。