На мой взгляд, опасность C ++ несколько преувеличена.
Существенная опасность заключается в следующем: хотя C # позволяет вам выполнять «небезопасные» операции с указателями с помощью unsafe
ключевого слова, C ++ (в основном расширенный набор C) позволяет вам использовать указатели всякий раз, когда вам это нравится. Помимо обычных опасностей, связанных с использованием указателей (которые одинаковы с C), таких как утечки памяти, переполнения буфера, висячие указатели и т. Д., C ++ представляет новые способы для вас, чтобы серьезно испортить вещи.
Эта, так сказать, «лишняя веревка», о которой говорил Джоэл Спольски , сводится к одному: написание классов, которые внутренне управляют своей собственной памятью, также известной как « правило 3 » (которое теперь можно назвать правилом 4 или Правило 5 в C ++ 11). Это означает, что если вы когда-нибудь захотите написать класс, который управляет своими собственными распределениями памяти внутри, вы должны знать, что вы делаете, иначе ваша программа, скорее всего, потерпит крах. Вы должны тщательно создать конструктор, конструктор копирования, деструктор и оператор присваивания, что на удивление легко ошибиться, что часто приводит к причудливым сбоям во время выполнения.
ОДНАКО в реальном повседневном программировании на C ++ действительно очень редко пишут класс, который управляет собственной памятью, поэтому вводить в заблуждение то, что программисты на C ++ всегда должны быть «осторожными», чтобы избежать этих ловушек. Обычно вы будете делать что-то более похожее на:
class Foo
{
public:
Foo(const std::string& s)
: m_first_name(s)
{ }
private:
std::string m_first_name;
};
Этот класс выглядит очень похоже на то, что вы делаете в Java или C # - он не требует явного управления памятью (потому что библиотечный класс std::string
позаботится обо всем этом автоматически), и никаких «Правил 3» вообще не требуется, так как по умолчанию Конструктор копирования и оператор присваивания в порядке.
Это только когда вы пытаетесь сделать что-то вроде:
class Foo
{
public:
Foo(const char* s)
{
std::size_t len = std::strlen(s);
m_name = new char[len + 1];
std::strcpy(m_name, s);
}
Foo(const Foo& f); // must implement proper copy constructor
Foo& operator = (const Foo& f); // must implement proper assignment operator
~Foo(); // must free resource in destructor
private:
char* m_name;
};
В этом случае новичкам может быть сложно получить правильное назначение, деструктор и конструктор копирования. Но в большинстве случаев нет причин когда-либо делать это. C ++ позволяет очень легко избежать ручного управления памятью в 99% случаев с помощью библиотечных классов, таких как std::string
и std::vector
.
Другая связанная с этим проблема заключается в ручном управлении памятью таким образом, чтобы не учитывать возможность возникновения исключения. Подобно:
char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;
Если на some_function_which_may_throw()
самом деле это сгенерирует исключение, вы остаетесь с утечкой памяти , потому что память , выделенная для s
никогда не будет утилизирована. Но опять же, на практике это вряд ли проблема больше по той же причине, по которой «правило 3» больше не является проблемой. Очень редко (и обычно не нужно) фактически управлять своей собственной памятью с помощью сырых указателей. Чтобы избежать вышеуказанной проблемы, все, что вам нужно сделать, это использовать std::string
или std::vector
, и деструктор будет автоматически вызываться при разматывании стека после возникновения исключения.
Итак, общая тема здесь заключается в том, что многие функции C ++, которые не были унаследованы от C, такие как автоматическая инициализация / уничтожение, конструкторы копирования и исключения, вынуждают программиста быть особенно осторожным при ручном управлении памятью в C ++. Но опять же, это проблема только в том случае, если вы собираетесь в первую очередь выполнять ручное управление памятью, что вряд ли когда-либо понадобится, когда у вас есть стандартные контейнеры и умные указатели.
Так что, на мой взгляд, хотя C ++ дает вам много лишней веревки, вряд ли когда-либо понадобится использовать ее, чтобы повеситься, и ловушки, о которых говорил Джоэл, тривиально легко избежать в современном C ++.
Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.
. Я считаю, что это квалифицируется как такой вопрос ...