質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • C++14

    53questions

unique_ptrで確保した領域を外部関数へ渡す方法をご教授お願いします

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 4,088

wake_up_kemeko

score 96

 unique_ptrを以下のような外部関数へ直接渡す方法を知りたいです。

unique_ptr型から生ポインタを引数としている関数を呼び出したかったのですが、getメソッドを使って渡すと、以下のエラーが出力されました。

  • getメソッド利用
// 動かない 11/20 訂正
  std::unique_ptr<wchar_t> sjis_to_utf16(const std::string str) {
        const int wchar_len = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
        std::unique_ptr<wchar_t> utf16_str(new wchar_t(wchar_len));
        ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, utf16_str.get(), wchar_len);
        return std::move(utf16_str);
    }
  • エラー
Debug Error!
Program:***.exe

HEAP CORRUPTION DCETECTED: after Normal block at (アドレス)
CRT detected that the application wrote to memory after end of heap buffer.


また、先にunique_ptrの要領のみを定義してから生ポインタにgetメソッドの返り値を入れて外部関数に渡しても同様のエラーが発生しました。

  • 生ポインタを使用すれば、動くことは確認できております。
// 動く
  std::unique_ptr<wchar_t> sjis_to_utf16(const std::string str) {
        const int wchar_len = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
        wchar_t* p_utf16_str = new wchar_t[wchar_len];
        ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, p_utf16_str, wchar_len);
        std::unique_ptr<wchar_t> utf16_str(p_utf16_str);
        return std::move(utf16_str);
    }
  • 追記 11/20
    自作した関数に.getメソッドで渡すと、正常に動作しているようでした。
    MultiByteToWideChar()側に問題があるのかもしれません。
    型の違いが原因である気がしてきました…
    MultiByteToWideCharのリファレンス

int MultiByteToWideChar(   UINT CodePage,         // コードページ   DWORD dwFlags,         // 文字の種類を指定するフラグ   LPCSTR lpMultiByteStr, // マップ元文字列のアドレス   int cchMultiByte,      // マップ元文字列のバイト数   LPWSTR lpWideCharStr,  // マップ先ワイド文字列を入れるバッファのアドレス   int cchWideChar        // バッファのサイズ );

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

こんにちは。

「生ポインタを使用すれば、動くことは確認できております。」の使い方は正しいですよ。

また、「getメソッド利用」のソースはp_utf16_str未定義でコンパイルに通らないと思いますし、get()も使ってないのでソースを間違われているのではないかと思います。


【質問で訂正されたコードのお試し動作】

#include <iostream>
#include <string>
#include <memory>
#include <windows.h>

  std::unique_ptr<wchar_t> sjis_to_utf16(const std::string str) {
        const int wchar_len = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
        std::unique_ptr<wchar_t> utf16_str(new wchar_t(wchar_len));
        ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, utf16_str.get(), wchar_len);
        return std::move(utf16_str);
    }

int main()
{
    std::unique_ptr<wchar_t>    utf16=sjis_to_utf16("テスト");

    std::wcout.imbue(std::locale(""));
    std::wcout << utf16.get() << std::endl;

    return 0;
}

Visual Studio 2015でコマンド・プロンプトでビルドしました。
実行すると、下記のように表示されます。

テスト


【raccyさんの回答を見て追記】
配列を破棄する時は、deleteではなくdelete[]を呼ばないといけません。
std::unique_ptr<>のdefault_deleteはTを渡したら単にdeleteするだけのようです。
しかし、T[]を渡せば、delete[]を呼ぶよう特殊化されているようです。

ですので、

誤> std::unique_ptr<wchar_t> utf16_str(new wchar_t(wchar_len));
正> std::unique_ptr<wchar_t[]> utf16_str(new wchar_t[wchar_len]);

と、しておくべきですね。

因みにmsvcはdeleteとdelete[]の実装は同じだったと記憶してます。なので、msvcでは「たまたま」問題が起きなかったということになります。

実はほとんど同じ処理を私のソフトでもやっていて、それと見比べていたのですが、自分のはしっかりwchar_t[]にしてました...見落としが多い。orz

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/20 12:39

    ご指摘ありがとうございます。
    おっしゃられる通り、掲示するソースに誤りがありました。
    訂正いたしましたので、もしよろしければご確認お願いいたします。

    キャンセル

  • 2016/11/20 13:14

    問題なく動きますよ。テスト・コードを回答に追記します。

    キャンセル

  • 2016/11/20 13:40 編集

    ご確認ありがとうございます。
    ご掲示いただきましたソースを当方の`Windows 10 Enterprise`, `Microsoft Visual Studio Community 2015`, `Debugモード`にて、`Debugなしで実行`を行ったところ、テストの文字は表示されました。
    しかし、プログラムは終了せずフリーズしてしまいます。

    `Debugありで実行`を行うと、
    ```
    Debug Error!
    Program:***.exe

    HEAP CORRUPTION DCETECTED: after Normal block (#164)at 0x00F1AD38.
    CRT detected that the application wrote to memory after end of heap buffer.

    (Press Retry to debug the application)
    [中止][再試行][無視]
    ```
    のポップアップウィンドウが表示されます。

    キャンセル

  • 2016/11/20 14:17

    あ、ケアレスミスですね。

    誤> std::unique_ptr<wchar_t> utf16_str(new wchar_t(wchar_len));
    正> std::unique_ptr<wchar_t> utf16_str(new wchar_t[wchar_len]);

    キャンセル

  • 2016/11/20 14:35

    Σそんなところでしたか!
    おかげさまで、すっきりすることができました。
    長らくお付き合いいただきありがとうございました。
    m(__)m

    キャンセル

+1

配列に対するuniqu_ptrの取り方が間違っているようです。

std::unique_ptr<wchar_t>だと、ただのwchar_tに対するポインタにしかならず、確保される領域もwchar_t一個分です。配列を使う場合はT[]のような型指定が必要らしく、std::unique_ptr<wchar_t[]>と書く必要があります。初期化もnew wchar_t(wchar_len)ではなくnew wchar_t[wchar_len])になります。new wchar_t(wchar_len)だとwacher_t一個分のしか確保されず、wchar_lenの初期値が入るだけかと思います。あわせて、戻り値も変わりますのでご注意ください。

charでの例ですが、ちょっとしたテストコードを書きましたので、参考にしてみてください。

#include <cstring>
#include <iostream>
#include <memory>
#include <string>

std::unique_ptr<char[]> test()
{
    std::unique_ptr<char[]> str(new char[10]);
    std::strcpy(str.get(), "hogehoge");
    std::cout << "length: " << std::strlen(str.get()) << std::endl;
    return str;
}

std::unique_ptr<std::string> test2()
{
    std::unique_ptr<char[]> str(new char[10]);
    std::strcpy(str.get(), "hogehoge");
    std::cout << "length: " << std::strlen(str.get()) << std::endl;
    return std::make_unique<std::string>(str.get());
}

int main()
{
    auto hoge = test();
    std::cout << hoge.get() << std::endl;
    auto hoge2 = test2();
    std::cout << hoge.get() << std::endl;
    return 0;
}

とりあえず、strlenやstrcpyなどは動いているのでたぶん何でもいけるでしょう。char[]だと扱いにくそうなのでstd::stringを返す例も作ってみました。std::make_uniqueはC++14からなので古いコンパイルにはご注意ください。

あと、returnの所にstd::moveがなくてもムーブになると思っているんですが、必要でしたっけ?


【追記】

unique_ptr - cpprefjp C++日本語リファレンスによると

new[]演算子で作成された配列へのポインタにも対応している。
T[]時にdelete[]を呼び出すようにdefault_deleteを特殊化している。auto_ptrでは配列を渡すことができなかった(正確にはデストラクタでdelete[]ではなくdeleteが呼び出されるため上手く動作しない)。
unique_ptr自体もT[]時には部分特殊化され、operator[]によるアクセスを提供している。

だそうです。std::unique_ptr<wchar_t>のままでもoperator[]を使わなければ動くようですが、ポインタが破棄されるときに問題が起きるかも知れません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/20 15:13 編集

    って、書いている間に解決している…。確かにT[]じゃなくても動く。うーん、よくわからないです。

    ⇒調べたら、すぐに見つかったし…ちょんと調べておけば良かった。

    キャンセル

  • 2016/11/20 16:08

    あああ、そう言えば配列の時は、T[]にしておかないとdelete[]が呼ばれないので処理系に依存してしまいますね。

    キャンセル

同じタグがついた質問を見る

  • C++14

    53questions