Пользователь
0,0
рейтинг
11 сентября 2009 в 05:00

Разработка → С++0x и решение проблем инициализации

C++*
C++0xВ статьях на хабре уже касались стандарта С++0x, а в частности лямбда-выражений. Я хотел написать еще про пару изменений и дополнений, которые внесут в С++. И без лишних слов сразу к делу.


Инициализация целочисленных переменных


С++0x решает проблему приведения элементраных вешественных типов к целочисленным. На простейшем примере это выглядит так:

Классический С++:

int a = 7.3;    //Приведение к int все равно сработает
void func(int);
func(5.6);      //И снова сработало приведение типов без нашего разрешения



В С++0x проблема решается с помощью введения новой конструкции для {}:

int x1 = {7.3};    //Ошибка приведения типов - и ошибка компиляции
double d = 7;
int x2 {d};      //И снова нельзя, произойдет ошибка
char x3{7};      //Все нормально
vector<int> vi = { 1, 2.3, 4, 5.6 };  //Не получится, так как нет соответствия



Делегирование конструкторов


В С++ необходимо иметь два конструктора для выполнения практически одинаковых действий или использовать дополнительную функцию.

Классический С++:

class X
{
  int a;
  validate(int x)
  {
    if (0<x && x<=max) a=x;
    else throw bad_X(x);
  }

public:
  X(int x)    //Конструктор с входным параметром типа int
  {
    validate(x);
  }

  X()        //Конструктор по-умолчанию
  {
    validate(42);
  }

  X(string s)    //Конструктор с входным параметром типа string
  {
    int x = lexical_cast<int>(s);
    validate(x);
  }
  // ...
};



В С++0x один конструктор можно объявить в контексте другого конструктора:

class X
{
  int a;

public:

  X(int x)    //Конструктор с входным параметром типа int
  {
    if (0<x && x<=max) a=x;
    else throw bad_X(x);
  }
  
  X() :X{42} { }    //Конструктор по-умолчанию
    
  X(string s) :X{lexical_cast<int>(s)} { }  //Конструктор с входным параметром типа string
  
  // ...
}



Нулевые указатели



В стандартном С++ нулевой указатель представляет из себя обычный 0. В библиотеках можно встретить #define NULL 0. В С++0x вводится специальный литерал nullptr.

Пример использования:

char* p = nullptr;
int* q = nullptr;
char* p2 = 0;    //Можно и по-старинке

void f(int);    //Функция с входным параметром int
void f(char*);    //Функция с входным параметром char*

f(0);        //вызов f(int) - все нормально
f(nullptr);      //вызов f(char*) - все нормально

void g(int);
g(nullptr);      //Облом: nullptr не является int
int i = nullptr;  //И снова облом



Заключиние


Изменения, конечно, немного косметические, но делают язык более безопасным и удобным, а главное — не выглядят так экстремально, как лямбда-выражения ).

Продолжение следует…

Progg it
Max K. @MaxFX
карма
40,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (38)

  • +1
    nullptr — это то, чего так долго всем не хватало :)

    P.S. В Boost не шарю пока что… Узнал вот только что про lexical_cast. Прикольная штука :)
    • +1
      и чем не хватало? лишних 7 символов печатать, а суть никак не изменилась
      • +8
        Во-первых, не семь. Во-вторых, более строгая проверка типов — раз; стандарт для обозначения нулевых указателей — два (а то пишут… кто 0, кто NULL, а кто-то вообще MY_OWN_SUPERMEGAMACROS_4_NULL_POINTERS).
        • –1
          Интересно, за что минус =)))
          • 0
            О, теперь всё норм :)

            Рейтинг ничто — правда всё!
        • 0
          А вы уверены, что NULL != nullptr? Если это не так, тогда все ваши выгоды выеденного яйца не стоят. Кроме того, чем вам 0 не нравится? Вполне ясно, что указатель неправильный, не вижу разницы между !ptr и ptr == nullptr, к тому же первое еще и явно нагляднее.
          • 0
            Тем что 0 имеет тип int, только и всего.
            • –2
              Значение указателя — это адрес, который так же имеет тип int, что не так?
              • 0
                И все же, указатель — это не просто int… Не всегда удобно интерпретировать его как простое целочисленное значение.
                • –5
                  Блин, а что же это еще, если не int? А как же его еще можно интерпретировать, если не int?
                  Даже учитывая комментарий ниже, всё равно это int, в любом случае.

                  P.S. «Не просто int» — это нечто.
                  • 0
                    К указателям удобно применять арифметику указателей, а не арифметику интов:
                    Если я прибавляю к указателю на начало массива еденицу, то я сдвигаю указатель на размер элемента массива, а не на еденицу.
                    • –5
                      Ну и что? Ну а что от этого меняется? Только лишь характер операций? От этого указатель не перестаёт быть int-ом.
                      Кроме того, возвращаясь к теме выше, даже учитывая специфику операций, чем вам мешает 0? От того, что я присвою 0 указателю нарушится механика операций? Или 0 не является адресом? Да всё равно тот же nullptr будет тем же 0-ём, какая разница?
                      • +1
                        Ну, так всё можно интами представлять о_О

                        Просто null и 0 это разные сущности: 0 — число, null — указатель. Вы ведь не храните инты как чары?
              • 0
                А в x64, а сегменты, а различные платформы,…
              • +3
                Это утверждение неверно. Размеры указателей и int не определены и в общем случае между собой не связаны.
                Словом, это два совершенно разных типа.
              • +1
                Логически NULL — это не адрес, а его отсутствие :) Указатель указывает в никуда (или, точнее, никуда не указывает), а не на нулевой байт. Просто так сложилось что обращаться по нулевому адресу нельзя нельзя, вот NULL и равен 0, а вот если бы мы могли использовать все пространство памяти…
      • +2
        изменилось, теперь не будет ошибок с присвоением NULL'а обычной переменной, nullptr можно будет присовить только указателю.
    • +2
      Буст вообще вся прикольная штука. Но только для шарящих, а то такого можно наверетенить.
  • +2
    А почему не null?
    • +2
      Надо же выделиться как-то. После их синтаксиса лямбд простой null смотрелся бы слишком банально.

      Хотя, и это не получилось — nullptr уже давно есть в C++/CLI
      • +1
        null уже использовался некоторыми библиотеками. Для совместимости со старым кодом взяли более редкое слово.
  • 0
    насколько я понял из текущего доступного драфта, это так и не починили:
    struct A{
      int a[3];
      A(): a({1,2,3}){};
    };
    <pre>
    • –1
      мда, видимо народ ничего кроме nullptr использовать не собирается, и зачем им с++, пешите на чистом си :)
      • 0
        Это Вы так решили из-за того, что Ваш коммент обделили вниманием? :)
        • 0
          не, изза того что никого не интересует проблема инициализации массивов в конструкторе :)
          • 0
            Вообще, возможно, было бы удобно делать так, как Вы написали. А что, по новому стандарту нельзя, да?
            • 0
              по старому стандарту фигурные скобки в списке инициализации конструктора недопустимы, вот по новому непонятно…
  • +1
    Спасибо, большое! Жду когда же допилят gcc, чтобы использовать на полную мощь.
  • 0
    Отлично, автор пиши больше про новый стандарт. За основу можешь взять эту ветку: forum.sources.ru/index.php?showtopic=190375
    Там много букв, так что самая выжимка не помешает.
  • +2
    Полезные изменения, хотя новые действия с фигурными скобками запутывают.
    • +2
      Да, количество скобок C+0x зашкаливает :) Со временем он, наверное, эволюционирует в в LISP. :)
      • 0
        К этому времени Бьёрн будет стареньким-стареньким :))
  • 0
    int x1 = {7.3};    //Ошибка приведения типов - и ошибка компиляции
    double d = 7;
    int x2 {d};      //И снова нельзя, произойдет ошибка
    char x3{7};      //Все нормально
    vector<int> vi = { 1, 2.3, 4, 5.6 };  //Не получится, так как нет соответствия
    

    Разьясните, а зачем нужно так писать? Чтобы специально получить ошибку компиляции? А если не хочешь получить, писать по старому? :-)
    Странное какое-то решение… Если бы такая более строгая типизация вводилась принудительно в весь язык, то было бы понятно, а так ведь все просто будут писать по-старому (хотя бы для совместимости).
    Если я всё верно понял.
    • 0
      поддерживаю, надеюсь разработчики компиляторов сделают опцию что по умолчанию будет считаться что со скобками написано.
    • 0
      Как раз, скорее, старый вариант оставили для совместимости, а писать начнут по-новому… Ну, естественно, не в тех проектах, которые по 20 лет уже пишутся.
    • 0
      К примеру использовать фигурные скобки можно при создании собственной библиотеки шаблонов, а при последующем использовании этих шаблонов, часто встречаются ситуации, когда тип подставляется на этапе компиляции. Так, чтобы не произошло необоснованного преобразования типов — такое как раз и подойдёт.

      П.С. Ошибка замеченная на этапе компиляции — это в некотором смысле хорошая ошибка :)
  • 0
    В C++ когда-нибудь модули будут? Или так и дальше всё инклудами?
    • 0
      Что вы называете модулем?
      Это еденица уровня инклуда, или уровня статической/динамической библиотеки, или нечто среднее?

      ЗЫ: не хочу обидеть, но мне кажется вы не так давно мигрировали с паскаля или делфи на с++, выучили синтаксис недоразобравшись в философии языка и теперь его критикуете :)

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