3

教育目的で、いくつかのテスト プログラムで cstring を使用しています。「...」などのプレースホルダーを使用して文字列を短くしたいと考えています。

つまり、最大長が 13 に設定されている場合に"Quite a long string"なり"Quite a lo..."ます。さらに、元の文字列を破棄したくないため、短縮された文字列はコピーにする必要があります。

以下の(静的)メソッドは、私が思いついたものです。私の質問は次のとおりです。短縮された文字列にメモリを割り当てるクラスも、それを解放する責任がありますか? 私が今行っているのは、返された文字列を別の「ユーザー クラス」に格納し、メモリの解放をそのユーザー クラスに任せることです。

const char* TextHelper::shortenWithPlaceholder(const char* text, size_t newSize) {
    char* shortened = new char[newSize+1];

    if (newSize <= 3) {
        strncpy_s(shortened, newSize+1, ".", newSize);
    }
    else {
        strncpy_s(shortened, newSize+1, text, newSize-3);
        strncat_s(shortened, newSize+1, "...", 3);  
    }
    return shortened;
}
4

8 に答える 8

6

このような関数の標準的なアプローチは、ユーザーに char[] バッファを渡させることです。sprintf()これは、たとえば、デスティネーション バッファをパラメータとして受け取る のような関数で見られます。これにより、呼び出し元はメモリの割り当てと解放の両方を担当し、メモリ管理の問題全体を 1 か所にまとめることができます。

于 2009-07-26T22:13:56.393 に答える
5

バッファオーバーフローやメモリリークを回避するために、この場合のように常にC++クラスを使用する必要があります。std::string

最後のインスタンスのみが、クラスを。などの低レベルに変換する必要がありますchar*。これにより、コードがシンプルで安全になります。コードを次のように変更するだけです。

std::string TextHelper::shortenWithPlaceholder(const std::string& text,
                                               size_t newSize) {
    return text.substr(0, newSize-3) + "...";
}

その関数をCコンテキストで使用する場合は、次のcstr()方法を使用するだけです。

some_c_function(shortenWithPlaceholder("abcde", 4).c_str());

それで全部です!

一般に、Cでプログラムするのと同じ方法でC++でプログラムするべきではありません。C++を実際に異なる言語として扱う方が適切です。

于 2009-07-26T22:30:54.473 に答える
2

ローカルに割り当てられたメモリにポインタを返すことに満足したことはありません。私は、クリーンアップに関して私の関数を呼び出す人の健全な不信を維持するのが好きです。

代わりに、短縮された文字列をコピーするバッファを受け入れることを検討しましたか?

例えば。

const char* TextHelper::shortenWithPlaceholder(const char* text, 
                                               size_t textSize, 
                                               char* short_text, 
                                               size_t shortSize)

ここで、short_text =短縮された文字列をコピーするバッファ、shortSize =提供されたバッファのサイズ。呼び出し元の便宜のために、short_textconst char*へのポインティングを引き続き返すこともできます( shortSizeが十分に大きくない場合はNULLを返します)。

于 2009-07-26T22:18:31.610 に答える
2

実際には、を使用するstd::string必要がありますが、必要な場合は、既存のライブラリで使用方法のガイダンスを確認してください。

C標準ライブラリでは、実行していることに最も近い関数は次のとおりです。

char * strncpy ( char * destination, const char * source, size_t num );

だから私はこれで行きます:

const char* TextHelper::shortenWithPlaceholder(
    char * destination, 
    const char * source, 
    size_t newSize);

呼び出し元はメモリ管理を担当します。これにより、呼び出し元はスタック、ヒープ、メモリマップファイル、またはそのデータを保持するためのソースを使用できます。new[]メモリの割り当てに使用したことを文書化する必要はありません。また、呼び出し元は、または、または低レベルのオペレーティングシステムコールとdelete[]は対照的に、使用することを知る必要はありません。メモリ管理を呼び出し元に任せると、柔軟性が高まり、エラーが発生しにくくなります。freedelete

宛先へのポインタを返すことは、次のようなことができるようにするための便利な方法です。

char buffer[13];
printf("%s", TextHelper::shortenWithPlaceholder(buffer, source, 12));
于 2009-07-26T22:28:36.537 に答える
1

最も柔軟なアプローチは、割り当てられたメモリをラップするヘルパーオブジェクトを返すことです。これにより、呼び出し元はそれについて心配する必要がなくなります。このクラスはメモリへのポインタを格納し、コピーコンストラクタ、代入演算子、およびデストラクタを備えています。

class string_wrapper
{
    char *p;

public:
    string_wrapper(char *_p) : p(_p) { }
    ~string_wrapper() { delete[] p; }

    const char *c_str() { return p; }

    // also copy ctor, assignment
};

// function declaration
string_wrapper TextHelper::shortenWithPlaceholder(const char* text, size_t newSize)
{
    // allocate string buffer 'p' somehow...

    return string_wrapper(p);
}

// caller
string_wrapper shortened = TextHelper::shortenWithPlaceholder("Something too long", 5);

std::cout << shortened.c_str();

ほとんどの実際のプログラムstd::stringはこの目的で使用します。

于 2009-07-26T22:25:45.370 に答える
0

私が同じように一般的であると考える2つの基本的な方法があります:a)TextHelperはc文字列を返し、それを忘れます。ユーザーはメモリを削除する必要があります。b)TextHelperは、割り当てられた文字列のリストを維持し、破棄されたときにそれらの割り当てを解除します。

今それはあなたの使用パターンに依存します。b)私には危険なようです:TextHelperが文字列の割り当てを解除する必要がある場合、ユーザーが短縮された文字列の操作を完了する前に、割り当てを解除するべきではありません。この時点がいつになるかわからない可能性があるため、プログラムが終了するまでTextHelperを存続させます。これにより、メモリリークに等しいメモリ使用パターンが発生します。std :: string :: c_str()と同様に、文字列がそれらを提供するクラスに意味的に属している場合にのみ、b)をお勧めします。TextHelperは、処理された文字列に関連付けられるべきではないツールボックスのように見えるので、2つから選択する必要がある場合は、a)を選択します。固定のTextHelperインターフェイスを考えると、ユーザークラスがおそらく最良のソリューションです。

于 2009-07-26T22:24:24.330 に答える
0

あなたの例では、呼び出し元は、割り当てられたメモリを解放する責任を負うしかありません。

ただし、これは使用するとエラーが発生しやすいイディオムであり、使用はお勧めしません。

ほぼ同じコードを使用できるようにする 1 つの代替方法は、参照されたカウント ポインターに変更shortenedし、ベア ポインターの代わりにその参照されたカウント ポインターをメソッドが返すようにすることです。

于 2009-07-26T22:14:29.357 に答える
0

編集:いいえ、私は間違っています。私はあなたが何をしようとしているのか誤解しました。呼び出し元は、インスタンス内のメモリを削除する必要があります。

C++ 標準では、0/NULL を削除しても何も起こらない (つまり、安全に実行できる) と規定されているため、関数を呼び出したかどうかに関係なく削除できます。編集:これがどのように除外されたのかわかりません...他の選択肢は配置の削除です。その場合、たとえそれが悪い形であっても、新しい配置を使用して割り当て/解放を同じ場所に保つ必要があります (そうしないと、矛盾がデバッグをばかげてしまいます)。

とはいえ、コードをどのように使用していますか?いつ複数回呼び出すかはわかりませんが、そうすると、メモリの各ブロックを覚えていないと、メモリリークが発生する可能性があります (と思います)。

std::auto_ptrまたはを使用しますBoost::shared_ptr。途中で自分自身を削除し、char* で使用できます。

TextHelper の割り当て方法を考慮して、できるもう 1 つのこと。ここに理論的なctorがあります:

TextHelper(const char* input) : input_(input), copy(0) { copy = new char[sizeof(input)/sizeof(char)]; //mess with later }
~TextHelper() { delete copy; }
于 2009-07-26T22:15:44.033 に答える