【问题标题】:How to implement a null object?如何实现一个空对象?
【发布时间】:2015-04-05 18:20:51
【问题描述】:

详情

我在此处 (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;
}

可能的解决方案 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;
};

现在很清楚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;
};

问题

这个概念是否有既定的模式或习语?如果没有,有什么实施建议吗?

基本原理

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

编辑

看起来std::experimental::optional(@myaut 提请我注意)是我想要使用的,但我无权访问它。

现在我需要使用适用于 Visual Studio 2013(2015 可能还可以)和 g++ 4.8 的解决方案。

【问题讨论】:

  • 或许你应该看看std::optional
  • @myaut 谢谢,我不熟悉这个,但我会研究一下。
  • 我认为您看不到在 C++ 中实现的这种模式是因为不需要它。在 Java 和 C# 中,将 null 作为无效结果返回是很常见的。由于 Java/C# 中的所有内容都在堆中分配(甚至原语也自动包装为对象),因此每个函数都可以返回 null。在 C++ 中,不可能在不返回指针的函数上返回 null。您必须抛出异常或以其他方式处理无效,而不是返回 null。这是 C++ 开发人员不返回 null 对象的方式——仅仅因为 C++ 通过其他方式处理无效。
  • @DavidHaim 我并不是想通过提出以下问题来断言你错了,我只是好奇。如果 C++ 中不需要 std::optional 有什么意义?我肯定想知道区别所以我应该在 C++ 中处理这个吗?您是否建议选项 1
  • @DavidHaim:实际上即使在 Java 中这也可能被认为是有害的(即 Robert Martin 在他的“清洁代码”一书中反对这一点)。

标签: c++


【解决方案1】:

来自this question(想点赞;):

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

Visual Studio 附带的受支持的 C++11/14/1z 核心和库功能的最新列表可从 Visual C++ Team blog,尤其是 this post 中找到。一组来自微软的标准库实现(和一些扩展)的头文件可以查看here

我最近尝到了它的滋味,花了一些精力来构建它,我设法使用它并且对它很满意。希望对您有所帮助。

【讨论】:

  • 我最终实现了自己的 Optional 类。
  • @JamesAdkison 太好了!你可以用Optional的实现来回答你自己的问题:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-11
  • 2012-01-18
  • 1970-01-01
  • 2011-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多