Несколько раз пытался освоить 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++ нужно всегда использовать ссылки вместо указателей (кроме интеграции с кодом на Си) и по возможности не управлять памятью вручную, т.к. это чревато утечками.
Я попытался переписать код, и вышло примерно следующее (для упрощения пока без шаблонов):
| main.cpp |
| #include <iostream>
using namespace std;
enum ListType {
LT_EMPTY_LIST,
LT_INTEGER_LIST
};
class List {
public:
virtual ListType getType() const = 0;
List() {}
};
class EmptyList: public List {
public:
virtual ListType getType() const override {
return LT_EMPTY_LIST;
}
EmptyList(int x): List() {}
};
class IntegerList: public List {
private:
const int number;
const List &next;
public:
virtual ListType getType() const override {
return LT_INTEGER_LIST;
}
int getNumber() const {
return number;
}
const List &getNext() const {
return next;
}
IntegerList(
int number,
const List &next
): List(next), number(number), next(next) {}
};
void recursivePrintIntegerList(const IntegerList &list, int counter) {
cout << list.getNumber();
if (list.getNext().getType() == LT_INTEGER_LIST) {
cout << ',' << ' ';
recursivePrintIntegerList((const IntegerList&)list.getNext(), counter + 1);
}
if (counter == 1) {
cout << endl;
}
}
void recursivePrintIntegerList(const IntegerList &list) {
recursivePrintIntegerList(list, 1);
}
void foreachPrintIntegerList(const IntegerList &list) {
int counter = 0;
for (
List ¤t = (List&)list;
current.getType() == LT_INTEGER_LIST;
current = ((IntegerList&)current).getNext()
) {
if (counter > 10) break;
if (counter > 0) {
cout << ',' << ' ';
}
counter++;
cout << ((IntegerList&)current).getNumber();
}
cout << endl;
}
int main(int argc, char *argv[]) {
const EmptyList empty(0);
const IntegerList n3(3, empty);
const IntegerList n2(2, n3);
const IntegerList n1(1, n2);
const IntegerList numbers(1, IntegerList(2, IntegerList(3, empty)));
cout << n1.getNumber() << ',' << ' ' << n2.getNumber() << ',' << ' ' << n3.getNumber() << endl;
cout << numbers.getNumber() << ',' << ' ';
cout << ((IntegerList&)numbers.getNext()).getNumber() << ',' << ' ';
cout << ((IntegerList&)(
(IntegerList&)(
(IntegerList&)numbers.getNext()
).getNext()
)).getNumber() << endl;
recursivePrintIntegerList(numbers);
foreachPrintIntegerList(numbers);
return 0;
}
|
| |
Помимо того, что этот талмуд совершенно непонятен, оно еще и не работает.
Рекурсивный обход списка в recursivePrintIntegerList вроде выводит все как положено, но в foreachPrintIntegerList цикл почему-то выводит одни единицы и не хочет переходить на следующий элемент.
Что нужно исправить? И что можно почитать по теме?
Из учебника Столярова так и не понял, что такое есть ссылка в C++ (вроде синтаксический сахар над указателем, но как-то странно и непонятно работает).
И еще, почему, если в EmptyList сделать пустой конструктор, то на строки const EmptyList empty(); const IntegerList n3(3, empty); компилятор ругается? Вариант с const EmptyList empty(void) не помог.