如何实现null对象?

Jam*_*son 6 c++

细节

我在这里找到了一些关于Null对象模式的信息(https://softwareengineering.stackexchange.com/questions/152094/null-pointers-vs-null-object-pattern)和这里(http://en.wikipedia.org/ wiki/Null_Object_pattern#C.2B.2B).

但是,C++实现并没有说明我的用例.

我还看到了Nullable Type的相关链接(http://en.wikipedia.org/wiki/Nullable_type).

用例

我有一个不属于层次结构的对象,通常不会在堆上分配.此外,没有一个方便的值可以用作指示null的标记.希望以下代码使用例清晰.

class ContrivedType
{
public:
    ContrivedType() :
        mValue(0)
    {
        // Do nothing
    }

    bool operator==(const ContrivedType& other) const
    {
        return mValue == other.mValue;
    }

    void setValue(std::uint16_t value)
    {
        mValue = value;
    }

private:
    // All values in the range [0, 65535] are valid for use
    std::uint16_t mValue;
};

class Foo
{
public:
    const ContrivedType getValue() const
    {
        return mValue;
    }

    void setValue(const ContrivedType &value)
    {
        mValue = value;
    }

private:
    ContrivedType mValue;
};

int main()
{
    Foo f;

    if (f.getValue() == ContrivedType())
    {
        // Ambiguous case
        // -    Was this value explicitly set to be the same value
        //      as when it's default constructed
        // OR
        // -    Was the value never set
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

可能的解决方案1

迫使用户ContrivedType需要在默认状态和未设置之间消除歧义以使用指针并动态分配ContrivedType.也许是这样的?

class Foo
{
public:
    Foo() :
        mValue(nullptr)
    {
        // Do nothing
    }

    const ContrivedType* getValue() const
    {
        return mValue.get();
    }

    void setValue(const ContrivedType &value)
    {
        if (!mValue)
        {
            mValue.reset(new ContrivedType(value));
        }
        else
        {
            *mValue = value;
        }
    }

private:
    std::unique_ptr<ContrivedType> mValue;
};
Run Code Online (Sandbox Code Playgroud)

现在很清楚是否ContrivedType已经设定.

可能的解决方案2

更新实现ContrivedType以支持null的概念.

class ContrivedType
{
public:
    ContrivedType() :
        mState(nullptr)
    {
        // Do nothing
    }

    explicit ContrivedType(std::uint16_t value) :
        mState(&mStorage)
    {
        mStorage.mValue = value;
    }

    bool isNull() const
    {
        return mState == nullptr;
    }

    bool operator==(const ContrivedType& other) const
    {
        if (!isNull())
        {
            return mStorage.mValue == other.mStorage.mValue;
        }
        else
        {
            return other.isNull();
        }
    }

    void setValue(std::uint16_t value)
    {
        mStorage.mValue = value;

        if (!mState)
        {
            mState = &mStorage;
        }
    }

private:
    struct State
    {
        // All values in the range [0, 65535] are valid for use
        std::uint16_t mValue;
    };

    State mStorage;

    // This will point to the storage when a value actually set
    State* mState;
};
Run Code Online (Sandbox Code Playgroud)

这个概念是否有既定的模式或成语?如果没有,是否有任何实施建议?

合理

在实际代码中,有一些具有1个或更多成员的类在某些情况下是可选的.这些类使用支持缺失字段的协议(即可选字段)在套接字上进行序列化.序列化可以跳过可选字段,而不是浪费字节序列化未明确设置的默认构造对象.例如,一个updateFoo(const Foo&)函数.如果仅Foo更新现有实例的子集,则只需要序列化这些字段.

编辑

它似乎std::experimental::optional(由@myaut引起我的注意)是我想要使用但我无法访问它.

现在我需要使用一个可以与Visual Studio 2013(2015可能还可以)和g ++ 4.8一起使用的解决方案.

YSC*_*YSC 4

这个问题(认为赞成它;):

std::experimental::optional源自Boost.Optional库,此实现在 Visual C++ 12.0 中运行良好(尽管略有不同可以在此处找到基于N3793提案论文的参考单标头实现。

Visual Studio 附带的受支持的 C++11/14/1z 核心和库功能的最新列表可以从Visual C++ 团队博客(特别是这篇文章)中找到。可以在此处查看 Microsoft 标准库实现(和一些扩展)的一组头文件。

我最近尝到了它的滋味,经过一些努力来构建它,我设法使用它并且对它很满意。希望能帮助到你。