C ++返回默认值NullObjectPattern

时间:2018-04-06 19:33:53

标签: c++ c++11 design-patterns coding-style

我对某种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;哪个更好?码?           有没有(更好的)选择?           也许我的例子错了?

由于

1 个答案:

答案 0 :(得分:1)

TL; DR :这取决于您的应用程序的上下文,但在使用第二种技术时您应该考虑一些严重的安全问题。

在这种情况下,更好是主观的,因为它取决于具体情况,但有一些客观的权衡和安全问题可以区分这两种技术。

在第一种情况下,您为每个调用创建完全不同的对象。这需要使用一些循环来实例化对象。根据对象的复杂程度,这可能是微不足道的,或者可能非常昂贵。例如,如果对象的构造函数通过网络进行调用并阻塞,则持续构造这些对象可能是一个糟糕的性能决策。这些是否是严重的性能问题取决于使用它的上下文。例如:

  • 在整个系统生命周期内,此方法平均调用了多少次?
  • 返回的对象在范围内停留多长时间,从而占用堆栈空间?
  • 此特定应用程序中的堆栈空间有多重要?这种方法可以说更简单,因为它不需要将一个字段(无论是类还是静态)添加到类中。

第二种方法在每次调用时节省了构造的开销(当初始化静态变量时执行一次),但它有一些应该考虑的严重安全问题。返回的对象由调用此方法的所有客户端共享,如果某个实体更改共享对象的值,则可能会出现问题。虽然共享对象作为const引用返回,但这并不意味着对象的值不能更改(即SomeObject对象的内部字段可以更改)。有三种直接的方法可以更改值,即使它以const的形式返回(如果使用更多的侵入式方法,还有更多的方法):

  1. NullObject更改了值。相对于const,此共享对象不是NullObject,因此NullObject如果SomeObject是可变的,则可以更改值SomeObject。如果进行了更改,则获得对此共享对象的引用的所有客户端都将看到此更改。如果对象是真正不可变的(参见(2)和(3)有关不可变性的方法),则没有什么可担心的,因为共享对象的值一旦被实例化就无法更改,但是如果对象是可变的,可以更改其值,并且获取共享对象的所有客户端都可以看到该更改。
  2. 即使对象为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;
    }
    

    这可能是不好的形式,有可能,因此必须加以考虑。

  3. 返回值的常量被丢弃。例如:

    std::vector<SomeObject>& mutable_vector = const_cast<std::vector<SomeObject>&>(instance.get());
    
  4. 考虑到这三个问题,重要的是要非常精确地确定在C ++中共享什么类型的对象,即使它们被表示为const。尽管const关键字似乎可以提供不变性,但仍然存在这种不变性的方法,其中一些方法可以由客户端执行。

    通常,每个解决方案的性能取决于应用程序的上下文,您应该使用这两种技术进行性能测试,以便了解计算方面对应用程序的影响(执行需要多长时间) ,内存消耗(执行时占用多少空间),以及与您的上下文相关的许多其他因素。此问题也相当于一个更普遍的问题:何时缓存值。在这种特殊情况下,该值旨在是不可变的,因此您不必担心生存时间(TTL),意义到期或陈旧数据,但有些概念会转换,例如&#34;是否值得为了性能的利益而引入缓存的复杂性?&#34;