2016-04-02 2 views
1

私はStringクラスMyString(宿題)を作成しており、unique_ptr<char[]>Vectorではなく)を返すtoCStringメソッドを提供する必要があります。残念ながら、私は呼び出し側にポインタを返すときに失敗します:結果は常に間違った内容で満たされます - それはスタック上にポインタおよび/または文字配列を作成するようです。固有のptrを正しく返します

unique_ptr<char[]> MyString::toCString() const { 
    char *characters = new char[m_len]; 
    char *thisString = m_string.get(); 
    for (int i = 0; i < m_len; i++) { 
     characters[i] = *(thisString + m_start + i); 
    } 
    const unique_ptr<char[], default_delete<char[]>> &cString = unique_ptr<new char[m_len]>(characters); 
    return cString; 
} 

私はいつも予想どおりの動作をします。問題は発信者のサイトでのみ発生します。私のミスはどこですか?

+1

なぜあなたは、スマートポインタを返すされていますか? _C_文字列は 'char'への単なるポインタです(配列ではありません) – ForceBru

+1

' unique_ptr (characters) 'これは意味をなさないのでコンパイルされません。実際のコードを表示します。 –

+0

スマート・ポインタを参照としてスタックに割り当ててから、関数を終了します。その動作が正しいと思われる場合でも、予測できない動作が発生します。 http://stackoverflow.com/questions/10643563/how-to-return-smart-pointers-shared-ptr-by-reference-or-by-value –

答えて

4

私はそこに受け入れ答えが既にあるが、これで問題が解決しない参照してください。あなたはC文字列をヌル終端していないので、クライアント側の問題が発生しています。

私はm_stringの型がわからないので、しばらくそれがstd :: stringであると仮定します。あなたは、実際のメソッドを自分で翻訳することができます:クリストフの提案を1として

std::unique_ptr<char[]> MyString::toCString() const 
{ 
    // get length (in chars) of string 
    auto nof_chars = m_string.size(); 

    // allocate that many chars +1 for the null terminator. 
    auto cString = std::unique_ptr<char[]>{new char[nof_chars + 1]}; 

    // efficiently copy the data - compiler will replace memcpy 
    // with an ultra-fast sequence of instructions in release build 
    memcpy(cString.get(), m_string.data(), nof_chars * sizeof(char)); 

    // don't forget to null terminate!! 
    cString[nof_chars] = '\0'; 

    // now allow RVO to return our unique_ptr 
    return cString; 
} 

は、ここで再び、のstd :: copy_nの観点で書かれた方法です。 std :: copy [_xxx]関数群はすべて、最後の書き込みを1回過ぎて処理するイテレータを返します。これを使用して、ヌルターミネータの場所の再計算を保存することができます。標準ライブラリは素晴らしいのではないですか?

std::unique_ptr<char[]> MyString::toCString() const 
{ 
    // get length (in chars) of string 
    auto nof_chars = m_string.size(); 

    // allocate that many chars +1 for the null terminator. 
    auto cString = std::unique_ptr<char[]>{new char[nof_chars + 1]}; 

    // efficiently copy the data - and don't forget to null terminate 
    *std::copy_n(m_string.data(), nof_chars, cString.get()) = '\0'; 

    // now allow RVO to return our unique_ptr 
    return cString; 
} 
+0

明らかに、オブジェクトは 'm_string'の部分文字列を表します。したがって、' m_start'と 'm_len'です。しかし、スペースを作ってNULターミネーターを追加することは絶対に正しいです。 –

+0

[古い 'memcpy()'を 'std :: copy()'や 'std :: copy_n()'と置き換えるのは意味がないでしょうか?(http://stackoverflow.com/questions/4707012/stdmemcpy-or-stdcopy-in-performance)を使用していますか? – Christophe

+0

@Christopheは合意した、それはもっと慣用的だろう。 –

2

あなたのようにunique_ptrへの参照を作成しないでください。代わりに、直接unique_ptrをを返す:移動コンストラクタは、すべての世話をします。

return unique_ptr<char[], default_delete<char[]>>(characters); 
+0

また、C++コンパイラがC++ 11をサポートしていることを確認してください。 –

+2

@NathanielJohnson確かに!一方、OPが 'unique_ptr'を使用し、非推奨の' auto_ptr'を使用していなくて、すでにいくつかのコードをコンパイルできるのであれば、C++ 11が与えられたと仮定できます;-) – Christophe

+1

あなたが言ったのは、戻り値最適化はすべてを処理します。値で効率的にリターンするには、移動コンストラクタは必要ありません。 –

1
あなたの質問を編集したので

、そして今あなたが

unique_ptr<char[]> cString = unique_ptr<char[]>{new char[m_len]}; 

第1の改良を使用している:オート使用

auto cString = unique_ptr<char[]>{new char[m_len]}; 

第二の改善:あなたのタグは、C + 11ですが、もしC + 14を使用している場合、std::make_uniqueを次のように使用します。

auto cString = std::make_unique<char[]>(m_len); 

さらに、Scott Meyersの言うとおり、C + 11を使用している場合は、単にmake_unique関数を記述してください。それは難しいことではなく、非常に便利です。

http://ideone.com/IIWyT0

template<class T, class... Types> 
inline auto make_unique(Types&&... Args) -> typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type 
{ 
    return (std::unique_ptr<T>(new T(std::forward<Types>(Args)...))); 
} 

template<class T> 
inline auto make_unique(size_t Size) -> typename std::enable_if<std::is_array<T>::value && std::extent<T>::value == 0, std::unique_ptr<T>>::type 
{ 
    return (std::unique_ptr<T>(new typename std::remove_extent<T>::type[Size]())); 
} 
関連する問題