我对某种Null对象模式有疑问。 当我想到一个吸气剂(我知道我们应该避免这种情况,但假设) 我看到了两种方法。
假设我们有一个班级NullObject.cpp
1)
class NullObject
{
std::vector<SomeObject> get() { return {}; }
}
class SomeImplementation
{
std::vector<SomeObject> get() { return someVectorMember; }
{
const std::vector<SomeObject>& object = instance.get();
因此,在第一个示例中,我们将始终按值返回并分配给const Object&
2)
class NullObject
{
const std::vector<SomeObject>& get() { return member; }
static std::vector<SomeObject> member;
}
class SomeImplementation
{
const std::vector<SomeObject>& get() { return someVectorMember; }
{
const std::vector<SomeObject>& object = instance.get();
在这种情况下,我们在Null类中有静态成员,所以我们可以返回一个const引用。
问题:例如,在性能方面哪个更好? 对于&#34;清洁&#34;哪个更好?码? 有没有(更好的)选择? 也许我的例子错了?
由于
答案 0 :(得分:1)
TL; DR :这取决于您的应用程序的上下文,但在使用第二种技术时您应该考虑一些严重的安全问题。
在这种情况下,更好是主观的,因为它取决于具体情况,但有一些客观的权衡和安全问题可以区分这两种技术。
在第一种情况下,您为每个调用创建完全不同的对象。这需要使用一些循环来实例化对象。根据对象的复杂程度,这可能是微不足道的,或者可能非常昂贵。例如,如果对象的构造函数通过网络进行调用并阻塞,则持续构造这些对象可能是一个糟糕的性能决策。这些是否是严重的性能问题取决于使用它的上下文。例如:
第二种方法在每次调用时节省了构造的开销(当初始化静态变量时执行一次),但它有一些应该考虑的严重安全问题。返回的对象由调用此方法的所有客户端共享,如果某个实体更改共享对象的值,则可能会出现问题。虽然共享对象作为const
引用返回,但这并不意味着对象的值不能更改(即SomeObject
对象的内部字段可以更改)。有三种直接的方法可以更改值,即使它以const
的形式返回(如果使用更多的侵入式方法,还有更多的方法):
NullObject
更改了值。相对于const
,此共享对象不是NullObject
,因此NullObject
如果SomeObject
是可变的,则可以更改值SomeObject
。如果进行了更改,则获得对此共享对象的引用的所有客户端都将看到此更改。如果对象是真正不可变的(参见(2)和(3)有关不可变性的方法),则没有什么可担心的,因为共享对象的值一旦被实例化就无法更改,但是如果对象是可变的,可以更改其值,并且获取共享对象的所有客户端都可以看到该更改。即使对象为mutable
,声明const
的字段也会更改。使用该对象的客户端不知道其内部(即封装),因此他们可能不知道SomeObject
中有标记为mutable
的内部字段并且可能会更改,即使返回的对象是const
。例如:
class SomeObject {
public: void doSomething() const {
// Do some logic
this->count++;
}
public: const int& getCounter() const {
return this->counter;
}
private: mutable int counter = 0;
}
这可能是不好的形式,有可能,因此必须加以考虑。
返回值的常量被丢弃。例如:
std::vector<SomeObject>& mutable_vector = const_cast<std::vector<SomeObject>&>(instance.get());
考虑到这三个问题,重要的是要非常精确地确定在C ++中共享什么类型的对象,即使它们被表示为const
。尽管const
关键字似乎可以提供不变性,但仍然存在这种不变性的方法,其中一些方法可以由客户端执行。
通常,每个解决方案的性能取决于应用程序的上下文,您应该使用这两种技术进行性能测试,以便了解计算方面对应用程序的影响(执行需要多长时间) ,内存消耗(执行时占用多少空间),以及与您的上下文相关的许多其他因素。此问题也相当于一个更普遍的问题:何时缓存值。在这种特殊情况下,该值旨在是不可变的,因此您不必担心生存时间(TTL),意义到期或陈旧数据,但有些概念会转换,例如&#34;是否值得为了性能的利益而引入缓存的复杂性?&#34;