依靠布尔值访问指针



我有一个名为MyClass的类,它包含一个指向对象的指针,如下所示:

class MyClass {
public:
    MyClass() : _blob(nullptr) {}
    ~MyClass() { free(); }
    void generate() {
        if(!_blob)
            _blob = new Blob;
    }
    void free() {
        if(_blob) {
            delete _blob;
            _blob = nullptr;
        }
    }
    Blob* getBlob() { return _blob; }
private:
    Blob *_blob;
};

它之所以持有指向堆分配对象的指针,而不在堆栈中包含它,是因为一些MyClass实例不为该对象分配数据,因此在访问它之前,我需要检查它是否不是空指针:

if(myClass->getBlob())
    myClass->getBlob()->doSomething();

现在我有了这个想法,存储一个名为_hasBlob的bool变量,然后像一样使用它

if(myClass->hasBlob())
    myClass->getBlob()->doSomething();

这被认为是更快和合法的吗?或者这被认为是一种糟糕的做法,因为我可以看到以某种方式取消引用nullptr的潜在危险。

它是合法的,但却是多余的。它并不快。事实上,虽然检查本身也很快,但保持布尔值与指针有效性同步的速度稍微慢一些。最糟糕的是,要证明布尔值总是同步的,这是一项维护负担。一旦它被实现并被证明是正确的,它就是多余的,浪费内存。

通常,无论何时请求,MyClass::getBlob()都会给您一个有效的对象。因此,通常我的建议是实现以下方法:

Blob* getBlob() { 
  if(_blob == nullptr) { 
     _blob = new Blob; 
     // Or call private method generateBlob, if there is a lot of logic
  }
  return _blob; 
}

或者,如果默认构造的Blob很小,并且您没有足够多的MyClass实例,您可以在构造函数中创建Blob对象,并让getBlob返回引用,因此一旦您有了有效的MyClass对象,就不必检查任何内容。

如果你想避免自动创建Blob对象,你可以添加一个hasBlob检查,但我不会保留一个单独的布尔值,而是将其实现为

bool hasBlob() const { return _blob != nullptr; }

这个函数几乎可以肯定是内联的,不会花费任何额外的存储空间,而且它保证了正确的结果(例如,设置_hasBlob = true然后无法分配Blob是不可能的)。正如您所说,仍然存在取消引用空指针的风险,但正如您已经清楚地记录的那样(希望如此),如果没有为该实例分配Blob,getBlob可能会返回空指针,这种风险现在由调用者承担,他们应该注意检查结果,就像任何返回指针的函数一样。事实上,这个解决方案与您已经拥有的代码完全等效,因为if(myObject->getBlob())现在执行的检查与if(myObject->hasBlob())完全相同——唯一的区别是后者可能更具自文档性。

由于您在评论中表示担心性能:我怀疑根据null检查指针的速度相当快,但与往常一样,如果您想确定,通常的建议是"测量它!"。例如,您可能会发现,由于getBlob的第一个版本中有额外的检查,编译器将不会内联函数。

最新更新