Каких ловушек C++ я должен избежать? [закрытый]

json ограничено в терминах объектов, которые он может печатать, а jsonpickle (возможно, вам нужен pip install jsonpickle) ограничен в терминах, он не может отступать от текста. Если вы хотите проверить содержимое объекта, класс которого вы не можете изменить, я все равно не мог найти более строгий путь:

 import json
 import jsonpickle
 ...
 print  json.dumps(json.loads(jsonpickle.encode(object)), indent=2)

Обратите внимание, что они все еще не могут распечатать объект методы.

73
задан 7 revs, 5 users 63% 22 January 2014 в 21:14
поделиться

28 ответов

Короткий список мог бы быть:

  • Избегают, чтобы утечки памяти посредством использования совместно использовали указатели для управления выделением памяти и очисткой
  • Использование , Приобретение Ресурса Является Инициализацией идиома (RAII) для управления очисткой ресурса - особенно в присутствии исключений
  • Старается не вызывать виртуальные функции в конструкторах
  • , Используют минималистские методы кодирования, где возможный - например, объявляя переменные только при необходимости, определяя объем переменных, и рано разрабатывают, если это возможно.
  • Действительно понимают обработку исключений в Вашем коде - оба относительно исключений, которые Вы выдаете, а также, брошенные классами, которые можно использовать косвенно. Это особенно важно в присутствии шаблонов.

RAII, совместно использованные указатели и минималистское кодирование, конечно, не характерны для C++, но они помогают избежать проблем, которые действительно часто неожиданно возникают при разработке на языке.

Некоторые превосходные книги по этому предмету:

  • Эффективный C++ - Scott Meyers
  • Более эффективный C++ - Scott Meyers
  • Стандарты Кодирования C++ - Sutter & часто задаваемые вопросы Alexandrescu
  • C++ - Градиент признаков

Чтение этих книг помогло мне больше, чем что-либо еще избежать вида ловушек, о которых Вы спрашиваете.

76
ответ дан 2 revs, 2 users 87% 24 November 2019 в 12:08
поделиться
#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);
}
-1
ответ дан gsarkis 24 November 2019 в 12:08
поделиться

Эссе/статья Указатели, ссылки и Значения очень полезно. Это говорит, стараются не избегать ловушек и хороших методов. Можно просмотреть целый сайт также, который содержит подсказки по программированию, главным образом для C++.

0
ответ дан 2 revs, 2 users 74% 24 November 2019 в 12:08
поделиться

Намерение (x == 10):

if (x = 10) {
    //Do something
}

я думал, что никогда не буду делать эту ошибку сам, но я на самом деле недавно сделал это.

0
ответ дан 2 revs, 2 users 71% 24 November 2019 в 12:08
поделиться

Всегда проверяйте указатель перед разыменованием его. В 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
}
0
ответ дан Mark Ransom 24 November 2019 в 12:08
поделиться
  • static_cast удрученный на виртуальном базовом классе

Едва ли... Теперь о моем неправильном представлении: Я думал, что A в следующем был виртуальный базовый класс, когда на самом деле это не; это, согласно 10.3.1, полиморфный класс . Используя static_cast здесь, кажется, прекрасен.

struct B { virtual ~B() {} };

struct D : B { };

, Таким образом, да, это - опасная ловушка.

1
ответ дан 3 revs, 2 users 75% 24 November 2019 в 12:08
поделиться
  • Blizpasta. Это - огромное, я вижу много...

  • Неинициализированные переменные являются огромной ошибкой, которую делают мои студенты. Много людей Java забывает, что просто высказывание "международного счетчика" не устанавливает в противоречии с 0. Так как необходимо определить переменные в h файле (и инициализировать их в конструкторе/установке объекта), легко забыть.

  • Ошибки диапазона на for циклы / доступ к массиву.

  • Не правильно убирающий объектный код, когда вуду запускает.

1
ответ дан 2 revs, 2 users 74%zach 24 November 2019 в 12:08
поделиться
1
ответ дан 2 revs, 2 users 57% 24 November 2019 в 12:08
поделиться

Чтобы испортить, используйте прямые указатели много. Вместо этого используйте RAII для почти чего-либо, удостоверяясь, конечно, что Вы используете правильные интеллектуальные указатели. Если Вы пишете, "удаляют" где угодно вне дескриптора или класса типа указателя, Вы очень вероятно делаете его неправильно.

1
ответ дан David Thornley 24 November 2019 в 12:08
поделиться

Сохраните пространства имен прямо (включая структуру, класс, пространство имен, и использующий). Это - мое разочарование номер один, когда программа просто не компилирует.

1
ответ дан David Thornley 24 November 2019 в 12:08
поделиться

Упущение определить виртуальный деструктор базового класса. Это означает, что вызов delete на Основе* не закончит тем, что разрушил полученную часть.

2
ответ дан 2 revs, 2 users 50% 24 November 2019 в 12:08
поделиться

Избегайте псевдо классы и квази классы ... Сверхдизайн в основном.

2
ответ дан 2 revs, 2 users 75% 24 November 2019 в 12:08
поделиться

Будьте осторожны при использовании интеллектуальных указателей и контейнерных классов.

2
ответ дан Registered User 24 November 2019 в 12:08
поделиться

PRQA имеют превосходный и бесплатный стандарт кодирования C++ на основе книг от Scott Meyers, Bjarne Stroustrop и Herb Sutter. Это объединяет всю эту информацию в одном документе.

3
ответ дан 2 revs, 2 users 67% 24 November 2019 в 12:08
поделиться

Выезд boost.org . Это обеспечивает большую дополнительную функциональность, особенно их реализации интеллектуального указателя.

3
ответ дан Paul Whitehurst 24 November 2019 в 12:08
поделиться

Самые важные ловушки для начала разработчиков должны избежать беспорядка между C и C++. C++ никогда нельзя рассматривать как простое лучше C или C с классами, потому что это сокращает его питание и может сделать его даже опасным (особенно при использовании памяти в качестве в C).

3
ответ дан Konrad Rudolph 24 November 2019 в 12:08
поделиться

Вот несколько ям, в которые у меня была неудача для падения. Все они имеют серьезные основания, которые я только понял, будучи укушенным поведением, которое удивило меня.

  • 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`
    
4
ответ дан 4 revs, 2 users 95% 24 November 2019 в 12:08
поделиться

Книжные Глюки C++ могут оказаться полезными.

4
ответ дан 2 revs, 2 users 67% 24 November 2019 в 12:08
поделиться

Используя 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.
}
6
ответ дан 2 revs, 2 users 92% 24 November 2019 в 12:08
поделиться

Я уже упомянул его несколько раз, но книги Scott Meyers Эффективный C++ и Эффективный STL действительно стоят своего веса в золоте для помощи с C++.

Задумываются о нем, Steven Dewhurst , Глюки C++ являются также превосходным "от канавок" ресурс. Его объект при прокрутке Ваших собственных исключений и как они должны быть созданы действительно, помог мне в одном проекте.

6
ответ дан 4 revs, 3 users 70% 24 November 2019 в 12:08
поделиться

Два глюка, что мне жаль, что я не учился на горьком опыте:

(1) Большой вывод (такой как printf) буферизуется по умолчанию. Если Вы отлаживаете отказывающий код, и Вы используете буферизованные операторы отладки, последний вывод, который Вы видите, май не действительно последний оператор печати, с которым встречаются в коде. Решение состоит в том, чтобы сбросить буфер после каждой печати отладки (или выключить буферизацию в целом).

(2) Быть осторожным с инициализациями - (a) избегают экземпляров класса как globals / помехи; и (b) пытаются инициализировать все Ваши членские переменные к некоторому безопасному значению в ctor, даже если это - тривиальное значение, такое как ПУСТОЙ УКАЗАТЕЛЬ для указателей.

Обоснование: упорядочивание инициализации глобального объекта не гарантируется (globals, включает статические переменные), таким образом, можно закончить с кодом, который, кажется, перестал работать недетерминировано, так как это зависит от объекта X инициализируемый перед объектом Y. Если Вы явно не инициализируете переменную типа примитива, такую как участник bool или перечисление класса, Вы закончите с различными значениями в удивительных ситуациях - снова, поведение может казаться очень недетерминированным.

6
ответ дан Tyler 24 November 2019 в 12:08
поделиться

Веб-страница Ловушки C++ Scott Wheeler покрывает некоторые основные ловушки C++.

8
ответ дан 2 revs, 2 users 75% 24 November 2019 в 12:08
поделиться

Едва ли определенная подсказка, но общее руководство: проверьте свои источники. C++ является старым языком, и он изменился много за эти годы. Лучшие практики изменились с ним, но к сожалению существует все еще большая старая информация там. Были некоторые очень хорошие книжные рекомендации на здесь - я могу вторая покупка каждых из книг C++ Scott Meyers. Познакомьтесь с Повышением и со стилями кодирования, используемыми в Повышении - люди, связанные с тем проектом, находятся на передовом рубеже дизайна C++.

не перестраивают колесо. Познакомьтесь с STL и Повышением, и используйте их средства каждый раз, когда возможная прокрутка Ваше собственное. В частности, используйте строки STL и наборы, если Вы не имеете очень, очень серьезное основание не к. Узнайте auto_ptr и библиотеку интеллектуальных указателей Повышения очень хорошо, поймите, под которыми обстоятельствами каждый тип интеллектуального указателя предназначается, чтобы использоваться, и затем использовать интеллектуальные указатели везде, Вы, возможно, иначе использовали необработанные указатели. Ваш код будет так же эффективен и намного менее подвержен утечкам памяти.

Использование static_cast, dynamic_cast, const_cast, и reinterpret_cast вместо бросков C-стиля. В отличие от бросков C-стиля они сообщат, просите ли Вы действительно другой тип броска, чем Вы думать, что Вы просите. И они выделяются визуально, предупреждая читателя, что бросок происходит.

8
ответ дан Avdi 24 November 2019 в 12:08
поделиться

У Brian есть большой список: я добавлял бы "Всегда явных конструкторов отдельного аргумента метки (кроме тех редких случаев, Вы хотите автоматический кастинг)".

12
ответ дан 0124816 24 November 2019 в 12:08
поделиться

У некоторых должны быть книги C++, которые помогут Вам избежать общих ловушек C++:

Эффективный C++
Более эффективный C++
Эффективный STL

Эффективная книга STL объясняет вектор проблемы bools:)

16
ответ дан 17 of 26 24 November 2019 в 12:08
поделиться

Ловушки в порядке убывания их важности

, В первую очередь, необходимо посетить отмеченное наградой 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;

<час>

Проблемы Misc

Упущение сбросить потоки прежде, чем вызвать блокирующуюся функцию как [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++ .

51
ответ дан 17 revs, 6 users 88% 24 November 2019 в 12:08
поделиться
  1. Не чтение FAQ C++, Облегченный . Это объясняет многих плохо (и хороший!) методы.
  2. Не использование Повышение . Вы сохраните себя много разочарования путем использования в своих интересах Повышения, если это возможно.
3
ответ дан 2 revs, 2 users 67% 24 November 2019 в 12:08
поделиться

Я потратил много лет на C ++ развитие. Я написал краткое изложение проблем, с которыми я столкнулся много лет назад. Соответствующие стандартам компиляторы больше не являются проблемой, но я подозреваю, что другие описанные ловушки все еще действительны.

0
ответ дан 3 revs, 2 users 78% 24 November 2019 в 12:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: