C++无效的对象返回语义
C++ invalid object return semantics
在任何类似的命名问题中都找不到答案。
我希望用户能够在对象生命周期的任何时候初始化字符串成员,不一定是在构造时,但我希望他们知道,在初始化字符串之前,对象是无效的。。。
创建一个简单的类时,请说出以下内容:
#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,并维护指向局部变量的指针?
这种行为有效吗?首选哪种方式,为什么?第二种方法不是更好吗?因为你告诉用户,"听着,这个对象当前无效,你需要初始化它",同时仍然暗示你正在管理这个对象的生存期。
_my_str将是一个空的/未初始化的字符串?
空的,是的。未初始化,否。它已完全初始化(为空字符串)。
在哪里返回nullptr,并维护指向局部变量的指针?
这种行为有效吗?
是的,它是有效的,但是
首选哪种方式,为什么?第二种方式不是更好吗?因为你告诉用户;听着,这个对象当前无效,你需要初始化它"同时仍然暗示您正在管理这样的对象的生存期。
为此维护两个不同的成员变量是完全没有意义的。听起来您需要的是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;
};
如果CCD_ 4(一个空字符串)可用作一个标记值;空/无效";在您的情况下声明,然后您可以这样做:
#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;
};
通常,您所指的模式称为Null对象模式。
实现它的"最古老的方法"是使用变量的一个可能值,并将其保留为"无值"的含义。在字符串的情况下,通常以这种方式使用空字符串。显然,当需要所有值时,这并不总是可能的。
"老办法"总是使用指针-(const T* get_t() const
)。通过这种方式,整个变量值范围可能是有意义的,并且仍然可以通过返回null指针来获得"无值"语义。这是更好的,但指针使用起来仍然不舒服,也不安全。如今,指针通常是糟糕的工程。
现代的方式是optional<T>
(或boost::optional<T>
)。
-
空的
std::string
值并非每个定义都无效。它只是空的。 -
一个重要的区别是,第二种"get_…"方法不复制对象,而是向用户提供一个指向内部字符串的非常量指针,这会导致违反常量正确性,因为你暗示在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
添加一个额外的状态信令有效性 - 如果不是:让字符串的空表示对象无效
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- 复制省略并在返回值中移动语义
- 移动语义与返回shared_ptr?
- 了解函数返回对象的移动语义
- 默认情况下,返回是否使用移动或复制语义
- 放置 new 的返回值与其操作数的强制转换值之间是否存在(语义)差异
- 在向量返回上移动语义行为
- 用移动语义返回两个对象
- 在 c++11 中,虚函数能否通过移动语义有效地返回大值
- 如何使用 C++11 移动语义从函数返回 std::vector
- C++无效的对象返回语义
- C++11在返回本地对象时移动语义
- 返回本地对象是否需要移动语义
- 从具有移动语义或返回值优化的函数返回值,但不返回复制构造函数
- 有没有办法使用 move 而不是复制语义将函数返回值(对象)包装在 Python 中
- 将对以字符串形式返回文件内容的函数执行移动语义优化或返回值优化,我会从中受益吗
- 为原始指针(如所有权语义)返回unique_ptr的不良做法
- 从语义操作返回的值会干扰规则属性
- 移动语义并返回常量值
- 返回类型和移动语义