0

parse(int argc, char* argv[])オブジェクトの目的の状態を設定するために使用する必要のある関数を持つクラスがあります。を使用してGUIからパラメーターを取得し、stringstreamそれらをchar**に変換して関数に渡そうとしています。これが私が持っているものです:

std::stringstream sstream;

sstream << "-clip" << " " << min_x_entry.get_text()
        << " " << max_x_entry.get_text(); // etc.

std::cout << sstream.str();    // All looks good here

std::vector<std::string> args;
std::vector<char*> argv;
std::string arg;

while (sstream >> arg)
{
    args.push_back(arg);
    argv.push_back(const_cast<char*>(args.back().c_str()));
}
argv.push_back(0);

int argc = args.size();

for (int i = 0; i < argc; ++i)
    std::cout << &argv[0][i];    // This outputs garbage

my_object.parse(argc, &argv[0])  // And this fails

私は何が欠けていますか?これを達成するためのより良い方法はありますか?

4

4 に答える 4

5

問題は、必要に応じてベクトルのサイズが大きくなるため、argsベクトルの再割り当てです。push_back()

new size()がcapacity()より大きくない場合、イテレータまたは参照は無効になりません。そうしないと、すべてのイテレータと参照が無効になります。

argvベクトルはの要素の内部へのポインタを格納しているためargs、これらは無効になります。

解決策は、args最初にベクトルを作成し、次にベクトルを作成することargvです。

while (sstream >> arg) args.push_back(arg);

for (auto i = args.begin(); i != args.end(); i++)
{
    argv.push_back(const_cast<char*>(i->c_str()));
}
argv.push_back(0);

for文字列を出力するループがargv正しくありません。これ:

&argv[0][i]

ですが、の最初のエントリのth要素char*から始まります。たとえば、の最初のc-stringが:だった場合iargvargv"string"

&argv[0][1] is "tring"
&argv[0][2] is "ring"

への変更:

for (int i = 0; i < argc; i++)
    std::cout << argv[i] << std::endl; // Added 'endl' to flush 'cout'.
于 2012-06-26T14:19:47.837 に答える
3
std::vector<std::string> args;
std::vector<char*> argv;

/* ... */

    argv.push_back(const_cast<char*>(args.back().c_str()));

ここに多くの問題があります。

  1. によって返されるポインタは、同じの非メンバー関数をc_str()後で呼び出した後でも有効であるとは限りません。から返されたポインタは、特に他のコードがの非メンバーを呼び出すかどうかわからない場合は特に、後で保存して使用するべきではありません。conststringc_str()conststring
  2. const_castによって返されるポインタから-neddconstを離しますc_str()。キャスト自体は、アンチパターンではないにしても合法です。しかし、後でそのポインタに格納されているデータを変更しようとすると、それは未定義動作です。

これがスタンダードが言っていることですc_str()

21.3.6basic_string文字列操作[lib.string.ops]

const charT* c_str() const;

1 /戻り値:長さsize()+ 1の配列の最初の要素へのポインター。最初のsize()要素は* thisによって制御される文字列の対応する要素と等しく、最後の要素はcharT()によって指定されたヌル文字です。 。

2 /必要条件:プログラムは、配列に格納されている値を変更してはなりません。また、プログラムは、これと同じオブジェクトを指定するクラスbasic_stringの非constメンバー関数への後続の呼び出しの後、戻り値を有効なポインター値として扱いません。const charT * data()const;

3 /戻り値:size()がゼロ以外の場合、メンバーは、最初のsize()要素が*thisによって制御される文字列の対応する要素と等しい配列の最初の要素へのポインターを返します。size()がゼロの場合、メンバーは、コピー可能でゼロを追加できるnull以外のポインターを返します。

4 /必要条件:プログラムは、文字配列に格納されている値を変更してはなりません。また、プログラムは、これと同じオブジェクトを指定するbasic_stringの非constメンバー関数への後続の呼び出しの後、戻り値を有効なポインター値として扱いません。allocator_type get_allocator()const;

5 /戻り値:文字列の作成に使用されるAllocatorオブジェクトのコピー。

于 2012-06-26T14:26:11.833 に答える
1

iループ内の変数を初期化するのを忘れました。そして、あなたはベクトルの最初のアイテムだけを印刷しようとしていますargv

for (int i = 0; i < argc; ++i)
    std::cout << argv[i];
于 2012-06-26T14:31:38.360 に答える
0

次のようなことを行うことで、メソッドが引数を変更する可能性があることをconst_cast気にせずに取り除くことができます。parse()

std::vector<std::vector<char>> args;

std::for_each(std::istream_iterator<std::string>(sstream),
              std::istream_iterator<std::string>(),
              [&args](const std::string& str)
              {
                  std::vector<char> temp(str.begin(), str.end());
                  temp.push_back('\0');
                  args.push_back(temp);
              });

std::vector<char*> argv(args.size());

for (auto& v : args) argv.push_back(v.data());

my_object.parse(argv.size(), argv.data());
于 2012-06-26T15:13:40.373 に答える