json
ограничено в терминах объектов, которые он может печатать, а jsonpickle
(возможно, вам нужен pip install jsonpickle
) ограничен в терминах, он не может отступать от текста. Если вы хотите проверить содержимое объекта, класс которого вы не можете изменить, я все равно не мог найти более строгий путь:
import json
import jsonpickle
...
print json.dumps(json.loads(jsonpickle.encode(object)), indent=2)
Обратите внимание, что они все еще не могут распечатать объект методы.
Короткий список мог бы быть:
RAII, совместно использованные указатели и минималистское кодирование, конечно, не характерны для C++, но они помогают избежать проблем, которые действительно часто неожиданно возникают при разработке на языке.
Некоторые превосходные книги по этому предмету:
Чтение этих книг помогло мне больше, чем что-либо еще избежать вида ловушек, о которых Вы спрашиваете.
#include <boost/shared_ptr.hpp>
class A {
public:
void nuke() {
boost::shared_ptr<A> (this);
}
};
int main(int argc, char** argv) {
A a;
a.nuke();
return(0);
}
Эссе/статья Указатели, ссылки и Значения очень полезно. Это говорит, стараются не избегать ловушек и хороших методов. Можно просмотреть целый сайт также, который содержит подсказки по программированию, главным образом для C++.
Намерение (x == 10)
:
if (x = 10) {
//Do something
}
я думал, что никогда не буду делать эту ошибку сам, но я на самом деле недавно сделал это.
Всегда проверяйте указатель перед разыменованием его. В C Вы могли обычно рассчитывать на катастрофический отказ в точке, где Вы разыменовываете неверный указатель; в C++ можно создать недопустимую ссылку, которая откажет в месте, далеко удаленном из источника проблемы.
class SomeClass
{
...
void DoSomething()
{
++counter; // crash here!
}
int counter;
};
void Foo(SomeClass & ref)
{
...
ref.DoSomething(); // if DoSomething is virtual, you might crash here
...
}
void Bar(SomeClass * ptr)
{
Foo(*ptr); // if ptr is NULL, you have created an invalid reference
// which probably WILL NOT crash here
}
static_cast
удрученный на виртуальном базовом классе
Едва ли... Теперь о моем неправильном представлении: Я думал, что A
в следующем был виртуальный базовый класс, когда на самом деле это не; это, согласно 10.3.1, полиморфный класс . Используя static_cast
здесь, кажется, прекрасен.
struct B { virtual ~B() {} };
struct D : B { };
, Таким образом, да, это - опасная ловушка.
Blizpasta. Это - огромное, я вижу много...
Неинициализированные переменные являются огромной ошибкой, которую делают мои студенты. Много людей Java забывает, что просто высказывание "международного счетчика" не устанавливает в противоречии с 0. Так как необходимо определить переменные в h файле (и инициализировать их в конструкторе/установке объекта), легко забыть.
Ошибки диапазона на for
циклы / доступ к массиву.
Не правильно убирающий объектный код, когда вуду запускает.
Считайте книжные Глюки C++: Предотвращение Типичных проблем в Кодировании и Дизайне .
Чтобы испортить, используйте прямые указатели много. Вместо этого используйте RAII для почти чего-либо, удостоверяясь, конечно, что Вы используете правильные интеллектуальные указатели. Если Вы пишете, "удаляют" где угодно вне дескриптора или класса типа указателя, Вы очень вероятно делаете его неправильно.
Сохраните пространства имен прямо (включая структуру, класс, пространство имен, и использующий). Это - мое разочарование номер один, когда программа просто не компилирует.
Упущение определить виртуальный деструктор базового класса. Это означает, что вызов delete
на Основе* не закончит тем, что разрушил полученную часть.
Избегайте псевдо классы и квази классы ... Сверхдизайн в основном.
Будьте осторожны при использовании интеллектуальных указателей и контейнерных классов.
PRQA имеют превосходный и бесплатный стандарт кодирования C++ на основе книг от Scott Meyers, Bjarne Stroustrop и Herb Sutter. Это объединяет всю эту информацию в одном документе.
Выезд boost.org . Это обеспечивает большую дополнительную функциональность, особенно их реализации интеллектуального указателя.
Самые важные ловушки для начала разработчиков должны избежать беспорядка между C и C++. C++ никогда нельзя рассматривать как простое лучше C или C с классами, потому что это сокращает его питание и может сделать его даже опасным (особенно при использовании памяти в качестве в C).
Вот несколько ям, в которые у меня была неудача для падения. Все они имеют серьезные основания, которые я только понял, будучи укушенным поведением, которое удивило меня.
virtual
функции в конструкторах не .
не нарушают ODR (Одно Правило Определения) , это - то, для чего анонимные пространства имен (среди прочего).
Порядок инициализации участников зависит от порядка, в котором они объявляются.
class bar {
vector<int> vec_;
unsigned size_; // Note size_ declared *after* vec_
public:
bar(unsigned size)
: size_(size)
, vec_(size_) // size_ is uninitialized
{}
};
Значения по умолчанию и virtual
имеют различную семантику.
class base {
public:
virtual foo(int i = 42) { cout << "base " << i; }
};
class derived : public base {
public:
virtual foo(int i = 12) { cout << "derived "<< i; }
};
derived d;
base& b = d;
b.foo(); // Outputs `derived 42`
Книжные Глюки C++ могут оказаться полезными.
Используя C++ как C. Наличие цикла создавать-и-выпускать в коде.
В C++, это не безопасное исключение, и таким образом выпуск не может быть выполнен. В C++ мы используем RAII для решения этой проблемы.
Все ресурсы, которые имеют руководство, создают, и выпуск должен быть обернут в объект, таким образом, эти действия сделаны в конструкторе/деструкторе.
// C Code
void myFunc()
{
Plop* plop = createMyPlopResource();
// Use the plop
releaseMyPlopResource(plop);
}
В C++, это должно быть обернуто в объект:
// C++
class PlopResource
{
public:
PlopResource()
{
mPlop=createMyPlopResource();
// handle exceptions and errors.
}
~PlopResource()
{
releaseMyPlopResource(mPlop);
}
private:
Plop* mPlop;
};
void myFunc()
{
PlopResource plop;
// Use the plop
// Exception safe release on exit.
}
Я уже упомянул его несколько раз, но книги Scott Meyers Эффективный C++ и Эффективный STL действительно стоят своего веса в золоте для помощи с C++.
Задумываются о нем, Steven Dewhurst , Глюки C++ являются также превосходным "от канавок" ресурс. Его объект при прокрутке Ваших собственных исключений и как они должны быть созданы действительно, помог мне в одном проекте.
Два глюка, что мне жаль, что я не учился на горьком опыте:
(1) Большой вывод (такой как printf) буферизуется по умолчанию. Если Вы отлаживаете отказывающий код, и Вы используете буферизованные операторы отладки, последний вывод, который Вы видите, май не действительно последний оператор печати, с которым встречаются в коде. Решение состоит в том, чтобы сбросить буфер после каждой печати отладки (или выключить буферизацию в целом).
(2) Быть осторожным с инициализациями - (a) избегают экземпляров класса как globals / помехи; и (b) пытаются инициализировать все Ваши членские переменные к некоторому безопасному значению в ctor, даже если это - тривиальное значение, такое как ПУСТОЙ УКАЗАТЕЛЬ для указателей.
Обоснование: упорядочивание инициализации глобального объекта не гарантируется (globals, включает статические переменные), таким образом, можно закончить с кодом, который, кажется, перестал работать недетерминировано, так как это зависит от объекта X инициализируемый перед объектом Y. Если Вы явно не инициализируете переменную типа примитива, такую как участник bool или перечисление класса, Вы закончите с различными значениями в удивительных ситуациях - снова, поведение может казаться очень недетерминированным.
Веб-страница Ловушки C++ Scott Wheeler покрывает некоторые основные ловушки C++.
Едва ли определенная подсказка, но общее руководство: проверьте свои источники. C++ является старым языком, и он изменился много за эти годы. Лучшие практики изменились с ним, но к сожалению существует все еще большая старая информация там. Были некоторые очень хорошие книжные рекомендации на здесь - я могу вторая покупка каждых из книг C++ Scott Meyers. Познакомьтесь с Повышением и со стилями кодирования, используемыми в Повышении - люди, связанные с тем проектом, находятся на передовом рубеже дизайна C++.
не перестраивают колесо. Познакомьтесь с STL и Повышением, и используйте их средства каждый раз, когда возможная прокрутка Ваше собственное. В частности, используйте строки STL и наборы, если Вы не имеете очень, очень серьезное основание не к. Узнайте auto_ptr и библиотеку интеллектуальных указателей Повышения очень хорошо, поймите, под которыми обстоятельствами каждый тип интеллектуального указателя предназначается, чтобы использоваться, и затем использовать интеллектуальные указатели везде, Вы, возможно, иначе использовали необработанные указатели. Ваш код будет так же эффективен и намного менее подвержен утечкам памяти.
Использование static_cast, dynamic_cast, const_cast, и reinterpret_cast вместо бросков C-стиля. В отличие от бросков C-стиля они сообщат, просите ли Вы действительно другой тип броска, чем Вы думать, что Вы просите. И они выделяются визуально, предупреждая читателя, что бросок происходит.
У Brian есть большой список: я добавлял бы "Всегда явных конструкторов отдельного аргумента метки (кроме тех редких случаев, Вы хотите автоматический кастинг)".
У некоторых должны быть книги C++, которые помогут Вам избежать общих ловушек C++:
Эффективный C++
Более эффективный C++
Эффективный STL
Эффективная книга STL объясняет вектор проблемы bools:)
, В первую очередь, необходимо посетить отмеченное наградой C++ FAQ . Это имеет много хороших ответов на ловушки. Если Вы имеете дальнейшие вопросы, посещаете ##c++
на irc.freenode.org
в [1 161] IRC. Мы рады помочь Вам, если мы можем. Обратите внимание, что все следующие ловушки первоначально записаны. Они только копируются со случайных источников.
delete[]
наnew
,delete
по телефонуnew[]
Решение : Выполнение вышеупомянутые урожаи к неопределенному поведению: Все могло произойти. Поймите свой код и что он делает, и всегда delete[]
, что Вы new[]
, и delete
, что Вы new
, тогда этого не произойдет.
Исключение :
typedef T type[N]; T * pT = new type; delete[] pT;
Вам нужно к [1 116] даже при том, что Вы new
, начиная с Вас new'ed массив. Таким образом, если Вы работаете с [1 118], проявляете специальную заботу.
Вызывание виртуальной функции в конструкторе или деструкторе
Решение : Вызывание виртуальной функции не вызовет переопределяющие функции в производных классах. При вызове чистая виртуальная функция в конструкторе или деструкторе является неопределенным поведением.
<час>Вызов
delete
илиdelete[]
на уже удаленном указателе
Решение : Присвойте 0 каждому указателю, который Вы удаляете. Вызов delete
или delete[]
на нулевом указателе ничего не делает.
Взятие sizeof указателя, когда число элементов 'массива' должно быть вычислено.
Решение : Передайте число элементов вместе с указателем, когда необходимо будет передать массив как указатель в функцию. Используйте функцию, предложенную здесь при взятии sizeof массива, который, как предполагается, является действительно массивом.
<час>Используя массив, как будто это был указатель. Таким образом, с помощью [1 123] для двух массивов dimentional.
Решение : См. здесь для того, почему они отличаются и как Вы обрабатываете их.
<час>Запись в строковый литерал:
char * c = "hello"; *c = 'B';
Решение : Выделите массив, который инициализируется от данных строкового литерала, тогда можно записать в него:
char c[] = "hello"; *c = 'B';
Запись в строковый литерал является неопределенным поведением. Так или иначе вышеупомянутое преобразование от строкового литерала до [1 125] удерживается от использования. Таким образом, компиляторы, вероятно, предупредят при увеличении уровня предупреждения.
<час>ресурсы Создания, затем забывая освобождать их, когда что-то бросает.
Решение : Используйте интеллектуальные указатели как [1 164] std::unique_ptr
или std::shared_ptr
, как указано другими ответами.
Изменение объекта дважды как в этом примере:
i = ++i;
Решение : Вышеупомянутое, как предполагалось, присваивало [1 129] значение [1 130]. Но то, что это делает, не определяется. Вместо того, чтобы увеличить i
и присвоить результат, это изменяется i
на правой стороне также. Изменение объекта между двумя точками последовательности является неопределенным поведением. Точки последовательности включают ||
, &&
, comma-operator
, semicolon
и entering a function
(не исчерпывающий список!). Измените код на следующее, чтобы заставить его вести себя правильно: i = i + 1;
Упущение сбросить потоки прежде, чем вызвать блокирующуюся функцию как [1 139].
Решение : Сбросьте поток путем потоковой передачи или std::endl
вместо [1 141] или путем вызова stream.flush();
.
Объявление функции вместо переменной.
Решение : проблема возникает, потому что компилятор интерпретирует, например
Type t(other_type(value));
как объявление функции функции t
возврат Type
и наличие параметра типа other_type
, который называют value
. Вы решаете его путем помещения круглых скобок вокруг первого аргумента. Теперь Вы получаете переменную t
из типа Type
:
Type t((other_type(value)));
<час> Вызывание функции свободного объекта, который только объявляется в текущей единице перевода (
.cpp
файл).
Решение : стандарт не определяет порядок создания свободных объектов (в объеме пространства имен) определенный через различные единицы перевода. Вызов функции членства на объекте, еще не созданном, является неопределенным поведением. Можно определить следующую функцию в единице перевода объекта вместо этого и назвать его от других:
House & getTheHouse() { static House h; return h; }
, Который создал бы объект по требованию и оставил бы Вас с полностью созданным объектом в то время, когда Вы вызываете функции на нем.
<час>Определение шаблона в
.cpp
файл, в то время как это используется в различном.cpp
файл.
Решение : Почти всегда Вы будете получать ошибки как [1 152]. Поместите все шаблонные определения в заголовок, так, чтобы, когда компилятор использует их, он мог уже произвести необходимый код.
<час>
static_cast<Derived*>(base);
, если основой является указатель на виртуальный базовый класс [1 154].
Решение : виртуальный базовый класс является основой, которая происходит только однажды, даже если он наследован несколько раз различными классами косвенно в дереве наследования. Выполнение вышеупомянутого не позволяется Стандартом. Используйте dynamic_cast, чтобы сделать это и удостовериться, что Ваш базовый класс является полиморфным.
<час>
dynamic_cast<Derived*>(ptr_to_base);
, если основа неполиморфная
Решение : стандарт не позволяет удрученный из указателя или ссылки, когда объект передал, не является полиморфным. Это или один из его базовых классов должно иметь виртуальную функцию.
<час>Создание Вашей функции принять
T const **
Решение : Вы могли бы думать, что это более безопасно, чем использование T **
, но на самом деле оно вызовет головную боль людям, которые хотят передать T**
: стандарт не позволяет его. Это дает аккуратный пример того, почему это запрещено:
int main() {
char const c = ’c’;
char* pc;
char const** pcc = &pc; //1: not allowed
*pcc = &c;
*pc = ’C’; //2: modifies a const object
}
Всегда принимают T const* const*;
вместо этого.
Другое (закрывшее) обсуждение ловушек о C++, таким образом, люди, ищущие их, найдут их, является вопросом о Переполнении стека ловушки C++ .
Я потратил много лет на C ++ развитие. Я написал краткое изложение проблем, с которыми я столкнулся много лет назад. Соответствующие стандартам компиляторы больше не являются проблемой, но я подозреваю, что другие описанные ловушки все еще действительны.