ポインタがNULL
に初期化されない理由を誰かが説明できますか?
例:
void test(){
char *buf;
if (!buf)
// whatever
}
buf
がnullではないため、プログラムはif内にステップインしません。
なぜ、ゴミ箱がオンになっている変数、特にメモリ上のゴミ箱に対応するポインタが必要なのかを知りたいのですが?
ポインター(および他のPODタイプ)を初期化する必要があることは誰もが認識しています。
質問は「誰がそれらを初期化すべきか」になります。
基本的に2つの方法があります。
コンパイラが、開発者によって明示的に初期化されていない変数を初期化したと仮定しましょう。次に、変数の初期化が自明ではなく、開発者が宣言点でそれを行わなかった理由は、何らかの操作を実行してから割り当てる必要があるためです。
そのため、コンパイラーが変数をNULLに初期化するコードに追加の命令を追加し、その後正しい初期化を行うために開発者コードが追加されるという状況になりました。または、他の条件下では、変数は使用されない可能性があります。多くのC++開発者は、その余分な命令を犠牲にして、両方の条件下でファウルを叫ぶでしょう。
時間だけではありません。しかし、スペースも。両方のリソースが貴重であり、開発者もgiveめたくない環境がたくさんあります。
[〜#〜] but [〜#〜]:初期化を強制する効果をシミュレートできます。ほとんどのコンパイラは、初期化されていない変数について警告します。そのため、私は常に警告レベルを可能な限り最高のレベルにします。次に、すべての警告をエラーとして扱うようコンパイラーに指示します。これらの条件下では、ほとんどのコンパイラは初期化されていないが使用されている変数に対してエラーを生成し、コードの生成を妨げます。
TC++ PLでのBjarne Stroustrupの引用(特別版p.22):
機能の実装は、それを必要としないプログラムに大きなオーバーヘッドを課すべきではありません。
初期化には時間がかかるからです。 C++では、変数で最初に行うべきことは、明示的に初期化することです。
int * p = & some_int;
または:
int * p = 0;
または:
class A {
public:
A() : p( 0 ) {} // initialise via constructor
private:
int * p;
};
C++のモットーの1つは次のとおりです。
使用しないものに対しては支払いません
このまさに理由で、vector
クラスのoperator[]
は、たとえば、インデックスが範囲外であるかどうかをチェックしません。
歴史的な理由から、主にこれがCで行われる方法であるためです.Cでそのように行われる理由は別の質問ですが、私はゼロオーバーヘッドの原理がこの設計決定に何らかの形で関与していたと思います。
それに、あなたがそれを吹き飛ばしたときの警告があります:「値を割り当てる前に使用される可能性があります」またはコンパイラに応じて同様の言葉です。
警告付きでコンパイルしますか?
変数が初期化されていないことが理にかなっている状況はほとんどなく、デフォルトの初期化のコストはわずかです。
C++はC89ではありません。地獄、CでさえC89ではありません。宣言とコードを混在させることができますので、初期化に適した値が得られるまで宣言を延期する必要があります。
ポインタは単なる別のタイプです。 int
、char
、またはその他のPODタイプを作成する場合、ゼロに初期化されないので、なぜポインターが必要ですか?これは、このようなプログラムを作成する人にとっては不必要なオーバーヘッドと考えられます。
char* pBuf;
if (condition)
{
pBuf = new char[50];
}
else
{
pBuf = m_myMember->buf();
}
あなたがそれを初期化しようとしていることがわかっている場合、メソッドの一番上でpBuf
を最初に作成するときにプログラムにコストがかかるのはなぜですか?これがオーバーヘッドゼロの原則です。
常にNULLに初期化されるポインターが必要な場合は、C++テンプレートを使用してその機能をエミュレートできます。
template<typename T> class InitializedPointer
{
public:
typedef T TObj;
typedef TObj *PObj;
protected:
PObj m_pPointer;
public:
// Constructors / Destructor
inline InitializedPointer() { m_pPointer=0; }
inline InitializedPointer(PObj InPointer) { m_pPointer = InPointer; }
inline InitializedPointer(const InitializedPointer& oCopy)
{ m_pPointer = oCopy.m_pPointer; }
inline ~InitializedPointer() { m_pPointer=0; }
inline PObj GetPointer() const { return (m_pPointer); }
inline void SetPointer(PObj InPtr) { m_pPointer = InPtr; }
// Operator Overloads
inline InitializedPointer& operator = (PObj InPtr)
{ SetPointer(InPtr); return(*this); }
inline InitializedPointer& operator = (const InitializedPointer& InPtr)
{ SetPointer(InPtr.m_pPointer); return(*this); }
inline PObj operator ->() const { return (m_pPointer); }
inline TObj &operator *() const { return (*m_pPointer); }
inline bool operator!=(PObj pOther) const
{ return(m_pPointer!=pOther); }
inline bool operator==(PObj pOther) const
{ return(m_pPointer==pOther); }
inline bool operator!=(const InitializedPointer& InPtr) const
{ return(m_pPointer!=InPtr.m_pPointer); }
inline bool operator==(const InitializedPointer& InPtr) const
{ return(m_pPointer==InPtr.m_pPointer); }
inline bool operator<=(PObj pOther) const
{ return(m_pPointer<=pOther); }
inline bool operator>=(PObj pOther) const
{ return(m_pPointer>=pOther); }
inline bool operator<=(const InitializedPointer& InPtr) const
{ return(m_pPointer<=InPtr.m_pPointer); }
inline bool operator>=(const InitializedPointer& InPtr) const
{ return(m_pPointer>=InPtr.m_pPointer); }
inline bool operator<(PObj pOther) const
{ return(m_pPointer<pOther); }
inline bool operator>(PObj pOther) const
{ return(m_pPointer>pOther); }
inline bool operator<(const InitializedPointer& InPtr) const
{ return(m_pPointer<InPtr.m_pPointer); }
inline bool operator>(const InitializedPointer& InPtr) const
{ return(m_pPointer>InPtr.m_pPointer); }
};
静的データは0に初期化されることに注意してください(特に断りがない限り)。
そして、はい、常に可能な限り遅く変数を初期値で宣言する必要があります。のようなコード
int j;
char *foo;
あなたがそれを読んだときに警報ベルをオフにする必要があります。 lint sが100%合法だからといって、それについてcarを説得できるかどうかはわかりません。
別の考えられる理由は、リンク時にポインターにアドレスが与えられますが、ポインターの間接的なアドレス指定/逆参照はプログラマーの責任です。通常、コンパイラはそれほど気にしませんが、ポインタを管理し、メモリリークが発生しないようにするために、プログラマに負担が渡されます。
実際、一言で言えば、リンク時にポインター変数にアドレスが与えられるという意味で初期化されます。上記のサンプルコードでは、クラッシュまたはSIGSEGVの生成が保証されています。
正しさのために、malloc
またはnew
なしで間接参照しようとすると、プログラマーはプログラムが不正に動作した理由を知るために、常にNULLへのポインターを初期化します。
これが助けになり、理にかなっていることを願って、
さて、C++がポインターを初期化した場合、「C++はCよりも遅い」と不満を言うCの人々は、何かに固執することになります;)
C++はCのバックグラウンドから来ています-これから戻ってくるいくつかの理由があります:
Cは、C++よりもアセンブリ言語の代替品です。それはあなたがそれをするように言わない何もしません。そのため:あなたがそれをNULLにしたい場合-それをしてください!
また、Cのようなベアメタル言語で物事を無効にすると、一貫性の問題が自動的に発生します。スタック上に作成された構造体はどうですか?すべてのバイトをゼロにする必要がありますか?グローバル変数はどうですか? 「(* 0x18);」のような文はどうですかそれはメモリ位置0x18がゼロにされるべきであることを意味しませんか?
あなたが話すこれらのポインターは何ですか?
例外の安全性のために、常にauto_ptr
、shared_ptr
、weak_ptr
およびその他のバリアントを使用してください。
優れたコードの特徴は、delete
への単一の呼び出しを含まないコードです。