漆黒な技術メモ

管理人が必要に応じて自分のメモを好き勝手に投下するたまり場的ブログ

C++のstd::string::clear()はメモリの解放をしてくれない話

まあ簡単に言うと自分のC++に対する知識が浅かったって話ですが…
研究のソースでstd::string::clear()を何回か使ってメモリの解放をした「つもり」でしたが、こいつは長さを0にセットするだけでメモリ領域は食ったままらしく、結果的にメモリリーク( std::bad_alloc )を起こして死にました。 ちなみに書いたソースはこんな感じ

class Buffer {
public:
    Buffer();
    virtual ~Buffer();
  
    void setData(std::string data,int type);
  
    std::string getData();
    int getType();
  
    void clearData();
    bool isexistData();

private:
    std::string data;
    int type;
    bool existData;
};

Buffer::Buffer() :
        type(-1), existData(false) {
}

Buffer::~Buffer() {
}

void Buffer::setData(std::string data,
        int type) {
    this->data = data;
    this->type = type;
    existData = true;
}

std::string Buffer::getData() {
    return data;
}

int Buffer::getType() {
    return type;
}

void Buffer::clearData() {
    data.clear();
    existData = false;
}

bool Buffer::isexistData() {
    return existData;
}

こういう状況で、いくら Buffer::clearData() を呼び出してもメモリリークを起こすので不思議だなあと思ってたら、stackoverflowにそんなことが書いてありました。

Calling std::string::clear() merely sets the size to zero. The capacity() won’t change (nor will reserve()ing less memory than currently reserved change the capacity).

stackoverflow.com

stackoverflowには、解放したいんだったらBuffer::clearData()

void Buffer::clearData() {
  std::string().swap(data);
    existData = false;
}

で行う(空文字を確保したstd::stringとのメモリ領域スワップ、スコープが終われば元々dataで確保した領域はデストラクタが呼ばれて解放される)と良いと書いてありました。
でも、この書き方だとなにやってるかわかりづらいですよね…

std::shared_ptrに変える

まあそもそもバッファなんだからshared_ptrにしろよという突っ込みが聞こえてきそうですが、その話は棚に上げて… std::shared_ptrはじめスマートポインタは reset() で所有権を放棄、つまるところ(shared_ptrの場合はカウンタが0であれば)明示的にメモリを開放することができます。 というわけで該当部を以下のようなコードに変更して事なきを得ましたとさ。

void Buffer::clearData() {
    data.reset();
    existData = false;
}