を使用する必要がありますstd::list
。実際には、 を使用する必要がありますstd::vector
。これは、ほとんどの実用的な目的で高速であり (リストは、オブジェクトが非常に大きいか、構築するのに非常にコストがかかる場合にのみ高速です)、ランダムアクセスを必要としないためです。
new T(*(cctorList->info));
cctorList
( list&
) がなく、メンバーもないため、コンパイルさoperator->
れませんinfo
。
コピー コンストラクターはpush_back
、反復などの他のより基本的な操作の観点から最適に実装されます。したがって、最初にそれらを実行すると、コピー コンストラクターは次のようになります。
template <class T>
list<T>::list(const list &cctorList)
{
std::copy(begin(cctorList), end(cctorList), std::back_inserter(this));
}
実際、そのコンストラクターをテンプレート化するだけです。
template <class T, class Collection> list(const Collection &cctorList)
(本体はそのまま)。これはコピー コンストラクターとして機能しますが、暗黙的に T に変換できる任意の型の他のコレクションからコピーすることもできます。
実際のデータはvalueで保持する必要があります。つまり、次のnode
ように定義する必要があります
struct node
{
node *next;
node *previous;
T info;
}
node
とにかく値をコピーしているので、 と のために2 つの別々の割り当てを行う必要はありませT
ん。
編集:あなたは概念を学びたいと言います。しかし、最新の C++ の最も重要な概念は、アルゴリズムの構成です。それらの定義はしばしば自明です。の基本的な実装は次のstd::copy
とおりです。
template <typename InputIterator, typename OutputIterator>
OutputIterator copy(InputIterator begin, InputIterator end, OutputIterator out) {
for(;begin != end; ++out, ++begin) *out = *begin;
}
現在、これは何も割り当てられていないようです。その秘訣は にありback_insertion_iterator
ます。挿入反復子は、シーケンスを事前に割り当てずにこれを機能させるためのトリックです。operator*
基になるコレクションでusingを定義しpush_back
、 を無視しoperator++
ます。これは、「出力イテレータ」の概念を満たします。これは、これら 2 つの呼び出しが厳密にインターリーブされている場合にのみ機能することが保証され、単純な古い配列から出力ストリームまで、多くのことでアルゴリズムが機能するためです。
他の部分は、自明な定義は正しいものの、ライブラリで使用される実際の定義ではないということです。ライブラリ内の実際の定義は最適化されています。たとえば、通常std::copy
、入力イテレータがそれらの距離を知っているかどうか、および出力が挿入演算子であるかどうかを確認して、操作でシーケンスしreserve
、割り当てを回避するために呼び出します。これらは最適化であり、標準ライブラリの実装の詳細に依存します。
それらを理解したい場合は、標準ライブラリから物事の基本的な実装を書き留めて、それらが同じように機能することをテストできます。ただし、標準ライブラリが物事を定義する方法に従って、std::copy
、std::swap
、挿入イテレータ アダプタなどの単純なヘルパー ビットからそれらを構築する必要があります。標準ライブラリを見ると、ほとんどの関数はワンライナーです!
Edit2: また、標準ライブラリが提供するすべての汎用性により、汎用性が十分でないと批判されるビットがまだあります。たとえば、GotW #84: Monoliths "Unstrung"では、どのメソッドstd::string
を汎用アルゴリズムに変換できるかについて説明しています。