2011-03-11 8 views
2

まれに発生する生存環境の問題を解決するためのコードレビューを行っています。デバッグ環境では再現できないため、唯一の調査手段は、生存環境からのコアダンプとコード解析です。
コアダンプ:ここでの状況をまとめたものである私は、これらのコンストラクタの両方を考える文字列クラスに関するコアダンプの問題に関するヘルプ

class CustomStr: public string 
{ 
    //Some custom members here 

}; 

This custom class has constructors: 
CustomStr::CustomStr(const char *str):string(str) 
{ 
    //Some derived class inits 
} 

CustomStr::CustomStr(const CustomStr& str) : string(str.c_str()) 
{ 
    //Some derived class inits 
} 

(gdb) bt 
#0 in strlen() from /lib/libc.so.6 
#1 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string$base() from libstdc++.so.6 
#2 in CustomStr::CustomStr() 

は、コード、STD :: Stringクラス上のようなものをラッパークラスを持っていますNULLへのポインタが渡されると、同じものがStringコンストラクタに渡され、内部的にstrlen()を呼び出して長さを決定するときに未定義動作(UB)が発生するという問題があります。 私は実装するための正しい方法は、のような文字列コンストラクタを呼び出す前に、NULLをチェックするようになると思う。

CustomStr::CustomStr(const char *str) 
{ 
    if(str!= NULL) 
     string(str); 
    //Some derived class inits 
} 

CustomStr::CustomStr(const CustomStr& str) 
{ 
    if(str!= NULL) 
     string(str.c_str()); 
    //Some derived class inits 
} 

私が持っている質問は次のとおりです。

  1. は(私はだと思う)問題を実行します。提案されたソリューションは有効なケースと思われますか?
  2. 文字列コンストラクタはNULLをチェックしていますか?内部的にはNULLにUBが表示されるstrlen()が呼び出されるからです。有効なのconstのchar *が渡されている場合以外NULLチェックから
  3. はどのようにして確認できますか?

答えて

2

const char *からのコンストラクタはnullポインタをチェックしません。必要な場合は、呼び出し側がチェックします。あなたは、セグメントがヌルであることが可能かどうかを知る必要があります。

もう一方のコンストラクタは、c_str()を渡す代わりに、文字列のコピーコンストラクタを使用できます。再計算から長さを節約し、空の文字列でも動作します。

0

はstdする::文字列(のconstのchar *)を引数(非NULLでのconstのchar *などを終了さ)すべき\ 0で終了する文字列へのポインタになります。 NULLは何も指しておらず、確かにヌルで終了する文字列でもありません。したがって、NULLを渡すことは前提条件違反です。

std::stringは、novicesで使用されるクラスであるため、デバッグビルドでポインタがNULLでないことをアサートする必要があります。とにかく有効なプログラムがNULLを渡すことはないので、実行時にチェックするべきではありません。

いつものようにC++では、のテストにポインタが「有効」かどうかはありません。 C++は、その質問に答えることができる義務的な仕組みがあれば、大幅なパフォーマンス上の不利益を被るでしょう。

1

さらにコアダンプを調べると、クラッシュの原因をきわめて正確に把握できます。特に、どんな例外がスローされていますか?それがアクセス違反(別名segfault)の場合、ダンプはまた、不正なアドレスがアクセスされていることを正確に伝える必要があります。これは、あなたの問題がNULLポインタからの読み込みであるかどうかを示します。

確かにstd::stringコンストラクタのNULLポインタが問題になる可能性がありますが、コアダンプの目的はそのようなデバッグから推測を外すことです。

いずれにしても、NULLポインタから構築するとstd::stringが爆発するだけなので、文字列コンストラクタに渡す前にポインタをNULLチェックする必要があります。 例えば

class CustomStr : public string 
{ 
    CustomStr(const char *str) : string(str ? str : "<INVALID>") { .. } 
}; 

は1つの簡単な回避策になります。

+0

これの背後にあるアイデアは、決してnullポインタ:-)を渡したことのない私が、チェックからパフォーマンスペナルティを得るべきではないということです。あなたはそれを誰にも課すことはできません。ちょうど新しい初心者を救うためです。チェックが必要な場合は、コンストラクタを呼び出すときに自分で追加することができます。 –

+0

は好奇心のために、私はコアの全体1時間の会議の講演会が先週ダンプんでした:http://bit.ly/hPCmVW – Crashworks

+0

@Bo Perssonの:確かに、あなたはチェックは、デバッグ専用の主張、またはあること作ることができますNULLポインターなどを渡す人を殺すだけです。重要なことは、ダンプを見て、これが本当に問題であることを確かめることです。 (正直言って、私のチームの公式パフ・ナチのように言いますが、その場合にNULLチェックをするのは、RISCチップでも最小限です。ブランチレス条件付き移動として行うことができるからです)。 – Crashworks

0

標準ライブラリクラスから派生してはならず、仮想デストラクタを持たず、基本クラスではありません。

あなたが持っているのは問題ではないかもしれませんが、それは知られているとよいでしょう。それをしないでください。

ラッパークラスのコンポジションとしてstd :: stringを使用できます。または、私が見つけたように、いくつかの問題を解決するよりも多くの問題につながるstd :: stringクラスのためのラッパーはまったく使用しないでください。

あなたはなど、「トリム」などのいくつかの特定の機能が必要な場合は、おそらくいくつかの外部関数を使用することができます

..

それはブーストライブラリ内の文字列操作関数の多くが存在することが知られて良いことです。

+0

コンテナクラスから派生することはよくありませんが、std :: stringへのポインタを使ってCustomStrを削除すると**非仮想デストラクタが問題になります**。低リスク、私の推測です。 –

2

C++標準はstd::string(const char*)に渡されたポインタがヌルポインタ(§21.4.2でC++ 0xのドラフトn3092)にならないことを要求します。したがって、文字列クラス自体はその条件をチェックせず、nullを渡すとコードが準拠していないことを意味します。あなたがCustomStr(const char*)に定める

は大丈夫に見えますが、CustomStr(const std::string&)のための1ではありません。参照をnullにすることはできません。 (私はそれがまったくコンパイルされていれば驚くだろう)char*がヌルチェックを除いて "有効"であるかどうかをチェックする方法がある。

関連する問題