无法在任何类似名称的问题中找到答案。
我希望用户能够在对象的生命周期中的任何时刻初始化字符串成员,不一定在构造中,但是我希望他们知道该对象在字符串初始化之前是无效的... < / 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的位置,并保持指向局部变量的指针?
这是有效行为吗?哪种方式首选,为什么?第二种方式不会更好,因为你告诉用户,“听,这个对象目前无效,你需要初始化它”,同时仍暗示你正在管理这种对象的生命周期。
答案 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)
空std::string
值不是每个定义无效。它只是空的。
重要的区别在于第二个“get _...”方法不会复制对象但会给用户一个非常量指针指向内部字符串,这会导致违反const正确性,因为你暗示了这个类可能不会通过在get方法中使用const
来更改,同时仍然提供可能更改内部状态的指针。
如果你的逻辑意味着“空字符串”==“无效”,如果这是一个可能的状态,用户是否必须这样做没有太大的区别
if (get_my_str())) // use valid pointer to nonempty string
与if(!get_my_str().empty()) // use valid nonempty string
我想。
您希望从get方法返回std::string const &
并将其保留给用户,以便复制该对象。
4.1。没有强制副本(相对于值返回std::string
)
4.2。没有指针可能是nullptr并且意外地被解除引用。
4.3。绕过并存储一个可能比对象寿命更长的指针更常见于悬挂引用。
我希望用户以后能够初始化字符串,不一定在构造中,但我希望他们能够知道该对象在字符串初始化之前是无效的...
问题是:正确初始化后,空字符串实际上是“有效”值吗?
optional
添加一个额外的状态信令有效性。答案 2 :(得分:2)
通常,您引用的模式称为Null object模式。
最古老的方式&#34;实现它是使用变量的一个可能的值并保留它为&#34;没有值&#34;含义。在字符串的情况下,通常以这种方式使用空字符串。当需要所有值时,显然并非总是可能。
旧方式#34;始终使用指针 - (const T* get_t() const
)。这样,整个变量值范围可能是有意义的,并且仍然没有价值&#34;通过返回空指针可以获得语义。这样更好,但仍然指针使用不舒服,不安全。如今,指针通常是糟糕的工程。
现代的方式是optional<T>
(或boost::optional<T>
)。