Другое событие NullPointerException
возникает, когда объявляется массив объектов, а затем сразу же пытается разыменовать его внутри.
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Этот конкретный NPE можно избежать, если порядок сравнения отменяется ; а именно, использовать .equals
для гарантированного непустого объекта.
Все элементы внутри массива инициализируются их общим начальным значением ; для любого типа массива объектов, это означает, что все элементы null
.
Вы должны инициализировать элементы в массиве перед доступом или разыменованием их.
String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Это зависит от Вашего варианта использования. Если Вы когда-нибудь думаете, что необходимо будет звонить clone
на производном объекте, динамический тип которого Вы знаете (помните, смысл clone
должен позволить копировать без знание динамического типа), то необходимо, вероятно, возвратить немой указатель и загрузку это в интеллектуальный указатель в коде вызова. В противном случае тогда только необходимо возвратить smart_ptr и таким образом, можно не стесняться возвращать его во всех переопределениях.
Используйте Общественность, невиртуальную / Частный виртуальный шаблон:
class Base {
public:
std::auto_ptr<Base> clone () { return doClone(); }
private:
virtual Base* doClone() { return new (*this); }
};
class Derived : public Base {
public:
std::auto_ptr<Derived> clone () { return doClone(); }
private:
virtual Derived* doClone() { return new (*this); }
};
Синтаксис не вполне как хороший, но если Вы добавляете это к своему коду выше, разве это не решает все Ваши проблемы?
template <typename T>
std::auto_ptr<T> clone(T const* t)
{
return t->clone();
}
Я думаю, что функциональная семантика так ясна в этом случае, что существует мало пространства для беспорядка. Таким образом, я думаю, что можно использовать ковариантную версию (та, возвращая немой указатель на реальный тип) с легкой совестью, и вызывающие стороны будут знать, что они получают новый объект, свойство которого передается им.
Tr1::shared_ptr<>
может быть литым как он, был необработанный указатель.
я думаю, имеют клон (), возвращаются shared_ptr<Base>
, указатель является довольно чистым решением. Можно бросить указатель на shared_ptr<Derived>
посредством [1 110] tr1::static_pointer_cast<Derived>
или tr1::dynamic_pointer_cast<Derived>
в случае, если не возможно определить вид клонированного объекта во время компиляции.
Для обеспечения вида объекта predictible, можно использовать полиморфный бросок для shared_ptr как этот:
template <typename R, typename T>
inline std::tr1::shared_ptr<R> polymorphic_pointer_downcast(T &p)
{
assert( std::tr1::dynamic_pointer_cast<R>(p) );
return std::tr1::static_pointer_cast<R>(p);
}
издержки, добавленные утверждением, будут выброшены в версии выпуска.
Это - одна причина использовать boost::intrusive_ptr
вместо shared_ptr
или auto/unique_ptr
. Необработанный указатель содержит подсчет ссылок и может использоваться больше беспрепятственно в таких ситуациях.
У Вас могло быть два метода, виртуальный клон (), который возвращает обертку интеллектуального указателя вокруг базового типа и невиртуальный clone2 (), который возвращает корректный тип интеллектуального указателя.
clone2 был бы, очевидно, реализован с точки зрения клона и инкапсулировал бы бросок.
Тот путь может получить наиболее полученный интеллектуальный указатель, который Вы знаете во время компиляции. Это не может быть большая часть производного типа в целом, но это использует всю информацию, доступную компилятору.
Другая опция состояла бы в том, чтобы создать шаблонную версию клона, который принимает тип, который Вы ожидаете, но это добавляет больше нагрузки на вызывающей стороне.