Insert с иттератором

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Есть ещё замечание по коду. Везде делать проверку if (Owner==nullptr) и кидать исключение, не нужно. Достаточно сделать эту проверку в конструкторе итератора, ведь дальше Owner нигде в коде не меняется.
А как быть с конструктором итератора по умолчанию?
WinMain писал(а):Вариант моего контейнера с исключениями находится в этой теме.
Так:

Код: Выделить всё

 class TItem
 {
  friend class TList;
  friend class Iterator;
  private:
   TItem *Next;
  public:
   int Data;
 };
 private:
  TItem *First;
  TItem *Last;
 public:
  TList()
  {
   First=nullptr;
   Last =nullptr;
  }
  ~TList()
  {
   TItem *Next;
   for (Next=First->Next; Next!=nullptr; First=Next, Next=First->Next)
   {
    delete First;
   }
   Last=nullptr;
  }
  Iterator Insert (const Iterator &After, const int &Data)
  {
   TItem *Item;
   if (After.Owner==nullptr)
   {
    throw std::u32string(U"Адресация элемента для вставки неинициированным итератором.");
   }
   if (After.Owner!=this)
   {
    throw std::u32string(U"Адресация элемента для вставки итератором другого контейнера.");
   }
   Item=new (std::nothrow) TItem;
   if (Item==nullptr)
   {
    throw std::u32string(U"Ошибка выделения памяти по указателю Item в функции-члене Iterator TList::Insert(const Iterator &After, const int &Data)).");
   }
   Item->Data=Data;
   if (After.Pointer==nullptr)
   {
    if (Last!=nullptr)
    {
     Last->Next=Item;
    }
    Last=Item;
    Item->Next=nullptr;
   }
   if (First==nullptr)
   {
    First=Item;
   }
   return Iterator(this, Item->Next);
  }
  Iterator Begin()
  {
   return Iterator(this, First);
  }
  Iterator End ()
  {
   return Iterator(this, nullptr);
  }
  Iterator Find(const int &Value)
  {
   TItem *Item;
   for (Item=First; Item!=nullptr; Item=Item->Next)
   {
    if ((Item->Data)==Value)
    {
     return Iterator(this, Item);
    }
   }
   return Iterator(this, nullptr);
  }
};
что ли?
Romeo писал(а):ведь дальше Owner нигде в коде не меняется. И, кстати, этот факт можно подчеркнуть, объявив поле вот так:

Код: Выделить всё

TList *const Owner;
Нельзя, так как он меняется при присваивании.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):А как быть с конструктором итератора по умолчанию?
А зачем ты его вообще сделал? Он нигде не используется.
Сионист писал(а):Так что ли?:
Я бы всё-таки рекомендовал для проброски исключения использовать отдельный класс-исключение, вместо класса-строки. Всё должно использоваться по назначению, а не абы как.
Сионист писал(а):Нельзя, так как он меняется при присваивании.
Как только ты поймёшь разницу между инициализацией и присваиванием, ты сразу же совершишь гигантский качественный скачок.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Romeo писал(а):А зачем ты его вообще сделал? Он нигде не используется.

Код: Выделить всё

int main()
{
 TList List;
 int   i;
 TList::Iterator Iterator;
 Iterator=List.Begin();
 List.Insert(Iterator, 2);
 List.Insert(Iterator, 21);
 List.Insert(Iterator, 3);
 List.Insert(Iterator, 4);
 List.Insert(Iterator, 8);
 List.Insert(Iterator, 5);
 List.Insert(Iterator, 1);
 List.Insert(Iterator, 6);
 List.Insert(Iterator, 3);
 List.Insert(Iterator, 4);
 for (Iterator=List.Begin(); Iterator!=List.End(); ++Iterator)
 {
  std::cout<<*Iterator<<'\t';
 }
 Iterator=List.Find(8);
 (*Iterator)=104;
 for (Iterator=List.Begin(); Iterator!=List.End(); ++Iterator)
 {
  std::cout<<*Iterator<<'\t';
 }
 for (Iterator=List.Find(4); Iterator!=List.End(); Iterator=List.Find(++Iterator,4))
 {
  (*Iterator)=74;
 }
 for (Iterator=List.Begin(); Iterator!=List.End(); ++Iterator)
 {
  std::cout<<*Iterator<<'\t';
 }
 std::cout<<std::endl;
 return 0;
}
Как только ты поймёшь разницу между инициализацией и присваиванием, ты сразу же совершишь гигантский качественный скачок.
Ну это то разницу я понял 20 лет назад. Качественный скачок? Ну да был: я смог начать программировать не только на бейсике.
Я бы всё-таки рекомендовал для проброски исключения использовать отдельный класс-исключение, вместо класса-строки.
All complete. Расскажите, что именно бросать в каждом случае. Строки предлагал WinMain, вот я его и спросил, правильно ли я его понял. Расскажите свой вариант, исправлю.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):

Код: Выделить всё

 TList::Iterator Iterator;
 Iterator=List.Begin();
Не самый лучший пример, так как здесь можно (и даже нужно) написать вот так:

Код: Выделить всё

 TList::Iterator Iterator = List.Begin();
В чём отличие объяснять? Это как раз ещё один камень в огород "инициализации и присваивания".

Однако дефолтный конструктор всё же нужен, я в этом соглашусь. Правда во всех случаях, когда проверка на нулевой Owner у тебя может стрельнуть, у тебя делается проверка на нулевой Pointer, и стреляет именно она. Так что проверку на Owner всё равно можно убирать. Ну, либо, я не весь код вижу...
Сионист писал(а): Ну это то разницу я понял 20 лет назад. Качественный скачок? Ну да был: я смог начать программировать не только на бейсике.
Понял 20 лет назад, но упорно делаешь присваивание там, где нужна инициализация? Я впечатлён такой настойчивостью и непоколебимостью. Поля в конструкторе List у тебя присваиваются, поля в конструкторе Iterator - тоже. При создании Iterator тоже делаешь отдельно создание, а потом делаешь присваивание. Переделай присваивание на инициализацию и код сразу же заработает с const (это я сейчас о конструкторе Iterator).

Правда, после того, как ты открыл main я понимаю, что после исправления конструктора, следующей ошибкой компиляции станет неявный оператор присваивания, который генерируется компилятором. Его ты вызываешь из main. Эту проблему уже не решить...
Сионист писал(а): All complete. Расскажите, что именно бросать в каждом случае. Строки предлагал WinMain, вот я его и спросил, правильно ли я его понял. Расскажите свой вариант, исправлю.
Я предлагаю внутри класса List сделать класс Exception. Его можно либо отнаследовать от std::exception, либо, если наследоваться ни от чего не хочется, то просто разместить внутри какое-нибудь поле для хранения дополнительной информации об ошибке. Это должна быть либо строка, либо вообще какой-нибудь enum.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Не самый лучший пример, так как здесь можно (и даже нужно) написать вот так:
Код :
TList::Iterator Iterator = List.Begin();
В чём отличие объяснять? Это как раз ещё один камень в огород "инициализации и присваивания".
Здесь может и так. А если

Код: Выделить всё

int main()
{ 
 TList List;
 int   i;
 TList::Iterator Iterator;
 for (Iterator=List.Begin(), i=0; i<10; ++Iterator, ++i)
 {
  std::cin>>*Iterator;
 }
 Iterator=List.Find(8);
 (*Iterator)=104;
 for (Iterator=List.Begin(); Iterator!=List.End(); ++Iterator)
 {
  std::cout<<*Iterator<<'\t';
 }
 for (Iterator=List.Find(4); Iterator!=List.End(); Iterator=List.Find(++Iterator,4))
 {
  (*Iterator)=74;
 }
 for (Iterator=List.Begin(); Iterator!=List.End(); ++Iterator)
 {
  std::cout<<*Iterator<<'\t';
 }
 std::cout<<std::endl;
 return 0;
}
? Альтернатива - декларировать в заголовке цикла то, что имеет смысл во всей функции, так как в ней записан только линейно-циклический алгоритм многократного перебора контейнера по сути одним и тем же итератором, со вставкой из поиска и модификации по сути по тому же самому итератору, а это приведёт к лишему дублированию декларации, да ещё и появится двухстрочный блок на пустом месте. Вторая альтернатива - пустая первая позиция в заголовке цикла, что хорошо лишь в случае, когда один цикл продолжает перебор изменяемой в цикле переменной, уже перебранной в предыдущем цикле, а здесь этого нет. Или

Код: Выделить всё

int main()
{
 TList List;
 int   i;
 TList::Iterator Iterator;
 ...
 Iterator=List.Begin();
 List.Insert(Iterator, 2);
 List.Insert(Iterator, 21);
 List.Insert(Iterator, 3);
 List.Insert(Iterator, 4);
 List.Insert(Iterator, 8);
 List.Insert(Iterator, 5);
 List.Insert(Iterator, 1);
 List.Insert(Iterator, 6);
 List.Insert(Iterator, 3);
 List.Insert(Iterator, 4);
 for (Iterator=List.Begin(); Iterator!=List.End(); ++Iterator)
 {
  std::cout<<*Iterator<<'\t';
 }
 Iterator=List.Find(8);
 (*Iterator)=104;
 for (Iterator=List.Begin(); Iterator!=List.End(); ++Iterator)
 {
  std::cout<<*Iterator<<'\t';
 }
 for (Iterator=List.Find(4); Iterator!=List.End(); Iterator=List.Find(++Iterator,4))
 {
  (*Iterator)=74;
 }
 for (Iterator=List.Begin(); Iterator!=List.End(); ++Iterator)
 {
  std::cout<<*Iterator<<'\t';
 }
 std::cout<<std::endl;
 return 0;
}
? Декларация между операциями конечно читается, но со спотыканием и слишком рано инициировать конкретным значением тоже плохо. Потому и присваивание вместо инициализации. А если итератор - это вообще out-параметр функции, но не in-параметр? Например,

Код: Выделить всё

void Extrems(TList List; TList::Iterator &Min, TList::Iterator &Max)
{
 TList::Iterator Item;
 if (List::Begin==List::End())
 {
  Min=List::End;
  Max=List::End;
 }
 else
 {
  for (Min=List::Begin(), Max=List::Begin(), Item=List::Begin(); Item!=List::end(); ++Item)
  {
   if ((*Min)>(*Item))
   {
    Min=Item;
   }
   if ((*Max)<(*Item))
   {
    Max=Item;
   }
  }
 }
}
? Как инициировать Min и Max в вызывающей функции? А так итератор может быть инициирован без привязки к определённому контейнеру.
Поля в конструкторе List у тебя присваиваются, поля в конструкторе Iterator - тоже.
Потому что так удобнее и ни чего не мешает так делать. Инициализация в конструкторе повторяет сие синтаксис явного вызова конструктора предка. А часто ли он вообще используется? Даже в сумме для инициализации полей и для передачи части параметров конструктора конструктору предка его класса? А присваивание часто. Частое применение даёт меньше шансов переврать синтакис, к тому же синтаксис присваивания проще.
Romeo писал(а):Я предлагаю внутри класса List сделать класс Exception. Его можно либо отнаследовать от std::exception, либо, если наследоваться ни от чего не хочется, то просто разместить внутри какое-нибудь поле для хранения дополнительной информации об ошибке. Это должна быть либо строка, либо вообще какой-нибудь enum.
Что за поле? Как кодировать в нём инфу о месте и характере ошибки? Как бросать? Как выводить в лог при перехвате?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а): А если...

Альтернатива - декларировать в заголовке цикла то, что имеет смысл во всей функции, так как в ней записан только линейно-циклический алгоритм многократного перебора контейнера по сути одним и тем же итератором, со вставкой из поиска и модификации по сути по тому же самому итератору, а это приведёт к лишему дублированию декларации, да ещё и появится двухстрочный блок на пустом месте. Вторая альтернатива - пустая первая позиция в заголовке цикла, что хорошо лишь в случае, когда один цикл продолжает перебор изменяемой в цикле переменной, уже перебранной в предыдущем цикле, а здесь этого нет.

Или? Декларация между операциями конечно читается, но со спотыканием и слишком рано инициировать конкретным значением тоже плохо. Потому и присваивание вместо инициализации. А если итератор - это вообще out-параметр функции, но не in-параметр? Например...
Много белого шума... Причём бесполезного, так как на следующей же строчке я написал, что согласен, что для других случаев дефолтный конструктор понадобится. Ты что, прочёл первую строку, весь исполнился праведного гнева, накатал три страницы текста, а следующую строку прочитал только после того, как иссяк, и, посмотрев на то, сколько работы проделано, решил не удалять результаты аврального труда? :)
Сионист писал(а):

Код: Выделить всё

void Extrems(TList List; TList::Iterator &Min, TList::Iterator &Max)
А вот за это нужно бить, причём нещадно. List - только константной ссылкой и никак иначе! Копирование тяжёлых объектов при передаче параметров - это один из признаков "дурно пахнущего кода".
Сионист писал(а):Потому что так удобнее
Удобнее, чем что? Чем в списке инициализации указать? Вместо "равно" написать скобочки, и уже не удобно сразу? Очень спорное утверждение... Неудобно спать на потолке, а код нужно писать не так, чтобы он был удобным, а так, чтобы был правильным.
Сионист писал(а):... и ни чего не мешает так делать.
Как раз мешает. Во всяком случае должно мешать хорошему программисту. Спросишь что? Отвечу - здравый смысл. Если тебе ничего не мешает, то делай выводы сам...

Инициализация вызывается в любом случае. Если ты её не прописываешь явно, то для поля вызывается дефолтный конструктор. Зачем вызывать дефолтный конструктор и затем вызывать оператор присваивания, если можно сразу проинициализировать поле нужным значением? Если поле - объект тяжёлого класса, то разница в скорости выполнения может быть существенной. Скажешь, что для встроенных типов, таких, как указатели, профит будет мизерным? Да соглашусь. Но зачем отказываться от мизерного профита, если его можно добиться совсем не тратя сил? А ведь есть ещё один аргумент. Есть типы, которые можно только инициализировать, а присваивать нельзя. Это константы. Именно поэтому у тебя с константным Owner не будет работать код конструктора в текущем его состоянии, но будет работать, если переписать на инициализацию.

Совет на будущее. Всегда используй инициализацию везде, где это возможно. Присваивание используй лишь тогда, когда это действительно необходимо по контексту (когда переменная должна хранить сначала одно значение, а потом другое). Такой подход - это признак хорошего тона в С++.
Сионист писал(а): Инициализация в конструкторе повторяет сие синтаксис явного вызова конструктора предка. А часто ли он вообще используется? Даже в сумме для инициализации полей и для передачи части параметров конструктора конструктору предка его класса? А присваивание часто. Частое применение даёт меньше шансов переврать синтакис, к тому же синтаксис присваивания проще.
Чистый бред. Читается так, будто писал человек, который вообще начал программировать на С++ два дня назад. Избавляйся уже от заблуждений молодости, пора становиться более зрелым программистом...
Сионист писал(а):Что за поле? Как кодировать в нём инфу о месте и характере ошибки? Как бросать? Как выводить в лог при перехвате?
Вот пример:

Код: Выделить всё

class List
{
...
class Exception
{
public:
   enum class Type : int { Deref, Common };

   Exception(Type type) : m_type(type) {}

   std::string GetDescription() const
   {
      switch (m_type)
      {
         case Type: :D eref:
            return "Error of dereferencing";
         case Type::Common:
            return "Common error";
      }

      assert(false);
      return "";
   }

private:
   Type m_type;
};
...
class Iterator
{
...
   if (!Pointer) throw Exception(Exception::Type: :D eref);
...
};
...
};

int main()
{
    List list;
    List::Iterator it;

    try
    {
      *it;
    }
    catch (const List::Exception& ex)
    {
      std::cout << ex.GetDescription() << std::endl;
      return 1;
    }

    return 0;
}
Набор ошибок, конечно, нужно расширить.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

А вот за это нужно бить, причём нещадно. List - только константной ссылкой и никак иначе!
Ну пропустил. Не в реальном же проекте и даже не в примере, предназначенном в качестве образца, как надо делать.
Инициализация вызывается в любом случае. Если ты её не прописываешь явно, то для поля вызывается дефолтный конструктор. Зачем вызывать дефолтный конструктор и затем вызывать оператор присваивания, если можно сразу проинициализировать поле нужным значением? Если поле - объект тяжёлого класса, то разница в скорости выполнения может быть существенной. Скажешь, что для встроенных типов, таких, как указатели, профит будет мизерным? Да соглашусь. Но зачем отказываться от мизерного профита, если его можно добиться совсем не тратя сил?
Как раз в том и заковыка. Применять префиксные, но не постфиксные инкремент/декремент везде, где не требуется перед икрементом/декрементом вернуть старое значение модифицируемой переменной - оптимизация, на которую ничего не тратится. А вспоминать весь синтаксис явного вызова конструктора предка - это нифига не нулевые усилия. Да ещё и каждый раз гуглить его на всякий случай. Там ведь не просто скобки поставить, это не

Код: Выделить всё

int main()
{
 int *p(nullptr);
}
. Только что погуглил, там надо прописать вызов конструктора до фигурных скобок, поставить двоеточие, разделить запятыми.
Чистый бред. Читается так, будто писал человек, который вообще начал программировать на С++ два дня назад. Избавляйся уже от заблуждений молодости, пора становиться более зрелым программистом...
На плюсах пишу с 2001-го года, за всё время вызов конструктора предка в принципе мог применить раза три и раз пятьсот инициализацию полей. Присваивание же применяется в большем количестве за неделю.
enum class Type : int { Deref, Common };
Что это за синтаксис? Где можно о нём прочитать?
Набор ошибок, конечно, нужно расширить.
Я и эти коды ошибок не понял.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а): Как раз в том и заковыка. Применять префиксные, но не постфиксные инкремент/декремент везде, где не требуется перед икрементом/декрементом вернуть старое значение модифицируемой переменной - оптимизация, на которую ничего не тратится. А вспоминать весь синтаксис явного вызова конструктора предка - это нифига не нулевые усилия. Да ещё и каждый раз гуглить его на всякий случай. Там ведь не просто скобки поставить, это не
...
Только что погуглил, там надо прописать вызов конструктора до фигурных скобок, поставить двоеточие, разделить запятыми.
У тебя здесь нет никакого базового класса. Не путай вызов базового класса и инициализацию полей. Если пишешь на С++ с 2001 года, то очень печально не помнить и даже каждый раз гуглить, как делается инициализация полей или вызов базового конструктора.

Инициализация полей:

Код: Выделить всё

Iterator() : Pointer(nullptr), Owner(nullptr)
{
}
Не понимаю, чем вышеприведенный код сложнее, чем вот такой:

Код: Выделить всё

Iterator() 
{
   Pointer = nullptr;
   Owner = nullptr;
}
Сионист писал(а): На плюсах пишу с 2001-го года, за всё время вызов конструктора предка в принципе мог применить раза три и раз пятьсот инициализацию полей. Присваивание же применяется в большем количестве за неделю.
На самом деле, это крайне грустная статистика. Она говорит о том, что достаточно сложные арихитектурные решения со сложной иерархией классов ты либо никогда не реализовывал, либо реализовывал крайне редко. Сделаю предположение, что скорее всего писал по мелочам, например, небольшие утилитки для прикладных вычислений. Для такого написания не требовалось ни создавать иерархии классов, ни использовать всю мощь языка (отсюда не академичность знаний). Собственно, достаточно было знаний чистого С, плюс базовые знания ООП, верно? Думаю, я прав. Даже не знаю, что посоветовать делать в таком случае. Советовать читать Страуструпа, наверное, уже поздно. Это нужно было 15 лет назад делать, когда только начинал - тогда бы это результаты дало. Ты же методом тыка пытался учиться, видимо. Хотя нет, совет всё-таки есть. Хотя бы просто прислушивайся к тому, что советуют программисты, для которых С++ является основной профессией.
Сионист писал(а): Что это за синтаксис? Где можно о нём прочитать?
Это новый синтаксис для enum-ов, который был введён в 11 стандарте. Вот неплохая статья на хабре о новинках нового стандарта. Рекомендую её прочесть всю, она небольшая. О enum там написано в пункте №5.
Сионист писал(а): Я и эти коды ошибок не понял.
На каждый тип ошибки нужно создать свой enum item. Для ошибки разыменования я уже создал Deref. Нужно ещё как минимум отдельное значение для попытки вставить в чужой контейнер. Может потом и ещё что-то выплывет. Последнее значения (Common) используется для случаев, которые не категоризованы.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

У тебя здесь нет никакого базового класса. Не путай вызов базового класса и инициализацию полей.
Я и не путаю, речь только об идентичности синтаксиса. Остальные переменные инициируются другим синтаксисом.
Сделаю предположение, что скорее всего писал по мелочам, например, небольшие утилитки для прикладных вычислений.
Утилитами не занимаюсь вообще и самое маленькое, что я написал, заняло 5 месяцев.
На самом деле, это крайне грустная статистика. Она говорит о том, что достаточно сложные арихитектурные решения со сложной иерархией классов ты либо никогда не реализовывал, либо реализовывал крайне редко.
Трёхмерная модель свободно-вынужденной темпломагнитоэлектрогидродинамики достаточно сложна, фёст альпина запросила за более простой двумерный проект два миллиона, но при этом какая либо иерархия классов вне пользовательского интерфейса была не нужна в принципе, да и вообще весь ООП был по сути ограничен пользовательским интерфейсом.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):Я и не путаю, речь только об идентичности синтаксиса. Остальные переменные инициируются другим синтаксисом.
Ну похожи они только тем, что расположены вне тела конструктора.
Сионист писал(а):Утилитами не занимаюсь вообще и самое маленькое, что я написал, заняло 5 месяцев.
Тогда подобное состояние дел ещё более удивительно.
Сионист писал(а):Трёхмерная модель свободно-вынужденной темпломагнитоэлектрогидродинамики достаточно сложна, фёст альпина запросила за более простой двумерный проект два миллиона, но при этом какая либо иерархия классов вне пользовательского интерфейса была не нужна в принципе, да и вообще весь ООП был по сути ограничен пользовательским интерфейсом.
Это очень плохой показатель. Подозреваю, что там был спагетти код. Крупные проекты без продуманной архитектуры очень быстро превращаются в плохо пахнущие инертные махины, которые ни поддержать, ни расширить, ни покрыть тестами.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить