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

С++ для разработчика С#

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

  • Ссылки на другие библиотеки
  • Предоставление моим библиотекам других функций
  • Обработка строк
  • Преобразования типов данных
  • Хорошая структура проекта
  • Структуры данных для использования (т.е. в С#, я много использую List<T>, что я использую на С++, который работает одинаково?)

Похоже, что в зависимости от используемой среды IDE, рекомендации разные, поэтому я действительно искал что-то, что может быть немного более универсальным. Или, в худшем случае, сосредоточиться на использовании компилятора /IDE. Кроме того, чтобы быть ясным, я не ищу ничего об общих методах программирования (Design Patterns, Code Complete и т.д.), Поскольку я чувствую, что я довольно хорошо разбираюсь в этих темах.

4b9b3361

Ответ 1

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

Каждое приобретение ресурсов (будь то блокировка синхронизации, соединение с базой данных или кусок памяти или что-либо еще, что необходимо получить и освободить) должна быть обернута в объект, чтобы конструктор выполнял сбор, а деструктор освобождает ресурс. Этот метод известен как RAII, и в основном это способ избежать утечек памяти. Привыкай к этому. Стандартная библиотека С++, очевидно, использует это широко, поэтому вы можете понять, как она работает там. Прыгая немного в ваши вопросы, эквивалент List<T> равен std::vector<T>, и он использует RAII для управления своей памятью. Вы бы использовали его примерно так:

void foo() {

  // declare a vector *without* using new. We want it allocated on the stack, not
  // the heap. The vector can allocate data on the heap if and when it feels like
  // it internally. We just don't need to see it in our user code
  std::vector<int> v;
  v.push_back(4);
  v.push_back(42); // Add a few numbers to it

  // And that is all. When we leave the scope of this function, the destructors 
  // of all local variables, in this case our vector, are called - regardless of
  // *how* we leave the function. Even if an exception is thrown, v still goes 
  // out of scope, so its destructor is called, and it cleans up nicely. That 
  // also why C++ doesn't have a finally clause for exception handling, but only 
  // try/catch. Anything that would otherwise go in the finally clause can be put
  // in the destructor of a local object.
} 

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

Обработка строк:

std::string твой друг. В C вы должны использовать массивы char (или char указателей), но это противно, потому что они не ведут себя как строки. В С++ у вас есть класс std::string, который ведет себя так, как вы ожидали. Единственное, что нужно иметь в виду, это то, что "мир привет" имеет тип char [12] и NOT std::string. (для совместимости с C), поэтому иногда вам нужно явно преобразовать строковый литерал (что-то заключенное в кавычки, например "hello world" ) в std::string, чтобы получить нужное поведение: Вы все еще можете написать

std::string s = "hello world";

потому что строки C-стиля (такие как литералы, такие как "hello world" ) неявно конвертируются в std::string, но это не всегда работает: "hello" + "world" не будет компилироваться, потому что оператор + не определен для двух указателей. "hello worl" + 'd', однако, скомпилируется, но он не будет делать ничего разумного. Вместо добавления char в строку, он примет интегральное значение char (которое получает повышение до int) и добавляет его к значению указателя.

std::string ( "hello worl" ) + "d" делает так, как вы ожидали, потому что левая сторона уже есть std::string, а оператор добавления перегружен для std::string, чтобы сделать то, что вы 'd ожидать, даже если правая сторона char * или один символ.

Последняя заметка о строках: std::string использует char, который является однобайтовым типом данных. То есть, он не подходит для текста в Юникоде. С++ предоставляет широкий тип символов wchar_t, который является 2 или 4 байтами, в зависимости от платформы, и обычно используется для текста в Юникоде (хотя ни в одном случае стандарт С++ не указывает набор символов). И строка wchar_t называется std:: wstring.

Библиотеки:

Они не существуют, в основном. Язык С++ не имеет понятия о библиотеках, и к этому привыкает. Он позволяет # включать другой файл (обычно файл заголовка с расширением .h или .hpp), но это просто текстовая копия/вставка. Препроцессор просто объединяет два файла, что приводит к тому, что называется единицей перевода. Несколько исходных файлов, как правило, содержат одни и те же заголовки и работают только при определенных обстоятельствах, поэтому этот бит является ключом к пониманию модели компиляции С++, которая, как известно, является изворотливой. Вместо того, чтобы компилировать кучу отдельных модулей и вызывать какие-то метаданные между ними, как компилятор С#, каждая единица перевода компилируется изолированно, а результирующие объектные файлы передаются в компоновщик, который затем пытается объединить общие биты (если в нескольких единицах перевода включен один и тот же заголовок, вы по существу дублируете код в единицах перевода, поэтому компоновщик объединяет их обратно в одно определение);)

Конечно, для написания библиотек существуют специфичные для платформы способы. В Windows вы можете сделать .dll или .libs, с той разницей, что .lib связан с вашим приложением, а .dll - это отдельный файл, который вам нужно связать с вашим приложением, как и в .NET. В Linux эквивалентные типы файлов -.so и .a, и во всех случаях вам также необходимо предоставить соответствующие файлы заголовков, чтобы люди могли разрабатывать их против ваших библиотек.

Преобразования типов данных:

Я не уверен точно, что вы ищете там, но один момент, который я чувствую, имеет большое значение, так это то, что "традиционная" трансляция, как в следующем, плохо:

int i = (int)42.0f; 

Для этого есть несколько причин. Во-первых, он пытается выполнить несколько различных типов приведения в порядок, и вы можете быть удивлены тем, какой компилятор заканчивается. Во-вторых, его трудно найти в поиске, а в-третьих, это не уродливо. Лучше всего избегать броска, а на С++ они немного уродливы, чтобы напомнить вам об этом.;)

// The most common cast, when the types are known at compile-time. That is, if 
// inheritance isn't involved, this is generally the one to use
static_cast<U>(T); 

// The equivalent for polymorphic types. Does the same as above, but performs a 
// runtime typecheck to ensure that the cast is actually valid
dynamic_cast<U>(T); 

// Is mainly used for converting pointer types. Basically, it says "don't perform
// an actual conversion of the data (like from 42.0f to 42), but simply take the
// same bit pattern and reinterpret it as if it had been something else). It is
// usually not portable, and in fact, guarantees less than I just said.
reinterpret_cast<U>(T); 

// For adding or removing const-ness. You can't call a non-const member function
// of a const object, but with a const-cast you can remove the const-ness from 
// the object. Generally a bad idea, but can be necessary.
const_cast<U>(T);

Как вы заметите, эти приведения гораздо более конкретны, а это значит, что компилятор может дать вам ошибку, если приведение недействительно (в отличие от традиционного синтаксиса, где он просто попытается выполнить любой из вышеперечисленных бросков, пока не найдет тот, который работает), и он большой и многословный, позволяющий вам искать его и напоминает вам, что их следует избегать, когда это возможно.;)

Стандартная библиотека:

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

Стандартная библиотека состоит из нескольких довольно четких строительных блоков (библиотека со временем накапливается. Части ее были перенесены с C. Библиотека потоков ввода-вывода принимается из одного места, а классы-контейнеры и связанные с ними функциональность принимается из совершенно другой библиотеки и отличается значительно различной: последние являются частью того, что часто называют STL (стандартная библиотека шаблонов). Строго говоря, это имя библиотеки, которая слегка изменена, принятой в стандартную библиотеку С++.

STL является ключом к пониманию "современного С++". Он состоит из трех столбов, контейнеров, итераторов и алгоритмов. Вкратце, контейнеры выставляют итераторы, а алгоритмы работают над парами итераторов.

Следующий пример принимает вектор int, добавляет 1 к каждому элементу и копирует его в связанный список, только для примера:

int add1(int i) { return i+1; } // The function we wish to apply

void foo() {
  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  v.push_back(5); // Add the numbers 1-5 to the vector

  std::list<int> l;

  // Transform is an algorithm which applies some transformation to every element
  // in an iterator range, and stores the output to a separate iterator
  std::transform ( 
  v.begin(),
  v.end(), // Get an iterator range spanning the entire vector
  // Create a special iterator which, when you move it forward, adds a new 
  // element to the container it points to. The output will be assigned to this
  std::back_inserter(l) 
  add1); // And finally, the function we wish to apply to each element
}

Вышеприведенный стиль немного привыкает, но он чрезвычайно мощный и лаконичный. Поскольку функция преобразования является шаблоном, она может принимать любые типы в качестве входных данных, если они ведут себя как итераторы. Это означает, что функция может использоваться для объединения любых типов контейнеров или даже потоков или чего-либо еще, что может быть выполнено итерацией до тех пор, пока итератор предназначен для совместимости с STL. Нам также не нужно использовать пару begin/end. Вместо конечного итератора мы могли бы пройти один, указывая на третий элемент, и тогда алгоритм остановился бы там. Или мы могли бы написать пользовательские итераторы, которые пропускали все остальные элементы или что-то еще, что нам нравилось. Вышеприведенный пример является основным примером каждого из трех столпов. Мы используем контейнер для хранения наших данных, но алгоритм, который мы используем для его обработки, фактически не должен знать о контейнере. Он просто должен знать о диапазоне итераторов, на котором он должен работать. И, конечно, каждый из этих трех столпов можно расширить, написав новые классы, которые затем будут работать плавно вместе с остальной частью STL.

В некотором смысле это очень похоже на LINQ, поэтому, поскольку вы пришли из .NET, вы, вероятно, можете увидеть некоторые аналоги. STL-аналог немного более гибкий, хотя ценой немного более странного синтаксиса.:) (Как уже упоминалось в комментариях, он также более эффективен. В целом, для алгоритмов STL есть нулевые накладные расходы, они могут быть столь же эффективными, как и ручные кодированные контуры. Это часто бывает неожиданным, но возможно, потому что все соответствующие типы известны в compile-time (что является требованием для работы шаблонов), а компиляторы С++ имеют тенденцию к активному встраиванию.)

Ответ 2

У вас есть набор инструментов. Например, существуют STL (стандартная библиотека шаблонов) и Boost/TR1 (расширения для STL), которые считаются отраслевыми стандартами (ну, по крайней мере, STL). Они предоставляют списки, карты, наборы, общие указатели, строки, потоки и всевозможные другие полезные инструменты. Лучше всего, они широко поддерживаются в компиляторах.

Что касается преобразований данных, вы можете делать броски или создавать явные функции преобразования.

Библиотеки. Вы можете создавать статические библиотеки (впитываться в окончательный исполняемый файл) или библиотеки DLL (вы уже знакомы с ними). MSDN - отличный ресурс для DLL. Статические библиотеки зависят от вашей среды сборки.

В общем, это мой совет:  - Очень хорошо узнайте свою IDE выбора  - Приобретайте "С++ Complete Reference" Герберта Шильдта, который я считаю отличным томом на всех С++ (включая STL).

Учитывая ваш фон, вы должны быть хорошо настроены, как только вы сделаете это.

Ответ 3

Я не буду повторять то, что другие говорили о библиотеках и т.д., но если вы серьезно относитесь к С++, сделайте себе одолжение и заберите Bjarne Stroustrup "Язык программирования С++".

Мне потребовались годы работы на С++, чтобы, наконец, взять копию, и однажды я провел день, хлопнув своим лбом, говоря: "Конечно, я должен был это понять!".

(По иронии судьбы, я имел ТОЧНО тот же опыт с K & R "Язык программирования C." Когда-нибудь я научусь просто пойти "Книга" в первый день.)

Ответ 5

Реферирование и использование других библиотек, если вы включаете источник, выполняется просто путем # включения файлов заголовков для библиотеки в любой файл .cpp, в котором они вам нужны (и затем скомпилировать источник для библиотеки вместе с вашим проект). Однако большую часть времени вы, вероятно, будете использовать .lib(статическую библиотеку) или DLL (динамическую библиотеку). Большинство (все?) DLL поставляются с .lib файлом, поэтому процедура для обоих типов одинакова: включите соответствующие файлы заголовков там, где они вам нужны, а затем добавьте связанный .lib файл во время этапа компоновки (в visual studio, я думаю, вы можете просто добавить файл в проект).

Прошло много времени с тех пор, как я создал свои собственные библиотеки для других, поэтому я позволю кому-то еще ответить на эту часть. Или я вернусь и отредактирую этот ответ завтра, так как мне придется создавать .lib для работы завтра:)

Строковый материал обычно выполняется с помощью std::string. В особых случаях вы также можете использовать старую функцию sprintf() в стиле C, но это обычно обескураживает.

Что касается структур данных, которые вы ищете, ознакомьтесь с STL (стандартная библиотека шаблонов). Он включает в себя список, вектор, карту, строку и т.д., Которые должны быть вам знакомы. Я не уверен, что вы подразумеваете под преобразованиями типов... Я предполагаю, что вы знаете о кастинге, поэтому вы должны понимать нечто более сложное, чем это, и в этом случае оно, вероятно, относится к типам, которые вы пытаетесь преобразовать. Возможно, кто-то еще может предложить дополнительную информацию.

Ответ 6

В ответ на "Реферирование/использование других библиотек"

Информация о явной загрузке DLL в C/С++ для обоих окон и linux включает...

Окна:

Учебник по DLL для Windows

Функции: LoadLibrary, GetProcAddress, FreeLibrary

Linux:

Функции: dlopen, dlsym, dlerror, dlclose

Linux DLL Tutorial