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

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

新規登録して質問してみよう
ただいま回答率
85.35%

Q&A

解決済

2回答

13742閲覧

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

wake_up_kemeko

総合スコア104

0グッド

0クリップ

投稿2016/11/19 14:03

編集2016/11/20 03:45

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

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

  • getメソッド利用

cpp

1// 動かない 11/20 訂正 2 std::unique_ptr<wchar_t> sjis_to_utf16(const std::string str) { 3 const int wchar_len = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0); 4 std::unique_ptr<wchar_t> utf16_str(new wchar_t(wchar_len)); 5 ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, utf16_str.get(), wchar_len); 6 return std::move(utf16_str); 7 }
  • エラー
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メソッドの返り値を入れて外部関数に渡しても同様のエラーが発生しました。

  • 生ポインタを使用すれば、動くことは確認できております。

cpp

1 2// 動く 3 std::unique_ptr<wchar_t> sjis_to_utf16(const std::string str) { 4 const int wchar_len = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0); 5 wchar_t* p_utf16_str = new wchar_t[wchar_len]; 6 ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, p_utf16_str, wchar_len); 7 std::unique_ptr<wchar_t> utf16_str(p_utf16_str); 8 return std::move(utf16_str); 9 }
  • 追記 11/20

自作した関数に.getメソッドで渡すと、正常に動作しているようでした。
MultiByteToWideChar()側に問題があるのかもしれません。
型の違いが原因である気がしてきました…
MultiByteToWideCharのリファレンス

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

配列に対する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での例ですが、ちょっとしたテストコードを書きましたので、参考にしてみてください。

C++

1#include <cstring> 2#include <iostream> 3#include <memory> 4#include <string> 5 6std::unique_ptr<char[]> test() 7{ 8 std::unique_ptr<char[]> str(new char[10]); 9 std::strcpy(str.get(), "hogehoge"); 10 std::cout << "length: " << std::strlen(str.get()) << std::endl; 11 return str; 12} 13 14std::unique_ptr<std::string> test2() 15{ 16 std::unique_ptr<char[]> str(new char[10]); 17 std::strcpy(str.get(), "hogehoge"); 18 std::cout << "length: " << std::strlen(str.get()) << std::endl; 19 return std::make_unique<std::string>(str.get()); 20} 21 22int main() 23{ 24 auto hoge = test(); 25 std::cout << hoge.get() << std::endl; 26 auto hoge2 = test2(); 27 std::cout << hoge.get() << std::endl; 28 return 0; 29}

とりあえず、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 06:10

編集2016/11/20 06:20
raccy

総合スコア21739

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2016/11/20 06:21 編集

って、書いている間に解決している…。確かにT[]じゃなくても動く。うーん、よくわからないです。 ⇒調べたら、すぐに見つかったし…ちょんと調べておけば良かった。
Chironian

2016/11/20 07:08

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

0

ベストアンサー

こんにちは。

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

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


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

C++

1#include <iostream> 2#include <string> 3#include <memory> 4#include <windows.h> 5 6 std::unique_ptr<wchar_t> sjis_to_utf16(const std::string str) { 7 const int wchar_len = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0); 8 std::unique_ptr<wchar_t> utf16_str(new wchar_t(wchar_len)); 9 ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, utf16_str.get(), wchar_len); 10 return std::move(utf16_str); 11 } 12 13int main() 14{ 15 std::unique_ptr<wchar_t> utf16=sjis_to_utf16("テスト"); 16 17 std::wcout.imbue(std::locale("")); 18 std::wcout << utf16.get() << std::endl; 19 20 return 0; 21}

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/19 14:12

編集2016/11/20 07:16
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

wake_up_kemeko

2016/11/20 03:39

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

2016/11/20 04:14

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

2016/11/20 04:46 編集

ご確認ありがとうございます。 ご掲示いただきましたソースを当方の`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) [中止][再試行][無視] ``` のポップアップウィンドウが表示されます。
Chironian

2016/11/20 05: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]);
wake_up_kemeko

2016/11/20 05:35

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問