Несколько раз пытался освоить C++, и всегда забрасывал это дело — сложный, совершенно непонятный язык.
Вот, допустим, совершенно тривиальный код обхода списка целых чисел на чистом Си с указателями:
main.c
#include <stdio.h>
#include <stdlib.h>
struct INTEGER_LIST {
int number;
struct INTEGER_LIST *next;
};
void print_integer_list(struct INTEGER_LIST *list) {
struct INTEGER_LIST *current;
for (current = list; current; current = current->next) {
printf(current == list ? "%d" : ", %d", current->number);
}
putchar('\n');
}
void free_integer_list(struct INTEGER_LIST *list) {
struct INTEGER_LIST *next;
while (list) {
next = list->next;
free(list);
list = next;
}
}
struct INTEGER_LIST *new_integer_list(int min, int max) {
int i;
struct INTEGER_LIST *list, *next;
if (min >= max) return NULL;
list = NULL;
for (i = max; i >= min; i--) {
next = list;
list = (struct INTEGER_LIST*)malloc(sizeof(struct INTEGER_LIST));
if (!list) {
free_integer_list(next);
return NULL;
}
list->number = i;
list->next = next;
}
return list;
}
int main(int argc, char *argv[]) {
struct INTEGER_LIST *numbers;
numbers = new_integer_list(1, 3);
if (!numbers) return 1;
print_integer_list(numbers);
free_integer_list(numbers);
return 0;
}
Можно переписать его в стиле "Си с классами", здесь пока тоже все очевидно, просто теперь new/delete вместо malloc/free и class вместо struct:
main.cpp
#include <iostream>
class List {
private:
List *next;
public:
List *getNext() {
return this->next;
}
List(List *next) {
this->next = next;
}
};
class IntegerList: public List {
private:
int number;
public:
int getNumber() {
return this->number;
}
IntegerList(int number, List *next): List(next) {
this->number = number;
}
};
void printIntegerList(List *list) {
for (List *current = list; current; current = current->getNext()) {
if (current != list) {
std::cout << ',' << ' ';
}
std::cout << ((IntegerList*)current)->getNumber();
}
std::cout << std::endl;
}
void printIntegerList(List &list) {
printIntegerList(&list);
}
int main(int argc, char *argv[]) {
List *numbers = new IntegerList(1, new IntegerList(2, new IntegerList(3, NULL)));
printIntegerList(numbers);
while (numbers) {
List *next = numbers->getNext();
delete numbers;
numbers = next;
}
return 0;
}
Примерно так я представлял себе C++ в школе и универе, не зная ни про ссылки, ни про операторы, ни про шаблоны и имея смутное представление о множественном наследовании.
Т.к. Pure C, Turbo Pascal и Delphi (и чуть позже Java с C#) решали все необходимые задачи, изучать C++ тогда не было никакой необходимости.
По правилам хорошего тона в C++ нужно всегда использовать ссылки вместо указателей (кроме интеграции с кодом на Си) и по возможности не управлять памятью вручную, т.к. это чревато утечками.
Я попытался переписать код, и вышло примерно следующее (для упрощения пока без шаблонов):
Помимо того, что этот талмуд совершенно непонятен, оно еще и не работает.
Рекурсивный обход списка в recursivePrintIntegerList вроде выводит все как положено, но в foreachPrintIntegerList цикл почему-то выводит одни единицы и не хочет переходить на следующий элемент.
Что нужно исправить? И что можно почитать по теме?
Из учебника Столярова так и не понял, что такое есть ссылка в C++ (вроде синтаксический сахар над указателем, но как-то странно и непонятно работает).
И еще, почему, если в EmptyList сделать пустой конструктор, то на строки const EmptyList empty(); const IntegerList n3(3, empty); компилятор ругается? Вариант с const EmptyList empty(void) не помог.
Как запру я тебя за железный замок, за дубовую дверь окованную,
Чтоб свету божьего ты не видела, мое имя честное не порочила…
М. Лермонтов. Песня про царя Ивана Васильевича, молодого опричника и удалого купца Калашникова
Здравствуйте, Worminator X, Вы писали:
WX>Из учебника Столярова так и не понял, что такое есть ссылка в C++ (вроде синтаксический сахар над указателем, но как-то странно и непонятно работает).
Ссылки — это не совсемсинтаксический сахар над указателями. По крайней мере не в смысле определения, хотя их и можно использовать как таковые. Ссылка — это альтернативное имя для какого-то выражения, алиас.
Саму ссылку нельзя изменить, она привязывается (bind) к чему-то в момент определения и остается привязанной к этому выражению до конца своего существования.
Грубо говоря, ссылка ведет себя как разыменованный константный указатель. Не указатель на константу, а именно константный указатель. А, например, константная ссылка как константный указатель на константу (const * const).
// ссылкаint a[5][5];
int& b = a[1][1];
b = 10; // эквивалентно a[1][1] = 10
b = a[0][0]; // эквивалентно a[1][1] = a[0][0], перепривязки здесь нет
b = 12; // снова эквивалентно a[1][1] = 12
// аналогично на указателяхint a[5][5];
int* const b = &a[1][1];
*b = 10; // эквивалентно a[1][1] = 10
*b = a[0][0]; // эквивалентно a[1][1] = a[0][0], перепривязки здесь нет
*b = 12; // снова эквивалентно a[1][1] = 12
// b = &a[0][0]; // нельзя, сам указатель b константный, его не перепривязать, как и ссылку
// константная ссылкаint a[5][5];
const int& b = a[1][1];
a[0][0] = b; // эквивалентно a[0][0] = a[1][1]
// b = ...; // нельзя
// аналогично на указателяхint a[5][5];
int const* const b = &a[1][1];
a[0][0] = *b; // эквивалентно a[0][0] = a[1][1]
// *b = ...; // нельзя
// b = ...; // нельзя
В твоем коде
for (
List ¤t = (List&)list;
current.getType() == LT_INTEGER_LIST;
current = ((IntegerList&)current).getNext()
)
создается ссылка current, которая привязывается к list. Она и останется привязанной к list.
Выражение current = ((IntegerList&)current).getNext() не перепривязывает ссылку current к другому объекту, а присваивает привязанному объекту list значение из list.getNext().
Если на указателях, то это эквивалентно следующему:
for (
List* const current = &(List&)list; // сам указатель константный, его нельзя поменять, как и привязку ссылки
current->getType() == LT_INTEGER_LIST;
*current = *((IntegerList*)current)->getNext() // считаем, что getNext() тоже указатель возвращает, как во втором варианте
)
На ссылках такой цикл написать в принципе невозможно.
WX>По правилам хорошего тона в C++ нужно всегда использовать ссылки вместо указателей (кроме интеграции с кодом на Си) и по возможности не управлять памятью вручную, т.к. это чревато утечками.
Нет такого правила. Всему свое применение.
WX>Я попытался переписать код, и вышло примерно следующее (для упрощения пока без шаблонов):
Как раз здесь ссылки неуместны, если ты конечно не строишь иммутабельные списки. Если потребуется список поменять, его придется перестроить весь, от начала и до конца.
При этом, с динамической памятью на ссылках работать не получится.
Что касается управления памятью вручную, то да. Вместо обычных указателей здесь лучше использовать умный std::unique_ptr.
WX>И еще, почему, если в EmptyList сделать пустой конструктор, то на строки const EmptyList empty(); const IntegerList n3(3, empty); компилятор ругается? Вариант с const EmptyList empty(void) не помог.
const EmptyList empty(); — это объявление функции empty(), возвращающей const EmptyList. Смотри https://en.wikipedia.org/wiki/Most_vexing_parse
Исправить можно инициализатором фигурными скобками const EmptyList empty{};. Или вообще их отсутствием (все равно будет вызван конструктор по умолчанию).
WX>Несколько раз пытался освоить C++, и всегда забрасывал это дело — сложный, совершенно непонятный язык.
учебник С++
плюс курсы от константина владимирова на ютубе
там у него кажется давече был курс для самых маленьких
ну или на крайняк записывайтесь в клуб Шмыг
там продвигают методику изучения С++ методом задрачивания на форуме(ах)
но судя по всему вы уже там состоите
Здравствуйте, Worminator X, Вы писали: WX>Несколько раз пытался освоить C++, и всегда забрасывал это дело — сложный, совершенно непонятный язык. WX>Вот, допустим, совершенно тривиальный код обхода списка целых чисел на чистом Си с указателями: WX>По правилам хорошего тона в C++ нужно всегда использовать ссылки вместо указателей (кроме интеграции с кодом на Си) и по возможности не управлять памятью вручную, т.к. это чревато утечками. WX>Я попытался переписать код, и вышло примерно следующее (для упрощения пока без шаблонов): WX>Помимо того, что этот талмуд совершенно непонятен, оно еще и не работает.
Я бы начал с того, что нет в С++ такого правила, которое запрещало бы использование сырых указателей. Плохо, когда владение объектом осуществляется через сырой указатель — это действительно чревато и утечками памяти, и риском влететь на неопределенное поведение в случае повторного удаления объекта. Также плохо, когда указатель используется в качестве ссылки на обязательный объект, который не может отсутсвовать. Вообще плохо, когда сырые указатели используются бездумно, просто потому, что программист не видит особой разницы между ссылкой и указателем. В то же время есть случаи, когда использование сырых указателей вполне оправдано — например, в качестве итераторов последовательностей, занимающих непрерывные области в памяти.
Идем далее. Современный С++ заточен, в первую очередь, под решение практических задач. На выбранном примере трудно увидеть преимущества С++ как раз ввиду того, что практическая ценность данного примера стремится к нулю. Суди сам: твой список заточен под конкретный тип данных и у тебя (в рамках данного примера) даже не предусмотрено даже сколько-нибудь общего способа наполнить список произвольными данными. Как только ты захочешь исправить этот недостаток, твой код тут же начнет раздуваться и обрастать костылями, подобными print_integer_list и new_integer_list. Короче говоря, твой оригинальный пример на С лишь создает иллюзию простоты в виду своей полной практической бесполезности.
Идем дальше, твой пример на C иллюстрирует следующую большую проблему — необходимость ручного управления ресурсами. Программист, разрабатывавший класс списка тупо переложил ответственность за освобождение ресурсов на пользователя. Теперь остается только молиться на то, что пользователь вызовет free_integer_list в нужное время, в нужном месте и сделает это ровно один раз. Когда вся программа строится по такому принципу, вот тогда и получаются традиционные для С проблемы: утечки памяти, крэши и неопределенное поведение.
Что еще хочется отметить — абсолютно необоснованное использование полиморфизма времени выполнения — очень распространненная болячка. Мне доводилось видеть много примеров, когда ран-тайм полиморфизм применялся не потому, что он реально нужен, а только потому, что "так научили" и программист просто не знает, как можно это сделать по-другому.
Еще одна твоя ошибка в том, что ты пытаешься перенести эту проблемную реализацию с С на С++ один-в-один со всеми ее проблемами. Конечно же ничего хорошего из этого не получится. Это все равно, что залить керосин в электрическую лампочку и удивляться, почему не светит. При работе с С++ в принципе другой подход нужен.
Ну и как вишенка на торте, распростаненный стереотип — "я хотел сделать проще, поэтому без шаблонов". По факту же все наоборот.
В качестве иллюстрации сказанного можно предложить вот такой пример реализации односвязного списка на C++:
Эта реализация очень груба и местами даже наивна. И вообще она не нужна, поскольку в стандартной библиотеке есть тот же std::list и другие контейнеры. Тем не менее, даже такая реализация демонстрирует возможности и преимущества C++ по всем критериям — и по простоте и наглядности кода, и по обобщенности.
Для наглядности можно посмотреть на использование:
сравни это:
int main()
{
for (double value : UniDirList{3.14, 2.71, 1.61})
{
std::cout << value << " ";
}
}
Какой вариант проще для восприятия? Прими также во внимание, что я прямо в имеющейся реализации могу использовать произвольные типы данных: числа, строки, стандартные и пользовательские классы. И что пришлось бы сделать тебе, чтобы добиться сопоставимой функциональности.
Здравствуйте, vsb, Вы писали:
vsb>Ссылка это указатель, который никогда не может быть равен NULL, который нельзя изменить и который не нужно разыменовывать.
Это достаточно поверхностное представление.
— Указатель — объект, а ссылка — нет;
— У указателя есть адрес, а у ссылки нет;
— Указатель обладает свойством двойной константности — собственная и константность объекта, у ссылок собственной константности нет;
— Ссылки на указатели возможны, а указатели на ссылки — нет;
— Также возможны указатели на указатели и даже указатели на указатели на указатели, но не бывает ссылок на ссылки;
— Бывают perfect forwarding references, но не бывает perfect forwarding pointers;
— Кроме этого ссылки обладают специальной семантикой при определении специальных функций-членов — конструкторов и операторов присваивания;
— Ссылки обладают особыми свойствами, которые могут влият на время жизни временных объектов. У указателей таких свойств нет.
Здравствуйте, Worminator X, Вы писали: WX>Несколько раз пытался освоить C++, и всегда забрасывал это дело — сложный, совершенно непонятный язык.
Забрасывал..., но ведь потом опять возвращался к C++! WX>Что нужно исправить?
немного код и немного пробелов в знаниях.
#include <iostream>
using namespace std;
enum ListType {
LT_EMPTY_LIST,
// Переименовываем LT_INTEGER_LIST в невладеющий указателями View
LT_INTEGER_LIST_VIEW,
// Для примера добавляем владеющий указателями Container
LT_INTEGER_LIST_CONTAINER
};
class List {
public:
virtual ListType getType() const = 0;
// Добавил сервисный метод is для красоты:bool is(ListType type) const { return type == getType();}
List() {}
// Как уже подсказали, нужен деструктор при наследовании,
// т.к. вводим владеющий контейнер:virtual ~List() {}
};
class EmptyList: public List {
public:
virtual ListType getType() const override {
return LT_EMPTY_LIST;
}
EmptyList(int x): List() {}
};
// Выделяем общий интерфейс:class IntegerList: public List {
public:
virtual int getNumber() const {
return 0;
}
// чтобы не в даваться в детали, пусть будет виртуальным:virtual const List &getNext() const = 0;
};
// Реализация твоего IntegerView, как невладеющий указателями IntegerListView:class IntegerListView: public IntegerList {
private:
const int number;
const List &next;
public:
virtual ListType getType() const override {
return LT_INTEGER_LIST_VIEW;
}
virtual int getNumber() const override {
return number;
}
virtual const List &getNext() const override {
return next;
}
IntegerListView(
int number,
const List &_next
): number(number), next(_next) {}
};
// Добавляем для примера владеющий указателями IntegerListContainer:class IntegerListContainer: public IntegerList {
private:
const int number;
const List *next;
public:
virtual ListType getType() const override {
return LT_INTEGER_LIST_CONTAINER;
}
virtual int getNumber() const override {
return number;
}
virtual const List &getNext() const override{
return *next;
}
IntegerListContainer(
int number,
List *_next
): number(number), next(_next) {}
virtual ~IntegerListContainer() override {
if (next) {
delete next;
// ^^^^^^ Поэтому и нужен виртуальный деструтор.
}
}
};
// Слегка меняем интерфейс функции:void recursivePrintIntegerList(const IntegerList* pList, int counter, ListType LT_INTEGER_LIST_VIEW) {
cout << pList->getNumber();
if (pList->getNext().is(LT_INTEGER_LIST_VIEW)) {
cout << ',' << ' ';
recursivePrintIntegerList((const IntegerList*)&pList->getNext(), counter + 1, LT_INTEGER_LIST_VIEW);
}
if (counter == 1) {
cout << endl;
}
}
// Тоже слегка меняем интерфейс функции:void recursivePrintIntegerList(const IntegerList *pList, ListType LT_INTEGER_LIST_VIEW) {
cout << "recursivePrintIntegerList(" << LT_INTEGER_LIST_VIEW << ") => ";
recursivePrintIntegerList(pList, 1, LT_INTEGER_LIST_VIEW);
}
// Тоже слегка меняем интерфейс функции:void foreachPrintIntegerList(const IntegerList&list, ListType LT_INTEGER_LIST_VIEW) {
cout << "foreachPrintIntegerList(" << LT_INTEGER_LIST_VIEW << ") => ";
int counter = 0;
for (
// Тут тебе уже подсказали, что твой вариант предполагал семантику ссылок языков с GC,
// Поэтому явное возьмём указатели:const List *current = &list;
current && current->is(LT_INTEGER_LIST_VIEW);
current = &((const IntegerList*)current)->getNext()
) {
if (counter > 10) break;
if (counter > 0) {
cout << ',' << ' ';
}
counter++;
cout << ((const IntegerList*)current)->getNumber();
}
cout << endl;
}
int main(int argc, char *argv[]) {
const EmptyList empty(0);
const IntegerListView n3(3, empty);
const IntegerListView n2(2, n3);
const IntegerListView n1(1, n2);
// const IntegerList numbers(1, IntegerList(2, IntegerList(3, empty)));
// Если так ^^^^^^^^^^^^ сделать,
// то временные объекты тут же разрушатся и останутся висячие указатели на них.
// тут можно использовать владеющий контейнер пока так:const IntegerListContainer numbers(1, new IntegerListContainer(2, new IntegerListContainer(3, new EmptyList(0))));
// Кстати, после программирования на сборщике мусора,
// людям обычно нравятся std::shared_ptr<>,
// т.к. они по семантике похожи, но есть особенности.
// Подправим вывод информационных сообщений:
cout << "n1... => " << n1.getNumber() << ',' << ' ' << n2.getNumber() << ',' << ' ' << n3.getNumber() << endl;
cout << "numbers... => " << numbers.getNumber() << ',' << ' ';
cout << ((IntegerListContainer&)numbers.getNext()).getNumber() << ',' << ' ';
cout << ((IntegerListContainer&)(
(IntegerListContainer&)(
(IntegerListContainer&)numbers.getNext()
).getNext()
)).getNumber() << endl;
recursivePrintIntegerList(&n1, n1.getType());
recursivePrintIntegerList(&numbers, numbers.getType());
foreachPrintIntegerList(n1, n1.getType());
foreachPrintIntegerList(numbers, numbers.getType());
return 0;
}
WX>что такое есть ссылка в C++ (вроде синтаксический сахар над указателем, но как-то странно и непонятно работает).
Это ты ещё думаешь семантикой языков с GC. Надо уже переключиться на C++.
Есть разница в понятии "ссылка" в языках со сборщиком мусора и без него:
В языках со сборщиком мусора ссылка может изменять адрес связанного объекта.
В С++ ссылка не может изменять адрес связанного объекта.
Здравствуйте, andrey.desman, Вы писали:
AD>Ссылки — это не совсемсинтаксический сахар над указателями. По крайней мере не в смысле определения, хотя их и можно использовать как таковые. Ссылка — это альтернативное имя для какого-то выражения, алиас. AD>Саму ссылку нельзя изменить, она привязывается (bind) к чему-то в момент определения и остается привязанной к этому выражению до конца своего существования. AD>Грубо говоря, ссылка ведет себя как разыменованный константный указатель. Не указатель на константу, а именно константный указатель. А, например, константная ссылка как константный указатель на константу (const * const).
AD>создается ссылка current, которая привязывается к list. Она и останется привязанной к list. AD>Выражение current = ((IntegerList&)current).getNext() не перепривязывает ссылку current к другому объекту, а присваивает привязанному объекту list значение из list.getNext().
AD>Как раз здесь ссылки неуместны, если ты конечно не строишь иммутабельные списки. Если потребуется список поменять, его придется перестроить весь, от начала и до конца. AD>При этом, с динамической памятью на ссылках работать не получится.
Мда, сложно как-то. Ну его на фиг, этот C++, лучше чистый Си с указателями и Java для ООП.
А списки хотел сделать именно иммутабельными (вообще это попытка написать свой интерпретатор Лиспа, там было более сложное наследование с атомами и списочными парами).
Ссылка — это некий аналог #define макросов для компилятора, получается?
Как запру я тебя за железный замок, за дубовую дверь окованную,
Чтоб свету божьего ты не видела, мое имя честное не порочила…
М. Лермонтов. Песня про царя Ивана Васильевича, молодого опричника и удалого купца Калашникова
Здравствуйте, m2user, Вы писали:
M>Зачем вообще рекурсия при обходе связного списка?
Потому что цикл for на ссылках написать не смог. Как здесь объяснили, это потому что ссылку поменять нельзя.
Как запру я тебя за железный замок, за дубовую дверь окованную,
Чтоб свету божьего ты не видела, мое имя честное не порочила…
М. Лермонтов. Песня про царя Ивана Васильевича, молодого опричника и удалого купца Калашникова
Для чего тогда вообще в C++ нужны ссылки? Какую задачу они решают? Если все равно приходится использовать указатели, то достаточно моего 2-го кода с new/delete.
Как запру я тебя за железный замок, за дубовую дверь окованную,
Чтоб свету божьего ты не видела, мое имя честное не порочила…
М. Лермонтов. Песня про царя Ивана Васильевича, молодого опричника и удалого купца Калашникова
Здравствуйте, rg45, Вы писали:
R>Какой вариант проще для восприятия? Прими также во внимание, что я прямо в имеющейся реализации могу использовать произвольные типы данных: числа, строки, стандартные и пользовательские классы. И что пришлось бы сделать тебе, чтобы добиться сопоставимой функциональности.
Для меня пока код на STL и Boost выглядит как магия. Примерно как Spring Boot для начинающих Java разработчиков, не понимающих устройство аннотаций и инициализацию application context'а при старте.
Разработчик на C++ не должен воспринимать код как магию, он должен полностью понимать внутреннее устройство программы, и в какие инструкции она компилируется.
Поэтому изучению STL и других библиотек должно предшествовать полное изучение языковых возможностей, об этом же пишет доцент Столяров в своих книгах.
Как запру я тебя за железный замок, за дубовую дверь окованную,
Чтоб свету божьего ты не видела, мое имя честное не порочила…
М. Лермонтов. Песня про царя Ивана Васильевича, молодого опричника и удалого купца Калашникова
Здравствуйте, Worminator X, Вы писали:
WX>Для чего тогда вообще в C++ нужны ссылки? Какую задачу они решают?
Ссылки позволяют представить объекты в едином виде, без разделения на статические, автоматические и динамические (а в C++ могут быть еще и временные).
WX>Если все равно приходится использовать указатели
Нет смысла избавляться от указателей просто потому, что они указатели. Там, где указатель имеет смысл, как независимый объект (например, где он перемещается по массиву/списку, меняет значение произвольно и т.п.), логично использовать именно указатель. А в функцию, которая обрабатывает просто абстрактный объект, имеет смысл передавать именно ссылку. Если передавать указатель, в функции возникает совершенно ненужная сущность и возможность ею оперировать.
Здравствуйте, Worminator X, Вы писали:
WX>Для меня пока код на STL и Boost выглядит как магия. Примерно как Spring Boot для начинающих Java разработчиков, не понимающих устройство аннотаций и инициализацию application context'а при старте. WX>Разработчик на C++ не должен воспринимать код как магию, он должен полностью понимать внутреннее устройство программы, и в какие инструкции она компилируется. WX>Поэтому изучению STL и других библиотек должно предшествовать полное изучение языковых возможностей, об этом же пишет доцент Столяров в своих книгах.
Ну, все правильно, в общем-то. Тут все зависит от личной мотивации и желания. Остальное — лишь дело техники. Ну и практика, куда ж без нее.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Если не нравятся специфические приемы C++ — пишите на "C с классами", используя где указатели, где ссылки, в зависимости от удобства.
Для себя я пишу на чистом Си без виртуальных методов. В принципе связка Java + Си через JNI + ассемблер для платформенно-зависимых вещей решает практически любые задачи.
Просто бытует мнение, что без знания C++ и Haskell нельзя считаться настоящим программистом.
И в геймдеве, например, многие библиотеки только под C++, чистый Си там почему-то не любят.
Как запру я тебя за железный замок, за дубовую дверь окованную,
Чтоб свету божьего ты не видела, мое имя честное не порочила…
М. Лермонтов. Песня про царя Ивана Васильевича, молодого опричника и удалого купца Калашникова
Здравствуйте, Worminator X, Вы писали:
WX>Для меня пока код на STL и Boost выглядит как магия.
Так это магия и есть, причем весьма корявая. Кто-то считает, что это и есть "истинный C++", кто-то так не считает.
WX>Разработчик на C++ не должен воспринимать код как магию
Не должен, но уже пара поколений C++-активистов его к этому принуждают.
WX>он должен полностью понимать внутреннее устройство программы, и в какие инструкции она компилируется.
По факту, это уже давно считается немодным. Теперь и на C++ программирование заключается в "кодировании алгоритма конструкциями языка".
WX>Поэтому изучению STL и других библиотек должно предшествовать полное изучение языковых возможностей
А толку? Чисто языковые возможности C++ и шаблонная магия практически ортогональны друг другу. Более того, после вдумчивого изучения и использования "базового" C++, шаблонная магия способна вызывать стойкое отвращение.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Нет смысла избавляться от указателей просто потому, что они указатели. Там, где указатель имеет смысл, как независимый объект (например, где он перемещается по массиву/списку, меняет значение произвольно и т.п.), логично использовать именно указатель. А в функцию, которая обрабатывает просто абстрактный объект, имеет смысл передавать именно ссылку. Если передавать указатель, в функции возникает совершенно ненужная сущность и возможность ею оперировать.
Так, то есть указатели нужны там, где связанный объект может меняться (при обходе по циклу, например)? А ссылки всегда иммутабельные после первого присваивания?
Как запру я тебя за железный замок, за дубовую дверь окованную,
Чтоб свету божьего ты не видела, мое имя честное не порочила…
М. Лермонтов. Песня про царя Ивана Васильевича, молодого опричника и удалого купца Калашникова
Здравствуйте, Worminator X, Вы писали:
WX>Для себя я пишу на чистом Си без виртуальных методов.
Если будете писать на "C с классами и виртуальными методами", то на выходе получите почти (с точностью до особенностей оптимизатора) идентичный код.
WX>бытует мнение, что без знания C++ и Haskell нельзя считаться настоящим программистом.
Подозреваю, что Haskell большинство "настоящих программистов на C++" таки не знает. Но вот тех, кто не использует шаблонную магию, действительно принято считать ненастоящими. Я, кстати, тоже из этих.
WX>И в геймдеве, например, многие библиотеки только под C++, чистый Си там почему-то не любят.
Я вообще не вижу ни одного преимущества чистого C перед C++, кроме некоторого синтаксического сахара, добавленного в C в процессе параллельного развития с C++. Все, что написано на C, можно переписать на C++, и это будет более компактно, более понятно, более надежно и не менее эффективно. Но вот это откровенно дурацкое убеждение "не используя шаблонную магию, вы не используете C++" настолько прочно засрало всем мозги, что изучение C++ часто начинают именно с нее. Адекватно понять эти нечеловеческие конструкции без глубокого понимания ядра языка невозможно, поэтому большинство их тупо заучивает, и применяет где по шаблону, где по аналогии, а где просто интуитивно.
А если не стремиться следовать модным трендам, и использовать C++ в том объеме, который реально необходим для задачи, он у C только выигрывает.