1

ここに「GetParent()」関数を実装したいと思います-

class ChildClass;

class ParentClass
{
public:
    ....
    ChildClass childObj;
    ....
};

class ChildClass
{
    friend class ParentClass;
private:
    ChildClass();

public:
    ParentClass* GetParent();
};

親オブジェクトへのポインターを格納するプライベート メンバー変数を作成しようとしました。ただし、この方法には追加のメモリが必要です。

class ChildClass
{
    friend class ParentClass;

private:
    ChildClass();

    ParentClass* m_parent;

public:
    ParentClass* GetParent()
    {
        return m_parent;
    }
};

そこで、offsetof() マクロを使用しました (offsetof() を呼び出すパフォーマンス コストは無視できます) が、このアプローチが安全かどうかはわかりません。あらゆる状況で機能しますか?より良いアイデアはありますか?

class ChildClass
{
public:
    ParentClass* GetParent()
    {
        return reinterpret_cast<ParentClass*>(
            reinterpret_cast<int8_t*>(this) - offsetof(ParentClass, childObj)
            );
    }
};
4

2 に答える 2

3

将来の訪問者のためのより一般的な解決策は次のとおりです。

#include <cstddef>
#include <type_traits>

template <class Struct, std::size_t offset, class Member>
Struct &get_parent_struct_tmpl(Member &m){
    static_assert(std::is_standard_layout<Struct>::value,
                  "Given struct must have a standard layout type");
    return *reinterpret_cast<Struct *>(reinterpret_cast<char *>(&m) - offset);
}
#define get_parent_struct(STRUCTNAME, MEMBERNAME, MEMBERREF)\
    get_parent_struct_tmpl<STRUCTNAME, offsetof(STRUCTNAME, MEMBERNAME)>(MEMBERREF)

テストケース:

#include <cassert>

struct Foo{
    double d;
    int i;
    bool b;
    char c;
    bool b2;
};

int main(){    
    Foo f;
    bool &br = f.b;

    Foo &fr = get_parent_struct(Foo, b, br);

    assert(&fr == &f);
}

user2079303で言及されているように、指定された構造体が標準レイアウトstatic_assertを持たないことによって引き起こされる UB を防御する があります。

示されているコードには C++11 が必要ですが、削除#include <type_traits>static_assertて C++03 でコンパイルすることはできますが、標準のレイアウト タイプであることを手動で確認する必要があります。

于 2016-09-03T13:01:23.007 に答える