Подтвердить что ты не робот

Каковы некоторые идиомы, заблуждения и gotchas, связанные с С++, которые вы узнали из опыта?

Каковы некоторые идиомы, заблуждения и gotchas, связанные с С++, которые вы узнали из опыта?

Пример:

class A
{
  public: 
  char s[1024];
  char *p;

  A::A()
  {
    p = s;
  }

  void changeS() const
  {
    p[0] = 'a';
  }

};

Даже знаю, что changeS - это функция-член const, она меняет значение объекта. Таким образом, функция const member означает, что она будет обрабатывать все переменные как const, и это не означает, что она фактически сохранит все члены const. (почему? ключевое слово const в функции-члена рассматривает char * p; as char * const p; И не как const char * p;

Это значит, что p не может указывать на что-то другое. И не то, что вы не можете изменить p данных.

4b9b3361

Ответ 1

Мне понравилось это с тех пор, как я обнаружил его в некотором коде:

assert(condition || !"Something has gone wrong!");

или если у вас нет условия, вы можете просто сделать

assert(!"Something has gone wrong!");

Ниже описывается @Josh (см. комментарии). Вместо этого он использует оператор запятой:

assert(("Something has gone wrong!", condition)); 

Ответ 2

Вам не нужно знать синтаксис объявления синтаксиса сложной функции С++. Вот милый трюк, который я нашел.

Быстро, опишите это typedef:

typedef C &(__cdecl C::* const CB )(const C &) const;

Легко! CB является указателем на функцию-член класса C, принимающую ссылку на const для объекта C и возвращающую неконстантную ссылку на объект C. О, и его функция-член const. О, и сам указатель функции является const... (справа?)

Синтаксис спецификации объявления функции С++, как известно, тупое и трудно запоминающееся. Да, есть трюки, которые опытные ветераны С++ могут использовать, чтобы расшифровать такие ужасы, но это не то, о чем этот совет. Этот совет посвящен тому, как вам не нужно помнить этот ужасный синтаксис и по-прежнему иметь возможность объявлять такие функции typedefs (например, если вы взаимодействуете с каким-то устаревшим API, который никогда не слышал о boost:: function). Вместо того, чтобы сломать умственный пот, пусть компилятор сделает для вас работу. В следующий раз, когда вы пытаетесь создать typedef для функции-члена, которая выглядит так:

struct C {
        const C& Callback(const C&) const   { }
};

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

Например:

char c = &C::Callback;

Компилятор удачно использует это полезное сообщение об ошибке:

"… cannot convert from 'const C &(__cdecl C::* )(const C &) const' to 'char'"

Это то, что искали.:)

Ответ 3

Вот еще один, который я однажды поймал:

char int2hex(int x) {
     return "-0123456789abcdef"[(x >= 0 && x < 16) ? (x + 1) : 0];
}

он просто индексирует массив char вместо того, чтобы делать переключатель. Если он находится за пределами диапазона, он возвращает '-'.

Ответ 4

Никогда не тратьте время на попытки реализовать операции копирования на классы, когда мы не знаем, потребуется ли это позже. Многие объекты, которые мы обрабатываем, - это просто сущности, и их копирование вряд ли имеет смысл. Сделайте их не скопируемыми и позже скопируйте/дублируйте, если это действительно возникнет.

Ответ 5

Иногда заголовки загрязняются тем, что не ведут имена макросов, например

#define max(a, b) (a > b ? a : b)

Это сделает код недействительным, который использует так называемую функцию max или функцию. Печально известный пример windows.h, который делает именно это. Один из способов - это положить круглые скобки вокруг вызова, что останавливает его от использования макроса и заставляет его использовать реальную максимальную функцию:

void myfunction() {
    ....
    (max)(c, d);
}

Теперь max находится в круглых скобках, и он больше не считается вызовом макроса!

Ответ 6

Используется редко, но удобная идиома С++ - это использование оператора?: в цепочке конструктора.

class Sample
{  
    const char * ptr;
    const bool  freeable;

    Sample(const char * optional):
        ptr( optional ? optional : new char [32]),
        freeable( optional ? false : true ) {}
    ~Sample( )  { if (freeable) delete[] ptr; }
}  

С++ не позволяет изменять значения const внутри тела конструктора, поэтому это позволяет избежать const-casts.

Ответ 7

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

Ответ 8

Несколько вещей, которые обычно вызывают людей:

std::cout << a << a++ << --a;
i = ++i;

Вышеуказанные строки являются как undefined.

void foo(bar* b1, bar* b2);

int main() {
  foo(shared_ptr<bar>(new bar()), shared_ptr<bar>(new bar()));
}

Вышеуказанная информация может протекать из памяти.

int* arr = new int[10];
arr + 11;

Это приводит к поведению undefined.

Что касается идиом, мой любимый RAII. Выделяйте объекты в стеке, что гарантирует, что деструктор вызывается, когда объект выходит из области действия, предотвращая утечку ресурсов.

Ответ 9

Поскольку мы все игнорируем OP и вместо этого отправляем наши любимые интересные трюки...

Используйте boost (или tr1) shared_ptr для поддержания инварианта класса во время выполнения (вид очевидный, но я не видел, чтобы кто-то еще это делал):

#include <cassert>
#include <functional>
#include <stdexcept>
#include <boost/shared_ptr.hpp>
using namespace std;
using namespace boost;

class Foo
{
public:
    Foo() : even(0)
    {
        // Check on start up...
        Invariant();
    }

    void BrokenFunc()
    {
        // ...and on exit from public non-const member functions.
        // Any more is wasteful.
        shared_ptr<Foo> checker(this, mem_fun(&Foo::Invariant));

        even += 1;
        throw runtime_error("didn't expect this!");
        even += 1;
    }

private:
    void Invariant() { assert(even % 2 == 0); }
    int even;
};

Ответ 10

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

Ответ 11

Если у вас есть класс, который не имеет семантики значения, убедитесь, что все следующие конструкции явно объявлены для предотвращения головных болей по дороге.

  • Конструктор по умолчанию
  • Конструктор копирования
  • Оператор присваивания

Во многих случаях вам нужно только объявить подмножество этих конструкций. Однако в некоторых случаях это может оказаться очень сложным, что необходимо, а какие нет. Гораздо безопаснее объявить все 3 частных и сделать это.

Также очень полезно добавить комментарий к началу, объяснив, что это не безопасный для копирования класс.

Этот будет экономит ваше время по дороге.

Ответ 12

Я не могу сказать, что я опытный программист на С++, но недавно я узнал, как сложно передать массив и массивы в качестве параметра функции. Старайтесь избегать этого любой ценой: (

Если вы знаете, что размер при компиляции прост. Даже если вы знаете один из параметров во время компиляции. Если вы просто не знаете... вы можете смотреть на что-то вроде этого

m[i*dim2+j]

Будучи я итератором для строк, dim2 - числом cols и j - итератором для cols.

Ответ 13

Ничего, что я на самом деле натолкнулся во время программирования, но друг хотел объяснить, почему код работал. Мне потребовалось некоторое время, чтобы понять это. Возможно, это очевидно для вас, ребята, но я не опытный программист.

#include <iostream>
using namespace std;

int& strangeFunction(int& x){return x;}


int main(){
        int a=0;
        strangeFunction(a) = 5;               //<------- I found this very confusing
        cout << a <<endl;
        return 0;
}

Ответ 14

Не используйте shared_ptr, если это необходимо. Предпочитаете использовать ссылки на С++ и unique_ptr. shared_ptr является высокопроизводительным свиноматом и делает код более сложным, чем он есть. Обычно это не будет проблемой, за исключением необычной привлекательности shared_ptr и ее заразной природы.

Ответ 15

Не попадайте в ловушку использования std::noncopyable, если это необходимо. Да, это полезно во многих местах, и их следует использовать там.

Ловушка заключается в том, что вы начинаете писать clone() функцию вместе с тем, чтобы сделать ее неготовной, что реализует ту же функциональность. Вместо этого вы также можете использовать explicit (ссылка) для конструктора копирования, чтобы предотвратить случайное копирование (и сделать выделение приватным или удалить функцию в С++ 0x). clone() требуется для унаследованных базовых классов.

Ответ 16

Используйте boost::spirit::hold_any (ссылка) вместо boost::any для кода производительности (при хранении большого количества небольших объектов). Я видел большую разницу в их выступлениях.