5

C++ について少し調べていたところ、RTTI (Runtime Type Identification) に関する次の記事が見つかりました: http://msdn.microsoft.com/en-us/library/70ky2y6k(VS.80).aspx。まあ、それは別の話題です:) -しかし、クラスで奇妙なことわざにtype_info出くわしました。つまり、メソッドについて::nameです。「type_info::nameメンバー関数はconst char*、型の人間が読める名前を表す null で終わる文字列に a を返します。ポイントされたメモリはキャッシュされており、直接割り当てを解除することはできません。」

どうすればこのようなものを自分で実装できますか!? char呼び出し元が削除する新しい -array を作成したくないため、以前はこの正確な問題にかなり苦労していたので、std::stringこれまでに固執してきました。

簡単にするために、 を返すメソッドを作りたいとし"Hello World!"ましょう。それを呼び出しましょう。

const char *getHelloString() const;

個人的には、次のようにします(疑似):

const char *getHelloString() const
{
  char *returnVal = new char[13];
  strcpy("HelloWorld!", returnVal);

  return returnVal
}

..しかし、これは、呼び出し元がdelete[]私のリターンポインターで a を実行する必要があることを意味します:(

事前にThx

4

12 に答える 12

23

これはどう:

const char *getHelloString() const
{
    return "HelloWorld!";
}

リテラルを直接返すということは、文字列のスペースがコンパイラによって静的ストレージに割り当てられ、プログラムの実行中ずっと使用できることを意味します。

于 2008-10-14T12:03:22.890 に答える
3

文字列を静的に割り当てる方法に関するすべての回答が気に入っていますが、すべての実装、特に元のポスターがリンクされているドキュメントの実装に必ずしも当てはまるとは限りません。この場合、装飾された型名はスペースを節約するために静的に格納され、装飾されていない型名はオンデマンドで計算され、リンクされたリストにキャッシュされます。

Visual C++ のtype_info::name()実装がメモリをどのように割り当ててキャッシュするかについて知りたい場合は、簡単に見つけることができます。まず、小さなテスト プログラムを作成します。

#include <cstdio>
#include <typeinfo>
#include <vector>    
int main(int argc, char* argv[]) {
    std::vector<int> v;
    const type_info& ti = typeid(v);
    const char* n = ti.name();
    printf("%s\n", n);
    return 0;
}

それをビルドしてデバッガー (私は WinDbg を使用) で実行し、 によって返されたポインターを確認しますtype_info::name()。それはグローバル構造を指していますか?その場合、WinDbg のlnコマンドは最も近いシンボルの名前を教えてくれます。

0:000> ?? n
char * 0x00000000`00857290
 "class std::vector<int,class std::allocator<int> >"
0:000> ln 0x00000000`00857290
0:000>

lnこれは、文字列が特定のモジュールが所有するアドレスの範囲内になかったことを示しています。データまたは読み取り専用データ セグメント内にある場合は、その範囲内になります。によって返されたアドレスをすべてのヒープで検索して、ヒープに割り当てられているかどうかを確認しましょうtype_info::name()

0:000> !heap -x 0x00000000`00857290
Entry             User              Heap              Segment               Size  PrevSize  Unused    Flags
-------------------------------------------------------------------------------------------------------------
0000000000857280  0000000000857290  0000000000850000  0000000000850000        70        40        3e  busy extra fill 

はい、ヒープに割り当てられました。プログラムの先頭にブレークポイントを置いてmalloc()再起動すると、それが確認されます。

の宣言を見ると<typeinfo>、ヒープ ポインターがキャッシュされている場所の手がかりが得られます。

struct __type_info_node {
    void *memPtr;
    __type_info_node* next;
};

extern __type_info_node __type_info_root_node;
...
_CRTIMP_PURE const char* __CLR_OR_THIS_CALL name(__type_info_node* __ptype_info_node = &__type_info_root_node) const;

のアドレスを見つけて__type_info_root_node、デバッガーでリストをたどると、 によって返されたのと同じアドレスを含むノードがすぐに見つかりますtype_info::name()。リストはキャッシュスキームに関連しているようです。

元の質問にリンクされている MSDN ページは、空白を埋めているようです。名前は、スペースを節約するために装飾された形式で保存されており、このフォームには からアクセスできますtype_info::raw_name()type_info::name()特定の型で初めて呼び出すと、名前の装飾が解除され、ヒープに割り当てられたバッファーに格納され、バッファー ポインターがキャッシュされて返されます。

リンクされたリストは、プログラムの終了時にキャッシュされた文字列の割り当てを解除するためにも使用できます (ただし、そうであるかどうかは確認していません)。これにより、メモリ デバッグ ツールを実行したときにメモリ リークとして表示されなくなります。

于 2008-10-15T05:55:09.153 に答える
2

そうですね、単に関数について話しているのであれば、常に同じ値を返したいと思うでしょう。それはとても簡単です。

const char * foo() 
{
   static char[] return_val= "HelloWorld!";
   return return_val;
}

注意が必要なのは、結果をキャッシュする場所で処理を開始し、スレッド化を検討する必要がある場合、またはキャッシュが無効になり、スレッド ローカル ストレージに格納しようとする場合です。ただし、すぐにコピーされるのが 1 回限りの出力である場合は、これでうまくいくはずです。
または、固定サイズがない場合は、任意のサイズの静的バッファーを使用する必要がある場合に何かを行う必要があります..最終的に何かが大きすぎる可能性があるか、管理されたクラスに切り替えstd::stringます.

const char * foo() 
{
   static std::string output;
   DoCalculation(output);
   return output.c_str();
}

関数シグネチャも

const char *getHelloString() const;

メンバー関数にのみ適用されます。その時点で、静的関数のローカル変数を処理する必要はなく、メンバー変数のみを使用できます。

于 2008-10-14T12:10:03.233 に答える
1

OPで行うように、メモリのチャンクを割り当て、呼び出し元がそれを解放することを期待する関数を実装するときは注意してください。

const char *getHelloString() const
{
  char *returnVal = new char[13];
  strcpy("HelloWorld!", returnVal);

  return returnVal
}

これを行うことで、メモリの所有権を呼び出し元に譲渡します。このコードを他の関数から呼び出す場合:

int main()
{
  char * str = getHelloString();
  delete str;
  return 0;
}

...メモリの所有権を譲渡するセマンティクスは明確ではなく、バグやメモリ リークが発生しやすい状況を作り出しています。

また、少なくとも Windows では、2 つの関数が 2 つの異なるモジュールにある場合、ヒープが破損する可能性があります。特に、main() が VC9 でコンパイルされた hello.exe にあり、getHelloString() が VC6 でコンパイルされた utility.dll にある場合、メモリを削除するとヒープが破損します。これは、VC6 と VC9 の両方が独自のヒープを使用し、同じヒープではないためです。そのため、1 つのヒープから割り当てて、別のヒープから割り当てを解除しています。

于 2008-10-14T14:20:00.117 に答える
1

彼らはこれらの数が限られていることを知っているので、それらを永遠に保管しているだけだと思います. 場合によってはそれを行うのが適切かもしれませんが、一般的なルールとして、std::string の方が優れています。

また、新しい呼び出しを調べて、その文字列を既に作成したかどうかを確認し、同じポインターを返すこともできます。繰り返しますが、何をしているかによっては、これも役立つ場合があります。

于 2008-10-14T12:05:11.967 に答える
0

このようなことは次のようになります。

const char *myfunction() {
    static char *str = NULL; /* this only happens once */
    delete [] str; /* delete previous cached version */
    str = new char[strlen("whatever") + 1]; /* allocate space for the string and it's NUL terminator */
    strcpy(str, "whatever");
    return str;
}

編集:私に起こったことは、これの良い代替品が代わりにboost::shared_pointerを返す可能性があるということです。そうすれば、発信者は必要な限りそれを保持でき、明示的に削除することを心配する必要はありません。公正な妥協IMO。

于 2008-10-14T13:51:45.837 に答える
0

このようなものは、オブジェクトと RAII イディオムを使用してのみ「きれいに」実装できると思います。オブジェクトのデストラクタが呼び出されると (obj がスコープ外になる)、const char*ポインタはもう使用されないと安全に想定できます。

コード例:

class ICanReturnConstChars
{
    std::stack<char*> cached_strings
    public:
    const char* yeahGiveItToMe(){
        char* newmem = new char[something];
        //write something to newmem
        cached_strings.push_back(newmem);
        return newmem;
    }
    ~ICanReturnConstChars(){
        while(!cached_strings.empty()){
            delete [] cached_strings.back()
            cached_strings.pop_back()
        }
    }
};

私が知っている唯一の他の可能性は、smart_ptrを渡すことです..

于 2010-01-16T22:15:42.223 に答える
0

返された文字列の寿命について警告するアドバイスは、適切なアドバイスです。返されたポインターの有効期間を管理する場合は、自分の責任を認識することに常に注意する必要があります。ただし、指している変数がそれを返した関数の呼び出しよりも長持ちする場合、この方法は非常に安全です。たとえば、c_str()class のメソッドとして返される const char へのポインタを考えてみましょうstd::string。これは、文字列オブジェクトが削除されたり、内部メモリが再割り当てされたりしない限り、有効であることが保証されている文字列オブジェクトによって管理されるメモリへのポインタを返します。

クラスの場合、std::type_infoその名前空間が示すように、これは C++ 標準の一部です。から返されるメモリは、name()実際には、クラスがコンパイルされたときにコンパイラとリンカーによって作成された静的メモリを指しており、ランタイム型識別 (RTTI) システムの一部です。これはコード空間のシンボルを参照しているため、削除しようとしないでください。

于 2008-10-14T17:54:20.823 に答える
0

この種の機能が必要なときに私がよく行ったことは、クラスに char * ポインターを持ち、null に初期化して、必要に応じて割り当てることです。

つまり:

class CacheNameString
{
    private: 
        char *name;
    public:
        CacheNameString():name(NULL)  { }

    const char *make_name(const char *v)
    {
        if (name != NULL)
            free(name);

        name = strdup(v);

        return name;
    }

};
于 2008-10-14T13:35:51.987 に答える
0

戻り値の型を にする必要があるのはなぜconstですか? メソッドをgetメソッドと考えないでください。createメソッドと考えてください。作成演算子/メソッドが返すものを削除する必要がある API をたくさん見てきました。ドキュメントに記載されていることを確認してください。

/* create a hello string
 * must be deleted after use
 */
char *createHelloString() const
{
  char *returnVal = new char[13];
  strcpy("HelloWorld!", returnVal);

  return returnVal
}
于 2008-10-14T13:27:05.310 に答える
-3

おそらく静的バッファを使用して行われます:

const char* GetHelloString()
{
    static char buffer[256] = { 0 };
    strcpy( buffer, "Hello World!" );
    return buffer;
}

このバッファは、この関数からのみアクセスできるグローバル変数のようなものです。

于 2008-10-14T12:07:08.670 に答える
-5

GC に頼ることはできません。これは C++ です。つまり、プログラムが終了するまでメモリを使用可能にしておく必要があります。いつ削除しても安全になるかわかりません。そのため、const char* を構築して返したい場合は、それを単純に new[] して返します。避けられない漏れを受け入れます。

于 2008-10-14T12:10:19.070 に答える