C ++无效对象返回语义

时间:2016-05-05 00:09:42

标签: c++

无法在任何类似名称的问题中找到答案。

我希望用户能够在对象的生命周期中的任何时刻初始化字符串成员,不一定在构造中,但是我希望他们知道该对象在字符串初始化之前是无效的... < / p>

创建简单类时,请说明以下内容:

#include <string>

class my_class {
  public:

    my_class() : _my_str() { }

    my_class(const std::string & str) : my_class() {
      set_my_str(str);
    }

    std::string get_my_str() const {
      return _my_str;
    }

    void set_my_str(const std::string & str) {
      _my_str = str;
    }

  private:

    std::string _my_str;
};

并且用户创建了一个类的空实例(即使用空构造函数),_ my_str将是一个空/未初始化的字符串?

所以,我看到了两种处理行为的方法:上面提到的方法,返回空字符串,或者可能的第二种方式:

#include <string>

class my_class {
  public:

    my_class() : _my_str(), _my_str_ptr(nullptr) { }

    my_class(const std::string & str) : my_class() {
      set_my_str(str);
    }

    std::string * get_my_str() const {
      return _my_str_ptr;
    }

    void set_my_str(const std::string & str) {
      _my_str = str;
      _my_str_ptr = &_my_str;
    }

  private:

    std::string _my_str;
    std::string * _my_str_ptr;
};

返回nullptr的位置,并保持指向局部变量的指针?

这是有效行为吗?哪种方式首选,为什么?第二种方式不会更好,因为你告诉用户,“听,这个对象目前无效,你需要初始化它”,同时仍暗示你正在管理这种对象的生命周期。

3 个答案:

答案 0 :(得分:2)

  

_my_str将是一个空/未初始化的字符串?

空,是的。没有初始化,没有。它已完全初始化(为空字符串)。

  

返回nullptr的位置,并保持指向局部变量的指针?

     

这是有效行为吗?

是的,它有效,但

  

哪种方式首选,为什么?第二种方式不会更好,因为你告诉用户,&#34;听,这个对象目前无效,你需要初始化它&#34;同时仍然暗示你正在管理这种对象的生命周期。

为此维护两个不同的成员变量是完全没有意义的。听起来你需要的是std::optional(或Boost中的等价物,boost::optional),因此_my_str有两种状态:空/无效(不包含字符串)和非空/有效(包含字符串):

#include <string>
#include <experimental/optional>

using std::experimental::optional;

class my_class {
  public:

    my_class() /* default-initializes _my_str as empty */ { }

    my_class(const std::string & str) : _my_str(str) { }

    const std::string * get_my_str() const {
      if (_my_str) // if it exists
        return &*_my_str; // return the string inside the optional
      else
        return nullptr; // if the optional is empty, return null
    }

    /* Or simply this, if you don't mind exposing a bit of the
       implementation details of the class:
    const optional<std::string> & get_my_str() const {
      return _my_str;
    }
    */

    void set_my_str(const std::string & str) {
      _my_str = str;
    }

  private:

    optional<std::string> _my_str;
};

如果""(空字符串)可以用作sentinel value来表示&#34;空/无效&#34;在你的情况下,你可以这样做:

#include <string>

class my_class {
  public:

    my_class() /* default-initializes _my_str as "" */ { }

    my_class(const std::string & str) : _my_str(str) { }

    const std::string * get_my_str() const {
      if (!_my_str.empty()) // if it'a non-empty
        return &_my_str; // return the non-empty string
      else
        return nullptr; // if it's empty, return null
    }

    void set_my_str(const std::string & str) {
      _my_str = str;
    }

  private:

    std::string _my_str;
};

答案 1 :(得分:2)

  1. std::string值不是每个定义无效。它只是空的。

  2. 重要的区别在于第二个“get _...”方法不会复制对象但会给用户一个非常量指针指向内部字符串,这会导致违反const正确性,因为你暗示了这个类可能不会通过在get方法中使用const来更改,同时仍然提供可能更改内部状态的指针。

  3. 如果你的逻辑意味着“空字符串”==“无效”,如果这是一个可能的状态,用户是否必须这样做没有太大的区别

    • if (get_my_str())) // use valid pointer to nonempty string
    • if(!get_my_str().empty()) // use valid nonempty string
  4. 我想。

    1. 您希望从get方法返回std::string const &并将其保留给用户,以便复制该对象。

      4.1。没有强制副本(相对于值返回std::string

      4.2。没有指针可能是nullptr并且意外地被解除引用。

      4.3。绕过并存储一个可能比对象寿命更长的指针更常见于悬挂引用。

    2.   

      我希望用户以后能够初始化字符串,不一定在构造中,但我希望他们能够知道该对象在字符串初始化之前是无效的...

      问题是:正确初始化后,空字符串实际上是“有效”值吗?

      • 如果是:使用optional添加一个额外的状态信令有效性。
      • 如果不是:让字符串的空白代表您的对象无效。

答案 2 :(得分:2)

通常,您引用的模式称为Null object模式。

最古老的方式&#34;实现它是使用变量的一个可能的值并保留它为&#34;没有值&#34;含义。在字符串的情况下,通常以这种方式使用空字符串。当需要所有值时,显然并非总是可能。

旧方式#34;始终使用指针 - (const T* get_t() const)。这样,整个变量值范围可能是有意义的,并且仍然没有价值&#34;通过返回空指针可以获得语义。这样更好,但仍然指针使用不舒服,不安全。如今,指针通常是糟糕的工程。

现代的方式是optional<T>(或boost::optional<T>)。