Ответы пользователя по тегу C++
  • Как вызвать исключение "выход за границы массива"?

    @isotoxin
    Тут важно понимать, что C++ достаточно близок к железу и не контролирует ваши действия. Если вы обращаетесь к данным за границами массива, то делаете это на свой страх и риск. C++ вам это делать не запрещает. Но что произойдет, если вы так сделаете? Исходов два. Наиболее вероятный - вы просто прочитаете память за границами массива. Что там будет - неизвестно. Может быть просто мусор. Может быть часть другой переменной. Неизвестно. Т.е. дальнейшее поведение программы будет зависеть от того, как ваша программа будет работать с этим мусором. Если вы будете писать что-то за пределы массива, то либо попадете в неиспользованный участок памяти, либо испортите другие переменные. Эта ошибка называется memory corrupt и относится к самым злым ошибкам при работе с C/C++.
    Реже - вы получите исключение "memory access violation". Это будет происходит в тех случаях, когда выход за границы массива попадает также и за границы выделенных страниц памяти. Грубо говоря вы попадаете в адреса, где вообще нет памяти. Например windows отдает программе страницы памяти по 4k. За пределами этих страниц памяти как бы нет и любое обращение туда вызывает исключение доступа к памяти.
    Ну а теперь, что делать.
    Обычно чистые C-style массивы в C++ не используются именно по вышеперечисленным причинам. Небезопасно. Как правило, в C++ пишется обертка для массива (или используется готовая) и доступ осуществляется через специально написанные функции доступа, т.н. геттеры и сеттеры, которые и контролируют выход за пределы. И уже они могут бросать исключения или как-то иначе сообщать об ошибке.
    Ответ написан
    Комментировать
  • Управление памятью в С++?

    @isotoxin
    Высокоуровневые языки со сборщиками мусора, конечно, просты в использовании, но не дают понять сути происходящего. В этом их минус. А суть в работе с памятью такова:
    Любой объект ненулевой длины, любая переменная - всё требует место в памяти. Это в любом языке. Это фундамент мироздания.
    Вопрос только в том, как язык эту память выделяет или дает выделять программисту.
    Когда вы объявляете глобальную переменную, память под нее выделяется при старте программы. Это делается операционной системой в процессе загрузки программы. Управлять этим процессом можно, но новичку лучше пока голову не забивать. Далее. Переменная внутри функции. Память под такую переменную выделяется на стеке в момент вызова функции и освобождается по завершении функции. Тут тоже программист особо на этот процесс не влияет, хотя и может. Есть понимание, что такое стек? На всякий случай вкратце - это специальный участок памяти приложения (обычно 4 мегабайта на поток) как раз для переменных внутри функций. Т.о. если рекурсивная функция вошла в бесконечную рекурсию, она очень быстро выжирает всю память стека и приложение падает.

    Все что было до этого, характерно для большинства языков, в том числе и для Java.

    Наконец, самое сложное - переменные и данные, под которые программист явно запрашивает память.
    В java есть оператор new. В C++ он тоже есть. Они делают одно и тоже - выделяют где-то там (пока что не важно где) место под объект и инициализируют этот объект. А теперь о различии. Это основное отличие языка C++ от языков со сборщиком мусора. В C++ вы должны созданный таким образом объект явно удалить оператором delete. Если вы этого не сделаете, никто за вас это не сделает. Если этого не делать вообще, то приложение просто выжрет всю память и в итоге упадет. В Java вы просто оставляете объект на произвол судьбы и сборщик мусора сам удалит его, когда убедится, что объект больше нигде не используется.
    Вот такое управление памятью.
    Всякие malloc/free и т.п. оставлю без внимания, потому что это больше к C относится. Лучше пока их не трогать.
    Ответ написан
    Комментировать
  • Как объявить бесконечный двумерный массив типа string?

    @isotoxin
    Если массив разряженный, т.е. много незаполненных позиций, то неплохо подойдет имитация массива на основе map'а:
    std::map< std::pair<int, int>, std::string > arr;

    записать по произвольной позиции:
    arr[std::pair<int, int>(1, 3)] = "abc";

    прочитать:
    std::string s = arr[std::pair<int, int>(1, 3)];
    Ответ написан
  • Зачем нужны два раза двоеточия без класа?

    @isotoxin
    Обычно это нужно, если есть глобальная функция и функция член класса с тем же именем, и вам нужно вызвать именно глобальную.
    void foo() {
    }
    
    class bar
    {
        bar()  {
            foo(); // будет вызван член класса foo
            ::foo(); // будет вызвана глобальная foo
        }
        void foo()  {
        }
    };
    Ответ написан
    Комментировать