14992


TErrorLoggerというクラスを作っている時に疑問が生じました。

`TErrorLogger *testError = new TErrorLogger();`  
とするところを  
`TErrorLogger *testError;`  
としていたとします。

このまま  
`testError->Add()`のようなメンバ関数を呼ぶとエラーになりますが、`testError::Add()`内でそれを判別する方法はどういうものがあるでしょうか?

自分が試したのは以下の方法です。環境はC++ Builder XE4です。

[code]

    void TErrorLogger::Add(){
      if (this == NULL) {
        return; // error
      }
    }
    
[/code]

上記のような方法は正しいでしょうか？  
他のtestError未初期化の判別方法がありましたらご教授ください。


CC BY-SA 3.0
2015-08-29T05:28:14.253
14989
2015-08-29T23:00:22.850
4840
question
c++
クラス > メンバー関数内でクラスをnewしていないことの確認


`Add()` 内部からでは無いです。  
[c](/questions/tagged/c "'c' のタグが付いた質問を表示") [c++](/questions/tagged/c%2b%2b
"'c++' のタグが付いた質問を表示") の言語仕様上、未初期化の自動変数は不定値です。 `NULL` とは限りません。

対策1．コンパイラ/静的解析ツールに検出してもらう  
多くのコンパイラ/静的解析ツールで未初期化の自動変数の使用を検出できるはずです。  
警告メッセージを得るには警告レベルを上げる必要があるかもしれません。

[code]

    int func() {
        int* p;
        return *p;
    }
    
    $ g++ -c -Wuninitialized uninit.cpp
    uninit.cpp:3:12: warning: 'p' is used uninitialized in this function [-Wuninitialized]
    
[/code]

~~対策2．生ポインタを使うのをやめましょう  
`shared_ptr` とかのスマートポインタを使うと `new` を忘れるとコンパイルエラーになります。~~

対策3．そもそも `new` をやめましょう  
その `new` 本当に必要ですか？  
普通に `TErrorLogger log;` と書ける文脈ならば `new` しなきゃいいんです。


CC BY-SA 3.0
2015-08-29T06:07:03.500
14992
2015-08-29T09:00:20.870
2015-08-29T09:00:20.870
8589
8589
14989
answer


既に774RRさんから回答がありますので、それ以外の補足を。

[code]

    class TErrorLogger {
    public:
        void virtual Add() {};
    };
    
    TErrorLogger* p;
    p->Add();
    
[/code]

のように仮想関数の場合、実行時にポインター`p`から`Add()`関数のアドレスを取得します（仮想関数たる所以です）。ですので正しく初期化されていない場合は呼び出しが成功することなく実行時エラーになります。というわけで`Add()`側、クラス設計者側で対処することはできません。

ですのでC++言語では基本的には呼び出し側の責任と考えるべきです。774RRさんの回答されている対策1～3についても呼び出し側の対策でしかありません。

もしクラス設計者側で対処するのであれば、publicメンバーに関しては静的関数で提供するのはどうでしょうか？
特に質問のケースであればloggerのようですので可能だと思います。


CC BY-SA 3.0
2015-08-29T08:34:28.197
14996
2015-08-29T08:34:28.197
4236
14989
answer


提示の `p->Add()` が何をどこにどう加えるのか想像つかないのでそこはスルーして  
@sayuri さんの提案は以下のようなことだと思うわけです。

ロガーということは  
\- 複数個のログ発行元から  
\- ログ発行先 (端的にはログファイル) へ、ログを排他しながら直列化して出力する  
という動作であろうと推定されます。

ならば  
\- ロガーはシングルトンとして実装できそうな気がする  
\- ロガーのインスタンスは大域変数１個のみでもいけるかも (しかも公開不要かも)  
\- 公開インターフェイスは `static` な関数のみとできるかも  
\- 非公開な内部実装の中において `new` が必要なのに忘れたとしてもその責任はロガー実装者にある  
\- すなわちロガー使用者が `new` を記述する必要はなくなり、不正な使い方はしづらくなる

ってことで例えば次のような実装もありうるのではないか・・・と。  
他の言語では `static class` なんてのもありますよね。

実装側

[code]

    class MyLogger {
    public:
        static int Attach(); // fopen みたいに使う
        static int Detach(); // fclose みたいに使う
        static void Output(...); // fputs みたいに使う
    private:
        // 非公開である内部実装においてのみ `new` を使う
    };
    
[/code]

使う側

[code]

    MyLogger::Attach(); // を一連のログ出力の開始時点で実行
    MyLogger::Output("Hello World"); // Attach から Detach までは
    MyLogger::Output("Foo Bar"); // 他のログに割り込まれず一括出力
    MyLogger::Detach(); // を一連のログ出力の終了時点で実行
    
[/code]

まあ普通のクラスでは無理っすけど。  
これは `new` しなくてよいクラスってことで、オイラの提案３の変形ってとこですか。

繰り返しますけど 「クラスのメンバ中から `this` の妥当性を推定することは無理」 です。


CC BY-SA 3.0
2015-08-29T23:00:22.850
15020
2015-08-29T23:00:22.850
8589
14989
answer