if (this == NULL)Нормально ли что
...;
this
может быть нулевым, ведь при этом даже к членам класса обратиться нельзя, не то что вызвать виртуальную функцию.Однако тех, кого не удивляет конструкции вида
delete this;
, не удивит и проверка this на NULL. В самом деле, this - это обычный указатель, и как любой другой указатель он может быть равен NULL:class foo {
public:
bool am_i_alive() { return this != NULL; }
};
foo *p = NULL;
assert(!p->am_i_alive());
Но не разумнее ли, спрашивается, проверять указатель на NULL "снаружи" класса (в данном примере - проверять p на NULL), а не внутри его. То есть не вызывать методы объекта, если указатель на него нулевой. И так действительно будет разумнее:
void foo(Bar &bar, IFilter *filter = NULL)Единственное преимущество внесения проверки внутрь класса - сокращение вызывающего кода, немного повышающее его написание и читаемость. То есть по сути "синтаксический сахар":
{
if (filter)
filter->init(bar);
for (auto b : bar)
if (!filter || filter->is_accepted(b))
out.insert(b);
}
void foo(Bar &bar, IFilter *filter = NULL)Но ценой этого будет то, что вызываемые функции не должны быть виртуальными, то есть придется каждую виртуальную функцию "обернуть" примерно так:
{
filter->init(bar);
for (auto b : bar)
if (filter->is_accepted(b))
out.insert(b);
}
class IFilter
{
public:
void init(Bar& bar) { if (this) init_impl(bar; }
bool is_accepted(Bar::value_type b) { return !this || is_accepted_impl(b); }
private:
virtual void init_impl(Bar&) = 0;
virtual bool is_accepted_impl(Bar::value_type) = 0;
};
Любая книжка по рефакторингу скажет, что добиться подобного кода можно с помощью определеннго паттерна, который предлагает замену нулевых указателей на указатели ненулевые, но указывающие на объект-пустышку:
class IFilter
{
public:
virtual void init(Bar&) = 0;
virtual bool is_accepted(Bar::value_type) = 0;
};
class NullFilter
{
public:
virtual void init(Bar&) {}
virtual bool is_accepted(Bar::value_type) { return true; }
};
void foo(Bar &bar, IFilter &filter = NullFilter())
{
filter.init(bar);
for (auto b : bar)
if (filter.is_accepted(b))
out.insert(b);
}
В качестве члена какого-нибудь класса это может выглядеть так:
class FooЕдинственное, что может заставить не послушаться книжку по рефакторингу, так это профайлер ;)
{
public:
Foo() : filter(new NullFilter) {}
void set_filter(auto_ptr<IFilter> f) { filter = f; }
void use_filter()
{
// можно смело использовать конструкции filter.get()->xxx()
}
private:
auto_ptr<IFilter> filter;
};