我有一个名为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
的第一个版本中有额外的检查,编译器将不会内联函数。