Это означает, что указанная переменная не указана ни на что. Я мог бы сгенерировать это так:
SqlConnection connection = null;
connection.Open();
Это вызовет ошибку, потому что, пока я объявил переменную «connection
», она не указала ни на что. Когда я пытаюсь вызвать член «Open
», для его устранения нет ссылки, и он будет вызывать ошибку.
Чтобы избежать этой ошибки:
object == null
. Инструмент Resharper JetBrains определит каждое место в вашем коде, которое имеет возможность ошибки нулевой ссылки, позволяя вам ввести нулевую проверку. Эта ошибка является источником ошибок номер один, IMHO.
Первый class foo;
называется объявлением forward класса foo. Он просто позволяет компилятору знать, что он существует, и что он называет класс. Это делает foo тем, что называется «неполным типом» (если полное объявление foo уже не было замечено). С неполным типом вы можете объявлять указатели этого типа, но вы не можете выделять экземпляры этого типа или делать что-либо, требующее знания его размера или членов.
Такие форвардные объявления часто используются, когда два типа могут имеют указатели друг на друга, и в этом случае оба должны уметь выражать понятие указателя на другой тип, и поэтому у вас будет круговая зависимость без такой вещи. Это необходимо в основном потому, что C ++ использует один проходный механизм для разрешения типов; в Java вы можете иметь циклические зависимости без форвардных объявлений, потому что Java использует несколько проходов. Вы также можете видеть передовые декларации, когда автор находится под ошибочным впечатлением, что использование форвардных объявлений вместо включения требуемого заголовка сокращает время компиляции; это, конечно, не так, потому что вам нужно включить полное объявление (т. е. заголовок), так или иначе, и если используются препроцессорные охранники, то в отличие от времени компиляции нет никакой разницы.
Чтобы ответить на ваш вопрос о том, нужно ли вам включать или нет, предполагается, что вам нужен только частичный тип, тогда вашему заголовку не нужно напрямую включать заголовок для типа, который был объявлен вперед; однако тот, кто использует ваш заголовок, когда они используют ваш тип, должен будет включить заголовок для объявленного вперед типа, и поэтому вы можете просто включить другой заголовок.
Это называется форвардной декларацией. Тело класса foo
будет определено в более поздней части файла. Для определения циклических зависимостей выполняется следующее: определение класса Bar требует класса Foo и наоборот.
class Bar
{
Foo * foo;
};
class Foo
{
Bar * bar;
};
Как вы можете видеть, Bar
имеет ссылку на Foo
и наоборот , Если вы попытаетесь скомпилировать это, компилятор заявит, что он ничего не знает о Foo
. Решение состоит в том, чтобы переслать объявление class Foo
над Bar
(так же, как вы объявляете прототип функции над main
и определяете ее тело позже).
class Foo; //Tells the compiler that there is a class Foo coming down the line.
class Bar
{
Foo * foo;
};
class Foo
{
Bar * bar;
};
это передовое объявление класса. По моему опыту, это обычно делается, когда у вас есть круговая зависимость .. например
in foo.h
--------
#include "bar.h"
class foo
{
public:
foo(bar *bar);
private:
foo *m_foo;
};
and in bar.h
------------
class foo;
class bar
{
public:
void methodFooWillCall();
protected:
std::list<foo *> myFoos;
}
Это форвардная декларация класса 'foo'. Он позволяет объявлять указатели и ссылки на класс, но не использовать его (например, члены вызова или определить его размер), потому что он еще не определен! Это должно быть продолжено с полным нормальным объявлением (class foo { ... };
).
Это полезно для таких вещей, как объявление двух классов, содержащих указатели друг на друга, которые иначе было бы невозможно настроить.
Любопытно, зачем нам вообще нужно выражение forward ? Не является ли декларацией для перехода просто декларацией (в отличие от определения)?
class X; // declaration
class X // definition
{
int member;
void function();
};
Это forward declaration . Вам это нужно, например, если в строке класса указатель на объект foo, но вы не хотите сразу включать полное определение объекта foo.
Это называется форвардным объявлением. Он используется, чтобы ваш код знал, что класс foo существует. Это, в свою очередь, может использоваться панелью классов.
Обычно используется для решения круговых задач. Возьмем это, например,
//a.h
#include "b.h"
class A
{
void useB(B obj);
}
и
// b.h
#include "a.h"
class B
{
void useA(A obj);
}
Это вызывает проблему с круговым включением, так как a.h включает b.h, который по очереди включает a.h, в бесконечность. Вы можете решить эту проблему, сделав перед каждым заголовком декларацию в прямом выражении следующим образом:
//a.h
class B;
class A
{
void useB(B obj);
}
// b.h
class A;
class B
{
void useA(A obj);
}
Примечание. Очень часто, когда у вас есть проблема с круговым включением, это указывает на концепцию / моделирование проблема. Вероятно, вы должны спросить себя, четко ли определены ваши классы, прежде чем пытаться решить вашу проблему с помощью передовых объявлений.
Попытайтесь написать это:
file bar.h:
#include "bar.h"
class Foo
{
Bar* bar_ptr;
}
file foo.h:
#include "foo.h"
class Bar
{
Foo* foo_ptr;
}
t, сначала из-за бесконечной цепочки #include, то, если вы избавитесь от одного из включений, Foo не будет знать, что такое Bar, или Bar не будет знать, что такое Foo.
Попробуйте это вместо:
class Bar;
class Foo
{
Bar* bar_ptr;
};
file foo.h:
class Foo;
class Bar
{
Foo* foo_ptr;
};
Это передовая декларация. Рассмотрим следующий пример:
class foo; // you likely need this for the code beneath to compile
class bar {
void smth( foo& );
};
Если вы не включили определение class foo
таким образом, что компилятор видит его перед компиляцией определения class bar
, код не будет компилироваться ( компилятор скажет, что не знает, что означает foo
), если у вас нет прямого объявления.