web-dev-qa-db-ja.com

操作後にstd :: coutの状態を復元する

次のようなコードがあるとします:

void printHex(std::ostream& x){
    x<<std::hex<<123;
}
..
int main(){
    std::cout<<100; // prints 100 base 10
    printHex(std::cout); //prints 123 in hex
    std::cout<<73; //problem! prints 73 in hex..
}

私の質問は、関数から戻った後にcoutの状態を元の状態に「復元」する方法があるかどうかです。 (std :: boolalphaやstd :: noboolalpha ..に少し似ています)?

ありがとう。

88
UltraInstinct

必要がある #include <iostream>または#include <ios>その後、必要な場合:

std::ios_base::fmtflags f( cout.flags() );

//Your code here...

cout.flags( f );

これらを関数の最初と最後に配置するか、 [〜#〜] raii [〜#〜] でこれを使用する方法について this answer を確認してください。

78
Stefan Kendall

Boost IO Stream State Saver は、まさにあなたが必要とするもののようです。:-)

コードスニペットに基づく例:

void printHex(std::ostream& x) {
    boost::io::ios_flags_saver ifs(x);
    x << std::hex << 123;
}
56

ここで示された回答は、_std::cout_の完全な状態を復元しないことに注意してください。たとえば、_std::setfill_は、.flags()を呼び出した後でも「固執」します。より良い解決策は_.copyfmt_を使用することです:

_std::ios oldState(nullptr);
oldState.copyfmt(std::cout);

std::cout
    << std::hex
    << std::setw(8)
    << std::setfill('0')
    << 0xDECEA5ED
    << std::endl;

std::cout.copyfmt(oldState);

std::cout
    << std::setw(15)
    << std::left
    << "case closed"
    << std::endl;
_

印刷されます:

_case closed
_

のではなく:

_case closed0000
_
37
rr-

この回答のサンプルコードを使用してRAIIクラスを作成しました。この手法の大きな利点は、iostreamにフラグを設定する関数から複数のリターンパスがある場合に得られます。どちらの戻りパスが使用されても、デストラクタが常に呼び出され、フラグは常にリセットされます。関数が戻ったときにフラグを復元することを忘れる可能性はありません。

class IosFlagSaver {
public:
    explicit IosFlagSaver(std::ostream& _ios):
        ios(_ios),
        f(_ios.flags()) {
    }
    ~IosFlagSaver() {
        ios.flags(f);
    }

    IosFlagSaver(const IosFlagSaver &rhs) = delete;
    IosFlagSaver& operator= (const IosFlagSaver& rhs) = delete;

private:
    std::ostream& ios;
    std::ios::fmtflags f;
};

その後、現在のフラグの状態を保存したいときはいつでも、IosFlagSaverのローカルインスタンスを作成して使用します。このインスタンスが範囲外になると、フラグの状態が復元されます。

void f(int i) {
    IosFlagSaver iosfs(std::cout);

    std::cout << i << " " << std::hex << i << " ";
    if (i < 100) {
        std::cout << std::endl;
        return;
    }
    std::cout << std::oct << i << std::endl;
}
19
qbert220

出力をより読みやすくするために少し変更します:

void printHex(std::ostream& x) {
   ios::fmtflags f(x.flags());
   x << std::hex << 123 << "\n";
   x.flags(f);
}

int main() {
    std::cout << 100 << "\n"; // prints 100 base 10
    printHex(std::cout);      // prints 123 in hex
    std::cout << 73 << "\n";  // problem! prints 73 in hex..
}
7
whacko__Cracko

Stdoutバッファーの周りに別のラッパーを作成できます。

_#include <iostream>
#include <iomanip>
int main() {
    int x = 76;
    std::ostream hexcout (std::cout.rdbuf());
    hexcout << std::hex;
    std::cout << x << "\n"; // still "76"
    hexcout << x << "\n";   // "4c"
}
_

関数内:

_void print(std::ostream& os) {
    std::ostream copy (os.rdbuf());
    copy << std::hex;
    copy << 123;
}
_

もちろん、パフォーマンスが問題となる場合、これはiosオブジェクト全体(ただし、バッファーではない)をコピーするため、費用はかかりますが、ロケールなどの使用は考えられないものが含まれます。

そうでない場合は、.flags()を使用する場合は、_<<_構文(スタイルの純粋な質問)ではなく、一貫性を保ち、.setf()を使用する方が良いと感じています。

_void print(std::ostream& os) {
    std::ios::fmtflags os_flags (os.flags());
    os.setf(std::ios::hex);
    os << 123;
    os.flags(os_flags);
}
_

他の人が言ったように、上記の(および.precision().fill()を入れることができますが、通常はロケールと単語に関連するものではなく、通常は変更されず、より重いです)利便性のため、および例外セーフにするためのクラス。コンストラクターは_std::ios&_を受け入れる必要があります。

4
n.caillou