1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

VC++ で可変個引数にそのまま渡せる文字列クラス?

Posted at

注:ネタなので役にはたちません


なぜか VC++ でこんなコードが上手く動きました。

# include <stdio.h>
# include <atlstr.h>

int main()
{
    ATL::CString str("hogehoge");

    // なぜか動く
    printf("%s\n", str);

    // 普通はこう
    printf("%s\n", (LPCSTR)str);

    // もしくはこう?
    printf("%s\n", str.GetString());

    return 0;
}

もちろん std::string で似たようなことしてもまともに動きません。

# include <stdio.h>
# include <string>

int main()
{
    std::string str("hogehoge");

    // まともに動かない
    printf("%s\n", str);

    // こうしなければならない
    printf("%s\n", str.c_str());

    return 0;
}

ATL::CString と似たような方法で printf などの可変個引数にそのまま渡してもうまく動いているような気がしてしまう文字列クラスを作ってみました。

ただし gcc では実行出来なかったし、VC++ でも動作は保証されません( http://msdn.microsoft.com/ja-jp/library/vstudio/awkwbzyc.aspx )。

# include <stdio.h>
# include <string.h>
# include <crtdbg.h>

class ore_string
{
private:
    char* _str;
    
    struct data_t
    {
        size_t len;
        int ref;
    };
    
    char* buffer() const
    {
        return _str - sizeof(data_t);
    }
    
    data_t* data() const
    {
        return reinterpret_cast<data_t*>(buffer());
    }

public:
    ore_string(const char* str)
    {
        size_t len = strlen(str);
        _str = new char[len + 1 + sizeof(data_t)] + sizeof(data_t);
        data()->len = len;
        data()->ref = 1;
        strcpy_s(_str, len + 1, str);
    }

    ore_string(const ore_string& rhs)
    {
        _str = rhs._str;
        data()->ref++;
    }
    
    ~ore_string()
    {
        if(--data()->ref == 0)
        {
            delete[] buffer();
        }
    }
    
    const char* c_str() const
    {
        return _str;
    }
    
    int length() const
    {
        return data()->len;
    }
    
    int refcount() const
    {
        return data()->ref;
    }
};

int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    ore_string str1("hogehoge");

    printf("%s pointer:%p length:%d refcount:%d\n", str1, str1.c_str(), str1.length(), str1.refcount());

    {
        ore_string str2(str1);

        printf("%s pointer:%p length:%d refcount:%d\n", str1, str1.c_str(), str1.length(), str1.refcount());
        printf("%s pointer:%p length:%d refcount:%d\n", str2, str2.c_str(), str2.length(), str2.refcount());
    }
    
    printf("%s pointer:%p length:%d refcount:%d\n", str1, str1.c_str(), str1.length(), str1.refcount());

    printf("sizeof ore_string=%lu char*=%lu\n", sizeof(str1), sizeof(char*));

    return 0;
}

実行結果

hogehoge pointer:00586C50 length:8 refcount:1
hogehoge pointer:00586C50 length:8 refcount:2
hogehoge pointer:00586C50 length:8 refcount:2
hogehoge pointer:00586C50 length:8 refcount:1
sizeof ore_string=4 char*=4

ポイントは、文字長や参照カウンタを持っているにも関わらす sizeof(str1)sizeof(char*) が同じであることです。

「文字長+参照カウンタ+文字列」分のバッファを確保して、バッファのアドレスを「文字長+参照カウンタ」だけズラして ore_string の唯一のメンバ変数にしています。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?