1 февраля 2009 в 04:59

C++ MythBusters. Миф о подставляемых функциях

C++*
Здравствуйте.

Благодаря вот этому голосованию выяснилось, что на Хабре не хватает статей по такому мощному, но всё менее используемому языку C++. Профессионалам высокого уровня, гуру, магам и волшебникам языка C++, а также тем, кто уже успел оставить этот язык «позади» можно дальше не читать. Сегодня я хочу начать цикл статей, призванных помочь именно новичкам, относительно недавно начавшим изучать этот язык, либо же тем, кто (упаси Боже) читает мало книг, а пытается познавать всё исключительно на практике.

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



Лирическое отступление


Несколько слов о названии, призванном объединить статьи подобного рода. Оно, естественно, появилось не случайно, однако и не совсем соответствует сути.

Мои статьи будут рассчитаны на тех, кто уже более-менее знаком с языком C++, но у кого мало опыта в написании программ на нем. Я не буду писать мануалы или «вводные» пособия в духе «ведер», «чашек», «унций» и т.п. Вместо этого я попытаюсь освещать некоторые «узкие» и не всегда и не совсем очевидные места языка C++. Что имеется в виду? Я не раз сталкивался, как на собственном примере, так и при общении с другими программистами, со случаями, когда человек был уверен в своей правоте касательно какой-нибудь возможности языка C++ (иногда довольно длительное время), а в последствие оказывалось, что он глубоко и бесповоротно заблуждался по одному Богу известным причинам.

А причины на самом деле не такие уж и сверхъестественные. Зачастую свою роль играет человеческий фактор. К примеру, прочитав какую-либо книгу для начинающих, в которых, как известно, многие нюансы не объясняются, а иногда даже не упоминаются, дабы упростить восприятие основ языка, читатель додумывает недостающие вещи самостоятельно из соображений a la «по-моему, это логично». Отсюда возникают крупицы недопонимания, иногда приводящие к довольно серьезным ошибкам, ну, а в большинстве случаев просто мешающие успешному прохождению различного рода олимпиад по С++ :)

Итак, миф первый


Как известно, в языке C++ есть возможность объявления подставляемых функций. Это реализуется за счет использования ключевого слова inline. В месте вызова таких функций компилятор сгенерирует не команду call (с предварительным занесением параметров в стек), а просто скопирует тело функции на место вызова с подстановкой соответствующих параметров «по месту» (в случае методов класса компилятор также подставит необходимый адрес this там, где он используется). Естественно inline — это всего лишь рекомендация компилятору, а не приказ, однако в случае, если функция не слишком сложная (достаточно субъективное понятие) и в коде не производятся операции типа взятия адреса функции etc., то скорее всего компилятор поступит именно так, как того ожидает программист.

Подставляемая функция объявляется достаточно просто:

inline void foo(int & _i)
{
  _i++;
}


Но речь сейчас не об этом. Мы рассмотрим использование подставляемых методов класса. И начнем с небольшого примера, по вине которого и может возникать данный миф.

Все вы знаете, что определения методов класса можно писать как снаружи класса, так и внутри, и подставляемые функции здесь не исключение. Притом функции, определенные прямо внутри класса, автоматически становятся подставляемыми и ключевое слово inline в таком случае излишне. Рассмотрим пример (использую struct вместо class только для того чтобы не писать public):

// InlineTest.cpp

#include <cstdlib>
#include <iostream>

struct A
{
  inline void foo() { std::cout << "A::foo()" << std::endl; }
};

struct B
{
  inline void foo();
};

void B::foo()
{
  std::cout << "B::foo()" << std::endl;
}

int main()
{
  A a; B b;
  a.foo();
  b.foo();
  return EXIT_SUCCESS;
}


В данном примере все отлично, и на экране мы видим заветные строки:

A::foo()
B::foo()

Причем компилятор действительно подставил тела методов в места их вызовов.

Наконец-то мы подобрались к сути сегодняшней статьи. Проблемы начинаются в тот момент, когда мы (соблюдая «хороший стиль программирования») разделяем класс на cpp- и h-файлы:

// A.h

#ifndef _A_H_
#define _A_H_

class A
{
public:
  inline void foo();
};

#endif // _A_H_


// A.cpp

#include "A.h"

#include <iostream>

void A::foo()
{
  std::cout << "A::foo()" << std::endl;
}


// main.cpp

#include <cstdlib>
#include <iostream>
#include "A.h"

int main()
{
  A a;
  a.foo();

  return EXIT_SUCCESS;
}


На стадии линковки получаем ошибку вроде такой (зависит от компилятора — у меня MSVC):

main.obj: error LNK2001: unresolved external symbol «public: void __thiscall A::foo (void)» (? foo@A@@QAEXXZ)

Почему?! Всё достаточно просто: определение подставляемого метода и её вызов находятся в разных единицах трансляции! Не совсем уверен, как именно это устроено внутренне, но я вижу эту проблему так:

если бы это был обычный метод, то в единице трансляции main.obj компилятор бы поставил нечто вроде call XXXXX, а позже уже компоновщик заменил бы XXXXX на конкретный адрес метода A::foo() из единицы трансляции A.obj (конечно же, я всё упростил, но суть не меняется).

В нашем же случае мы имеем дело с inline-методом, то есть вместо вызова компилятор должен подставить непосредственно текст метода. Так как определение находится в другой единице трансляции, компилятор оставляет эту ситуацию на попечение компоновщика. Здесь есть два момента: во-первых, «сколько места должен оставить компилятор для подстановки тела метода?», а во-вторых, в единице трансляции A.obj метод A::foo() нигде не используется, причем метод объявлен как inline (а значит там, где нужно было, компилятор должен был скопировать тело метода), поэтому отдельная скомпилированная версия этого метода в итоговый объектный файл не попадает вообще.

В подтверждение пункта 2 приведу немного дополненный пример:

// A.h

#ifndef _A_H_
#define _A_H_

class A
{
public:
  inline void foo();
  void bar();
};

#endif // _A_H_


// A.cpp

#include "A.h"

#include <iostream>

void A::foo()
{
  std::cout << "A::foo()" << std::endl;
}

void A::bar()
{
  std::cout << "A::bar()" << std::endl;
  foo();
}


// main.cpp

#include <cstdlib>
#include <iostream>
#include "A.h"

int main()
{
  A a;
  a.foo();

  return EXIT_SUCCESS;
}


Теперь всё работает, как и должно, благодаря тому, что inline-метод A::foo() вызывается в неподставляемом методе A::bar(). Если взглянуть на ассемблерный код итогового бинарника, можно увидеть, что, как и раньше, отдельной скомпилированной версии метода foo() нет (то есть у метода нет своего адреса), а тело метода скопировано непосредственно в места вызова.

Как выйти из этой ситуации? Очень просто: подставляемые методы нужно определять непосредственно в header-файле (не обязательно внутри объявления класса). При этом ошибки повторного определения не возникает, так как компилятор говорит компоновщику игнорировать ошибки ODR (One Definition Rule), а компоновщик в свою очередь оставляет только одно определение в результирующем бинарном файле.

Заключение


Надеюсь, хоть кому-то моя первая статья станет полезной и чуточку поможет достигнуть полного осознания такого странного и местами противоречивого, но, безусловно, интересного языка программирования, как C++. Успехов:)

UPD. В процессе общения с gribozavr была выявлена некоторая неточность касательно ODR в моей статье. Выделил курсивом.
Сергей Оленда́ренко @GooRoo
карма
105,2
рейтинг 0,0
Самое читаемое Разработка

Комментарии (165)

  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Скоро будет еще одна, немного попроще. А потом еще ;)
  • +3
    Данная проблема описана у Страуструпа.
    Объявление и определение встроенной ф-ции должны находится в одной области видимости.

    "(то есть у метода нет своего адреса), а тело метода скопировано непосредственно в места вызова."
    Нет, ф-ция будет иметь адрес.

    «Очень просто: подставляемые методы нужно определять непосредственно в header-файле (не обязательно внутри объявления класса). При этом ошибки повторного определения не возникает, так как компилятор говорит компоновщику игнорировать ODR»
    Тут небудет двойного определения, вы в объявлении класса объявите inline метод, а потом определите его. Все по чесному, никаких отключений проверок.

    • –1
      > Нет, ф-ция будет иметь адрес.
      Если у нас где-нибудь будет выполняться операция взятия адреса данной функции, компилятор проигнорирует ключевое слово inline и скомпилирует функцию в обычной форме.

      >Тут небудет двойного определения, вы в объявлении класса объявите inline метод, а потом определите его. Все по чесному, никаких отключений проверок.
      Допустим, у нас есть хедер A.h, в котором объявлен класс A с inline-методом, определение которого находится в том же хедере.
      Этот хедер мы инклудим в файлы B.cpp и C.cpp, которые представляют собой отдельные единицы трансляции и на стадии компиляции образовывают отдельные объектные файлы.
      При сборке оба файла попадают в бинарник, а значит там будет двойное определение. Или я что-то неправильно понимаю?
      • +1
        По стандарту адрес будет в любом случае. А формат ф-ции(встроит ее компилятор или нет) будет зависеть от настроек этого самого компилятора. Вобще современные компиляторы и компоновщики дело темное и зачастую они игнорируют указания программиста.

        Два определения приемлимы если:
        1. Находятся в разных единицах трансляции.
        2. Они одинаковы.
        3. Значение лексем одинаково.

        В случае А, В и С все три пункта сходятся, значит ODR выполняется.

        • 0
          Сорри, ответ на Ваш коммент вот
  • +2
    > «По стандарту адрес будет в любом случае. А формат ф-ции(встроит ее компилятор или нет) будет зависеть от настроек этого самого компилятора. Вобще современные компиляторы и компоновщики дело темное и зачастую они игнорируют указания программиста».

    Компилируем в MS Visual Studio c ключом /Ob1 (это Inline Function Expansion: Only __inline) в релизной конфигурации следующий код:
    // A.h

    #ifndef _A_H_
    #define _A_H_

    #include <iostream>

    class A
    {
    public:
      inline void foo();
    };

    void A::foo()
    {
      std::cout << "A::foo()" << std::endl;
    }

    // main.cpp

    #include <cstdlib>
    #include <iostream>
    #include "A.h"

    int main()
    {
      A a;
      a.foo();
      return EXIT_SUCCESS;
    }


    Смотрим Disassembly:
    int main()
    {
      A a;
      a.foo();

    00401000 mov ecx,dword ptr [__imp_stlp_std::cout (4020ACh)]
    00401006 push esi
    00401007 push offset string "A::foo()" (402114h)
    0040100C mov esi,ecx
    0040100E call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::_M_put_nowiden (4020B0h)]
    00401014 push offset stlp_std::endl<char,stlp_std::char_traits<char> > (401030h)
    00401019 mov ecx,esi
    0040101B call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::operator<< (4020A4h)]


    Inline-метод был подставлен на место вызова. Ни кода метода, ни вообще класса в бинарнике нет.

    Отредактируем main.cpp:
    // main.cpp

    #include <cstdlib>
    #include <iostream>
    #include "A.h"

    int main()
    {
      A a;
      a.foo();

      void (A::*pF)() = &A::foo; // берем адрес подставляемого метода
      (a.*pF)(); // используем

      return EXIT_SUCCESS;
    }


    Снова смотрим Disassembly:
    int main()
    {
    00401030 push ecx
      A a;
      a.foo();

    00401031 mov ecx,dword ptr [__imp_stlp_std::cout (4020ACh)]
    00401037 push esi
    00401038 push offset string "A::foo()" (402114h)
    0040103D mov esi,ecx
    0040103F call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::_M_put_nowiden (4020B0h)]
    00401045 push offset stlp_std::endl<char,stlp_std::char_traits<char> > (401060h)
    0040104A mov ecx,esi
    0040104C call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::operator<< (4020A4h)]


    void (A::*pF)() = &A::foo;
      (a.*pF)();


    00401052 lea ecx,[esp+7]
    00401056 call A::foo (401000h)


    В месте вызова метода по-прежнему была подстановка. НО! Там, где мы получаем адрес метода и используем его, стоит call. По этому адресу появилось вот что:

    void A::foo()
    {
      std::cout << "A::foo()" << std::endl;

    00401000 mov ecx,dword ptr [__imp_stlp_std::cout (4020ACh)]
    00401006 push esi
    00401007 push offset string "A::foo()" (402114h)
    0040100C mov esi,ecx
    0040100E call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::_M_put_nowiden (4020B0h)]
    00401014 push offset stlp_std::endl<char,stlp_std::char_traits<char> > (401060h)
    00401019 mov ecx,esi
    0040101B call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::operator<< (4020A4h)]
    00401021 pop esi

    }

    То есть, тело метода все-таки было сгенерировано отдельно!

    > «Два определения приемлимы если…»
    Но ведь определения все-таки два! ;) Сойдемся на том, что конфликта здесь не возникает.

  • НЛО прилетело и опубликовало эту надпись здесь
  • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
    • +3
      Там это есть, 9.3/2:

      A member function may be defined (8.4) in its class definition, in which case it is an inline member function (7.1.2), or it may be defined outside of its class definition if it has already been declared but not defined in its class definition.
  • 0
    По-моему скоро ситуация с популярностью программирования на C++ намного улучшиться из-за того, что Qt будет набирать популярность. Спасибо за статью. Продолжайте в том же духе
    • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        Java и без того больше некуда набирать популярность :)
    • 0
      До статей о программировании с использованием Qt я еще тоже доберусь ;) Спасибо за поддержку.
  • –3
    Заглловочные файлы и необходимость определния функций до ее использования — одна из неудобностей языка C/C++, какой-то дикий пережиток прошлого.
    • 0
      За что минус? В чём он неправ?
      • 0
        Он просто не имеет их готовить :) У каждого языка своя ниша и парадигма, нужно использовать их по назначению и при необходимости совмещать — тогда всё будет в шоколаде.
        • +1
          Я согласен, что можно описать вагон того полезного, что можно сделать при помощи хидеров. Но в повседневной практике они только мешают.
          Я не против хидеров, только хотелось бы видеть их опциональными: встроенный #include в C# никому не помешает, лишь наоборот (орать «препроцессор зло» все горазды, но стандарт C++ сам же и говорит, что если тебе что-то не нужно, то не используй это, никто не заставляет).
          • 0
            А мне вот в Java препроцессора не хватает :-)
            • 0
              Юзай FMPP
        • 0
          Нет, это однозначно недостаток.

          1) необходимость писать вещи вроде

          #if! defined PATH_TO_FILE
          #define PATH_TO_FILE

          это изврат. В других языках есть вещи вроде require_once(), намного удобнее

          2) писать руками header-файлы неинтересно, а полноцеенный скрипт для их генерации с ходу не написать. это минус. Их должен генерировать компилятор, если они конечно вообще кому-то нужны.

          3) ну и дурацкиое правило, что функция должна быть определена, перед использованием — мне непонятно. Им что, лень добавить лишний проход компилятора, лучше пусть программист мучается?
          • 0
            Им что, лень добавить лишний проход компилятора, лучше пусть программист мучается?

            Во-первых компиляция и так не летает, а во-вторых, сдаётся мне, существует пример, в котором без предварительного объявления выбрать верный путь разбора будет невозможно, хотя я его привести не возьмусь.
            • 0
              По поводу не летает — компилятор может создавать .h файлы, кешируя таким образом хедер без необходимости создавать повторно. Ну и использование предкомпилированных хедеров может еще ускорить этот процесс.

              > в котором без предварительного объявления выбрать верный путь разбора будет невозможно

              сильно сомневаюсь
          • +1
            1) #pragma once?

            2) Генерировать это умеют и IDE. Хотя бы MSVS для MFC-проектов. А причём здесь компилятор?

            3) Понимаете, программы на C/C++ бывают ОЧЕНЬ большие. Мой проект имеет исходников на 400Mb. И это далеко не предел, это так, «программулинка». Полный ребилд длится минут 15. Если сюда ещё добавить второй проход компилятора (просто потому, что программисту лениво располагать объявление функции до её использования) я повешусь :)
            • 0
              1) насколько я помню, #pragma once не стандартизирована…

              2) согласен

              3) Это правда. Мне приходилось сталкиваться с проектом, который минут 20-25 собирался распределенно на десятке не самых слабых компов.
              • 0
                #pragma once да, нестандартизована, тут не спорю. Я эту директиву упомянул лишь для того, чтобы показать, что C/C++ не такой тупой, древний и не развивающийся, как некоторым кажется. ;)

                Хотя сам я делаю классически через #ifndef — у меня руки не отсохнут :)
            • 0
              1) я бы предпочеk #require_once, определять необходимость повторного инклюда в месте вызова
              2) Что за ерунда? Я не хочу ставить IDE, что без GUI-приблудины уже и заголовки не сгенерировать? Писать код мышкоя я отнюдь не собирался.

              Да и к тому же придется их через makefile или руками перегенерировать при изменении основных файлов

              3) Это не аргумент. Лучше пусть железка трудится, чем программист возится, делая лишнюю работу и руками прописывая определение функций.

              По вашему случаю — могу предположить, наверно, надо было как-то разбить проект на слабосвязанные модули, а не валить все в кучу.
              • +1
                > «По вашему случаю — могу предположить, наверно, надо было как-то разбить проект на слабосвязанные модули, а не валить все в кучу».

                Наверное, Вы себе слабо представляете, что означает действительно большой проект ;)
              • 0
                Попробуйте сгенерировать static версию Qt со static библиотекой MySQL и поддерживать всё это в актуальном состоянии.
                Думаю без хедеров вы взвоете в скором времени :)
    • –4
      С++ сам по себе пережиток прошлого, чем скорее на нем перестанут писать — тем лучше. Для любой задачи он — не пришей кобыле хвост.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Зависит от задачи. Для системного программирования — Си, для массового прикладного под винду есть C# и жаба, для прикладного со сложной логикой можно писать eDSL на LISP с транслятором в подмножество Си и «склейкой» с помощью все того же Си.
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              Нет, он простой и удобный, в отличие от Си++
              • НЛО прилетело и опубликовало эту надпись здесь
                • +1
                  > Но он динамический, в отличии от С++, значит у него другая ниша.

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

                  > К тому-же ему 50 лет :-P

                  Современные языки только сейчас начинают получать фичи, которые были в лиспе десятки лет назад. Фактически, каждый новый мейнстримовый язык становится все больше похож на лисп.
          • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            Плохая идея, я обхожу строной программы на Java или .NET (из-за проблем с производительностью и памятью)
            • 0
              В приложениях, которые обычно пишутся на этих языках куда важнее скорость разработки, чем производительность.
              Скажи, какого типа приложения пишешь?
              • –4
                Я вообще на PHP пишу (там тоже быстро))), а Java/.NET может и приемлемо работают, когда одна программа запущена, но у меня такого не бывает. И еще они заупскаются немгновено (а иногда очень немгновенно) и это неприемлемо.

                Еще у .NET идиоты-разработчики, людям приходится держать на компьютрере несколько версий фреймворка одновременно, да еще и скачивать иногда надо его отдельно.

                А у java-программ дурацкая иерархия папок вроде com/somesite/someprogram/… Неужели нельзя скомпилировать java в один файл на ассемблере?

                В общем, одни недостатки. А уж если java- или NET программа уйдет в своп, за пару секунлд она оттуда не вылезет.
                • НЛО прилетело и опубликовало эту надпись здесь
                  • –2
                    > Один из них — использование специально подготовленных данных базовой библиотеки, который сокращает время запуска приложения, и потребления памяти при запуске нескольких.

                    Упаси боже запускать одновременно несколько ява-программ!

                    > Это разделение на пакеты, модульность. Много файлов можно сохранить в один, архив (с расширением .jar).

                    Мне попадались программы (Zend Studio), которые устраивают помойку из папок и файлов, прямо таки линукс в миниатюре (bin, doc, share и так далее). Кстати, использование jar ведь замедляет работу программы и увеличивает расход памяти, так rкак 1) нужно вреям на распаковку 2) файл из архива приходится загружать в память, а не просто mmap()'ить.

                    > А здесь нет разницы, системе все равно, что доставать из свопа, или java, или win32 приложение.

                    Лукавите. Приложение на c++, хорошо написанное займет намного меньше памяти и быстрее вылезет из свопа. Например программа serviwin от руссиновича для запуска/останова служб запускается быстрее (мгновенно) и работает лучше даже чем встроенная в Windows консоль управления. А если бы она была написана на java, мгновенног озапуска можно было бы не ждать. И то же с многими другими программами.
                    • НЛО прилетело и опубликовало эту надпись здесь
                      • –1
                        Ну видимо java-приложения разные бывают, может мне просто кое-как сделанные попадались. Но «быстро грузится» для меня значит — 1-2 секунды на неновом железе и мгновенно на современном, если что. Показывать заставку при запуске и стартовать по полминуты-минуте — это было модно в 95 году, но никак не сегодня.

                        > для стандартной библиотеки JVM готовит спецфайл для mmap (называется classes.jsa), что бы можно было экономить на времени загрузки и памяти при запуске нескольких приложений

                        Неужели тогда нельзя для любого приложения было сделать один большой файл, чтобы отобразить его в память? И в таком виде поставлять. Чувствую здесь какой-то подвох.

                        > Памяти Serna израсходовало раза в 3 больше.

                        Видимо, из-за Qt. У меня Qt-приложения вроде Qt designer тоже не очень работают.

                        И все же мне не нравится современная тенденция делать монстрообразные программы, которые едят 100, 200 и даже больше Мб на не очень сложных задачах (например Amarok, хехе).
                        • НЛО прилетело и опубликовало эту надпись здесь
                          • 0
                            > Файлы классов — это лишь промежуточная форма кода/данных перед их адаптации к аппаратной платформе.

                            Ну так ё, перед тем как выпускать программу, адаптируйте ее к аппаратной платформе, откомпилируйте и поставляйте оптимизированнный бинарный код.

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

                            Мне и 95% пользователей это нафиг не нужно. Кстати, код C-программы тоже может располагаться на удалеенном сервере, и даже (если напистаь загрузчик) в БД, но смысла этом я не вижу.
                            • НЛО прилетело и опубликовало эту надпись здесь
                      • 0
                        И я все же вам не верю. У меня постоянно запущено минимум 10 программ (лень закрывать а потом запускать) — и если бы это были Java-/или NET-программы, все бы притормаживало. Для java-программы откушать 100-200 Мб-милое дело, да и освобождать она их никогда не спешит, там же система выделения памяти довольно-таки странная, память не освобождается сразу, а с помощью GC — а это занчит подтормаживания в случайный момент времни (Опера тоже кстати похоже этим страдает, хехе). Да и те оптимизации. которые вы упоминаете, вряд ли все разработчики используют.
                        • НЛО прилетело и опубликовало эту надпись здесь
                          • 0
                            И вы считаете номальным 17 Мб на helloWorld? Что там такого есть на 17 Мб? 17 миллионов классов что ли? О чем это может говорить, если не о том, что вопросы оптимизации при разработке в расчет просто не принимались? А, подозреваю просто ставились цели как можно быстрее выпустить продукт и захватить рынок.

                            Я сейчас померял HelloWorld на php под Windows — 6Мб Private Bytes. PHP, у которого более сложное устройство (например там нет строгой типизации, значит он должен быть еще менее эффективен) — и тот ест меньше памяти.

                            > а не тратить время попусту на их освобождение (изыскивания резервов).

                            А неужели вызов функции free() или аналогичной так уж ресурсоемок? Всего то, переписать пару указателей, пометить память как занятую и поместить в соотв. список.

                            > GC позволяет эффективнее использовать ресурсы.

                            Вы уверены? Может он просто позволяет не заморачиваться с ручным освобождением памяти? И я сильно сомневаюсь насчет эффективности. Перебор всех объектов (а их могут быть миллионы, не так ли?) с поиском неиспользуемых никак нельзя назвать эффективным (по моему).

                            • НЛО прилетело и опубликовало эту надпись здесь
                • +1
                  > Я вообще на PHP пишу (там тоже быстро))), а Java/.NET может и приемлемо работают, когда одна программа запущена, но у меня такого не бывает. И еще они заупскаются немгновено (а иногда очень немгновенно) и это неприемлемо.

                  Процессор и память стоят дешевле, чем программист.
                  В задачах, характерных для этих языков производительность вторична.

                  > А у java-программ дурацкая иерархия папок вроде com/somesite/someprogram/…

                  Модульный подход намного логичнее, чем тонны Сишных/ПХПшных инклюдов.

                  > Неужели нельзя скомпилировать java в один файл на ассемблере?

                  Во-первых, Джава компилируется в байт-код.
                  Во-вторых, джавовская система импорта позволяет импортировать отдельные объекты пакета, а не весь пакет целиком. А в Си — даже понятия «пакет» нет, есть только куча файлов, объединенных по принципу, известному лишь программисту.

                  • 0
                    > Процессор и память стоят дешевле, чем программист.

                    А студент стоит дешевле нормального разработчика (хотя нормальным такого разработчика-халявщика не назовешь). Наберем студентов?

                    Не путайте, эту фразу говорят про серверные приложения, где разработчик оплачивает серверное железо. С компьютерами пользователей ситуация другая.

                    > А в Си — даже понятия «пакет» нет, есть только куча файлов, объединенных по принципу, известному лишь программисту.

                    А зачем нужны пакеты? А сложности с инклюдами — это недостаток Си, согласен все эти хедеры лишняя муть. Зато у Явы другой недостаток — принцип формирования имени пакета и пути к нему через тонну подпапок жестко прописан. И все эти com.sun.java… — выглядят коряво, лишние сущности.

          • +2
            Вы когда-нибудь писали на C99? Скажу я Вам — это ад! Есть подмножество отличий C от C++, которые просто необходимы для написания понятного maintainable кода. Это, например, namespace, определение переменных в любом месте кода, классы, шаблоны (возможно забыл что-то еще).
            • –3
              > Вы когда-нибудь писали на C99? Скажу я Вам — это ад!

              Во избежание геморроя использую GNU89, ибо в GCC C99 реализован не полностью.

              > Есть подмножество отличий C от C++, которые просто необходимы для написания понятного maintainable кода. Это, например, namespace, определение переменных в любом месте кода, классы, шаблоны (возможно забыл что-то еще).

              C++ адски сложен и ограничен. Я на нем бросил писать еще в на втором курсе универа.
        • –5
          C++ с большим количеством синтаксического сахара.

          вещи вроде x = y+z или object->method() на любом языке вынглядят почти одинаково, но на C++ они выполняются куда как быстрее. А вот класс написать  — на C++ руки отвалятся пока хедер напишешь.
          • +1
            Сколько классы пишу, руки еще не отвалились :)
            • –2
              А мне вот было бы лень, после изучения php мне C++ перестал нравиться. Он очень даже «неслдакий» по моему. И писать например заголовок метода дважды (определение, а потом объявление) — на мой взгляд излишний труд, лишние буквы печатать, а это я не люблю))
              • НЛО прилетело и опубликовало эту надпись здесь
                • 0
                  Вы лукавите. Я имел в виду, то что .h файлы почти всегда можно сгенерировать из .cpp файлов, это может сделать железяка, нет смысла тратить на это время живого человека.
                  • НЛО прилетело и опубликовало эту надпись здесь
                    • 0
                      Можно пример непростого случая?
                      • НЛО прилетело и опубликовало эту надпись здесь
                      • НЛО прилетело и опубликовало эту надпись здесь
                        • 0
                          Вы хитрите, вы спрятали куда-то все объявление полей класса! Конечно, чтобы отменить хедеры, часть информации придется перенести в .cpp файл.

                          Получится примерно так:

                          class A extends A_parent {
                          SomeType x;
                          void show() {
                          … тут код
                          }
                          }

                          Всяко лучше чем 2 файла писать! меня например раздражает необходимость писать заголовок метода или функции по 2 раза, в объявлении или определении, прямо как будто Страуструп решил поиздеваться над программистом! И еще злит то, что надо функции определять/объявлять до их использования, а ведь удобнее например написать сначала main() а потом те функции которые из нее вызываются — а вот хрен.

                          p.s. И еще я сейчас подумал — можно было бы убрать инклюды, сделать вместо них файл autoload.conf и подключать нужные файлы компилятором автоматически, при использовании какого-то класса автоматически подключается его определение.

                          Возможно, раньше это сложно было бы реализовать из-за слабого железа, или (что более вероятно) из-за бездумности создатлей языка, но сейчас давно можно было бы реализовать то, что я написал. Только вот никто этого не сделает, так как Си++ уже забросили. Жаль.
                          • 0
                            Modules in C++ — предложение Вандервуда, увы, не прошедшее в C++09.
                            • 0
                              Хорошая идея, я бы только добавил автоматический импорт по определенным правилам, к примеру при использовании какого-то класса — можно было бы сэкономить еще и на написании директив import
                          • НЛО прилетело и опубликовало эту надпись здесь
                            • –2
                              Не, это не аргумент. Чтобы быстрее компилировалось, можно использовать различные кеширования. прекомпилированные хедеры, и так далее. Тем более что компьютеры становятся только мощнее.
                              А вот писать избыточный код — неинтересно. Мне кажется время программиста тут более важный фактор.
                • 0
                  > Для PHP это не нужно, т.к. там используется связывание в момент использования [метода]/исполнения

                  В 99,999% случаев понять, какая функция будет вызвана, можно в момент парсинга программы, так как 1) как правило в приложении нет 2 функций с одинаковым именем и 2) почти все функции вызываются по имени, которое явно проиписано, а не берется из переменной или еще откуда-то. На самом деле, связывать в момент компиляции скрипта было бы грамотнее, так как позднее связывание в основном никому не нужно, а вот производительность подрывает. Глупо.

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

                А мне вот лень тянуть скроллбар в больших классах в то время, когда мне просто нужно посмотреть интерфейс.
                • 0
                  Нельзя, они же будут инлайновыми.

                  > А мне вот лень тянуть скроллбар в больших классах в то время, когда мне просто нужно посмотреть интерфейс.

                  Я слышал. в современных IDE тянуть скроллбар никуда не надо. Список методов обычно выводится слева от кода.

                  И еще, я же не предлагал полностью отменить .h файлы, их можно генерировать компилятором из файлов с кодом, если они нужны например для создания SDK.
                  • 0
                    > «Нельзя, они же будут инлайновыми».

                    Как показывает Disassembly, тот же компилятор MSVC с настройками по умолчанию (без ключа /Ob1) встраивает не только inline-функции, но и другие, которые сам посчитает нужным встроить. Так что Вы зря переживаете по этому поводу.

                    > «Я слышал. в современных IDE тянуть скроллбар никуда не надо. Список методов обычно выводится слева от кода».

                    Хм, странно, у меня в notepad.exe ничего подобного нет…
                    • –1
                      А notepad можно назвать IDE?)) В любом случае сомнительная возможность пользователям блокнота постмотреть header-файл не стоит лишнего труда. .h файлы должны генерирорваться без участия человека.

                      Ну и объявлять функции в коде класа глупо  — они будут продублированы в нескольких объектных модулях, я бы не хотел делать такое фуфло, где есть много повторяющегося кода. Одно дело — маленькие функции, другое — полноценнные.

                      • 0
                        Я более чем уверен, что если в коде функции будет к примеру хоть один цикл, то компилятор не станет делать её подставляемой, так как в этом случае издержки на занесение параметров в стек и вызов функции командой call будут уступать выполнению самой функции (цикл породит гораздо более сложный asm-код). Посему не думаю, что эта функция будет многократно скопирована.
                        • –2
                          Представим что есть файл class1.cpp с классом Class1 и определенной в нем функцией весом килобайт в 50. этот файл инклудится допустим из 10 других файлов. Вопрос: в какой объектный модуль будет помещен код функции? Я думаю, во все 10 + в class1.o

                          > Посему не думаю, что эта функция будет многократно скопирована.

                          Гм, а вы представляете как присходит компиляция и сборка программы?
                          • 0
                            Если функция у нас «килобайт в 50», то компилятор явно не станет копировать код в места вызова. А значит по какому-то адресу он поместит её код, а в месте вызова поставит call. Да, код функции будет повторяться во всех объектных файлах, где она юзается, но компоновщик на стадии линковки оставит в бинарнике только одну версию этога метода, о чем упомянуто выше в моей статье.
                            • 0
                              Проблема в том что не все компиляторы поддержат такой хитрый трюк, например gcc может и не поддержать, следовательно все таки много кода в заголовок класса совать не стоит.
                              • 0
                                Я писал, что сложность функции — довольно субъективное понятие. И если GCC не может с этим справиться, то наверное это его проблемы… Другие ведь могут!
      • 0
        На чем предлагаете писать desktop-игры к примеру?
        • НЛО прилетело и опубликовало эту надпись здесь
        • –3
          Главная причина использования Си++ в геймдеве — большое количество относительно дешевых кодеров и тот факт, что основная «игровая» ОС — Windows вовсю использует этот язык.
          Разработчики Unreal, например, присматриваются к Haskell:
          www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/sweeny.pdf
          • НЛО прилетело и опубликовало эту надпись здесь
            • –2
              Разработчик, знающий/применяющий только один язык — хреновый разработчик. Настоящий профессионал должен знать пару десятков языков различных парадигм и уметь самому создавать языки под задачу, только тогда он может правильно выбрать себе инструмент (предвидя вопрос про себя скажу, что я столько не знаю, но к этому стремлюсь).
              Если бы нужна была именно скорость — использовали бы Си, он все равно быстрее.
              • 0
                Ну да, чего уж там? Давайте учить пару сотен языков… Если попытаетесь знать все языки, в совершенстве не будете знать ни один. Есть люди, такие как Вы, которые расширяют свои познания, а есть, которые углубляют. Нужно найти «золотую середину»!

                А главное — упор нужно делать не на языки, а на алгоритмы.
                • 0
                  > Ну да, чего уж там? Давайте учить пару сотен языков…

                  Это лишне.

                  > Если попытаетесь знать все языки, в совершенстве не будете знать ни один.

                  Есть вариант, что «в совершенстве» (кстати, что под этим понимается?) их знать и не надо будет: то, что долго и неудобно писать на одном языке почти наверняка просто написать на другом. А склеить все можно с помощью трансляторов и FFI.

                  > А главное — упор нужно делать не на языки, а на алгоритмы.

                  Алгоритм мало придумать — его надо реализовать. И желательно, чтобы реализация тратила минимум времени и средств. Поэтому — каждой задаче либо классу задач нужно искать свой инструмент, а если понадобиться — написать его самому.
                  • 0
                    > «…„в совершенстве” (кстати, что под этим понимается?)…»

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

                    > «Алгоритм мало придумать — его надо реализовать. И желательно, чтобы реализация тратила минимум времени и средств».

                    Что-то в этом есть, однако без хорошего понимания алгоритмизации никакое знание языков вам не поможет — все программы будут медленными (будь они написаны на С++ или, к примеру, на PHP :)
                    • 0
                      > Что-то в этом есть, однако без хорошего понимания алгоритмизации никакое знание языков вам не поможет — все программы будут медленными (будь они написаны на С++ или, к примеру, на PHP :)

                      Я с этим не спорю. Однако знание нескольких языков позволяет максимально полно использовать их сильные стороны, избегая слабых.
                      Плюс, алгоритмы могут находиться на разных уровнях в контексте решаемой задачи: идеальный вариант — решать прикладную задачу в терминах самой задачи, а Си++ слишком низкоуровневый и слишком ограниченный, чтобы это было возможно на нем.
              • –3
                Вы представляете себе разницу между C и С++? Она не так уж велика. На C++ получается примерно такая же производжительность, если не увлекаться херней вроде STL.

                Более того, когда пишешь на C, постоянно создается ощущение что пытаешься с помощью структур и функций имитировать объект… только вот зачем имитировать если можно взять C++ и написать нормальным образом?
                • НЛО прилетело и опубликовало эту надпись здесь
                  • –2
                    А если мы сделаем вектор из объектов?)) Да еще и сложноустроенных? А как он копирует объекты или строки — физическим созданием копии строки в памяти или через подсчет ссылок? По моему так авторы там не заморачивались с оптимизациями.

                    И еще я слышал активное использование шаблонов генерирует много лишнего кода. Напримерю автора библиотеки ptypes недостатки STL побудили написать свою библиотеку.

                    И еще STL не очень то синтаксически сладок, все эти шаблоны, итераторы… с ходу и не разберешься. Хотя конечно может я о нем слишком резко отозвался. Но по моему это тот случай когда в погоне за универсальностью жертвуют всем осталдьным.
                    • НЛО прилетело и опубликовало эту надпись здесь
                      • НЛО прилетело и опубликовало эту надпись здесь
                  • 0
                    Итераторы итераторами, но вообще, если смотреть более широко, STL может вносить заметный оверхед по сравнению с «ручным» кодом. Иначе, например, гейм-девелоперы не изобретали бы ее аналоги для большей производительности.
                • –2
                  > Вы представляете себе разницу между C и С++? Она не так уж велика. На C++ получается примерно такая же производжительность, если не увлекаться херней вроде STL.

                  Разницу очень даже представляю: Си прекрасный «высокоуровневый ассемблер» и идеальный язык для промежуточной трансляции DSL. А Си++ — недоразумение, претендующее на универсальность, но в итоге нормально не делающий ничего. Он слишком ограниченный и низкоуровневый, чтобы использовать его для эффективного решения прикладных задач, и слишком сложный большой, чтобы использовать его для традиционных сишных задач: программирование для микроконтроллеров, написание драйверов и ОС, и другие, требующих прямой работы с железом. Так что разница огромна.

                  • –1
                    И «херней вроде» не увлекаться не получиться, со временем проект обрастает точно такой же херней, только самописной и намного хуже, чем STL отлаженной.

                    > Более того, когда пишешь на C, постоянно создается ощущение что пытаешься с помощью структур и функций имитировать объект… только вот зачем имитировать если можно взять C++ и написать нормальным образом?

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

                    Язык должен быть простым и мощным, а Си++ под эти критерии не походит никак.
                    • 0
                      Слово «гандон», надо полагать, происходит от двух подряд идущих букв грузинского алфавита: соответственно «ган» გ и «дон» დ. Поэтому пишется это слово через «а».

                      А написал я это к тому, что по-моему пора прекратить холивар.
                      • +1
                        > А написал я это к тому, что по-моему пора прекратить холивар.

                        Да нет никакого холивора. Я лишь пытаюсь сказать, что относиться к языку программирования как к объекту религиозного поклонения не стоит, ибо это всего лишь инструмент. Профессионал знать, какие есть инструменты, кроме его любимого, и применять их в соответствии с задачами.
                        • 0
                          Поддерживаю.
                    • +2
                      > Пользы от них никакой — проект неизбежно превращается в лапшу из объектов, который невероятно трудно отлаживать и поддерживать

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

                      А уж если вручную пистаь все malloc()'и — вообще издевательство над человеком, нужно автоматическое управление памятью.
                      • –2
                        > Что за бред? А на Си проект превращается в кашу из структур, функций и глобальных переменных. Убиться можно.

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

                        • 0
                          Неправда. Разница есть.
                          • 0
                            Фундаментально проблемы одни и те же, Си++ лишь повышает «порог нечувствительности» к этим проблемам.
                  • +2
                    > А Си++ — недоразумение, претендующее на универсальность, но в итоге нормально не делающий ничего.

                    Повторюсь: сколько я не пытался написать что-то на Си — всегда получались структуры и набор функций для работы с ними — то есть объекты — но с ними удобнее работаьт в Си++. Не понимаю, что удобного в Си. Или вы фанат глобальных переменных и ручной работы с указателями и ручного управления памятью? Хотя в драйверах наверно выгоднее действительно этим заниматься вручную.

                    Возможно, Си++ вызывает неприязнь громоздким синтаксисом, он не без недостатков, но к сожалению нормальной альтернативы никто сделать не может, кругом одни сами знаете кто.
                    • –1
                      > Повторюсь: сколько я не пытался написать что-то на Си — всегда получались структуры и набор функций для работы с ними.

                      Воспринимай Си как ассемблер высокого уровня, писать на нем руками следует только если стоят задачи определенного класса.

                      > то есть объекты — но с ними удобнее работаьт в Си++.

                      По сравнению с CLOS объектная система Си++ ограничена.

                      > Не понимаю, что удобного в Си.

                      Си удобен своей простотой, в него удобно транслировать другие языки, в том числе собственной разработки «под задачу».

                      > Или вы фанат глобальных переменных и ручной работы с указателями и ручного управления памятью? Хотя в драйверах наверно выгоднее действительно этим заниматься вручную.

                      Я как раз за то, чтобы не задумываться о таких вещах, а сосредотачиваться на задаче.

                      > Возможно, Си++ вызывает неприязнь громоздким синтаксисом, он не без недостатков,

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

                      > но к сожалению нормальной альтернативы никто сделать не может, кругом одни сами знаете кто.

                      Альтернативы есть (свои в каждой задаче), просто люди сидят в своем дебильном Си++ и не хотят учить ничего другого.
                      • 0
                        > Альтернативы есть (свои в каждой задаче), просто люди сидят в своем дебильном Си++ и не хотят учить ничего другого.

                        Бррр… писать свой язык под каждую задачу — сомнительное удовольствие.

                        > Самая главная моя претензия к Си++ — невозможность расширить синтаксис.

                        Да, это бы хорошо было. Но я против того чтобы это «расширение» делалось в рантайме, ценой потери производительности.
                        • +1
                          > Бррр… писать свой язык под каждую задачу — сомнительное удовольствие.

                          В этом нет ничего сложного, плюс весь язык целиком писать не нужно: основные управляющие конструкции можно взять из языка, с помощью которого пишется специализириованый.

                          > Да, это бы хорошо было. Но я против того чтобы это «расширение» делалось в рантайме, ценой потери производительности.

                          Производительность — не самое главное. Если язык позволяет сэкономить на разработке время и деньги, то будут использовать его. Факт существования тормозной джавы — показателен.
                          • НЛО прилетело и опубликовало эту надпись здесь
                            • 0
                              Сам глубоко не разбирался, насколько могу сказать из общения с товарищами, причина — в реализации Java-машины, а точнее — компиляции байт-кода в инструкции конкретного железа.
                              Разбираться глубже мне только предстоит, пока это на уровне ОБС.
                          • 0
                            Кстати, почиатл недавно немного про bison  — в принципе создать новый язык (на основе Си) не такая уж и нереальная задача, как кажется
                            • 0
                              С помощью flex&bison не так уж сложно создать и язык с отличающимся синтаксисом. Вопрос в том, нужно ли это, когда существует много разных легковесных скриптовых языков. Взять хотя бы тот же Lua или Squirrel…
                              • 0
                                > С помощью flex&bison не так уж сложно создать и язык с отличающимся синтаксисом.

                                Синтаксис не важен, важна только семантика языка.
                                И, кстати, факт существования препроцессоров — лишнее доказательство слабости языка Си++ как основы для метапрограммирования.

                                > Вопрос в том, нужно ли это, когда существует много разных легковесных скриптовых языков. Взять хотя бы тот же Lua или Squirrel…

                                Писать языки, отличающиеся только синтаксисом — действительно не нужно. И речь шла не о скриптовых, а о полноценных компилируемых языках, чтобы получать от компилятора либо native, либо сишный код.
                                Такой подход, называемый Language Oriented Programming, сокращает количество операций, выполняемых вручную, активно используя генераторы кода и метапрограммирование. Пусть это не всегда применимо, но знать разные подходы к разработке должен каждый, кто хочет работать эффективно.
                                По теме DSL литературы не очень много, недавно нашел описание паттернов LOP:

                                • 0
                                  Факт существования препроцессора доказывает лишь то, что С++ поддерживает много разных парадигм. Никто Вас не заставляет пользоваться препроцессором, равно как и самим С++
                                  • 0
                                    > Факт существования препроцессора доказывает лишь то, что С++ поддерживает много разных парадигм.

                                    Нет, это означает, что его нельзя заточить под свои нужды в приемлимое время.

                                    > Никто Вас не заставляет пользоваться препроцессором, равно как и самим С++

                                    Дело не в моих предпочтениях, а в объективных недостатках языка. Собственно из-за них его и вытесняют более динамичные C# и Java.
                                  • 0
                                    ну, за это конечно спасибо :)
                            • 0
                              Эта задача — простая, математика там — элементарная. Все упирается исключительно в выбор языка, на котором будет написан компилятор. В качестве таких языков обычно выбирают Lisp, OCaml, Ruby, Forth и вот еще на хабре узнал про IO, который обязательно выучу, как только добью Форт и OCaml.
          • 0
            Неправда, главная причина — производительность, Хаскелл намного меленнее C++. Кстати, чуваки в пдф по ссылке считают нормальным 30 фпс — халявщики, в общем.
            • –1
              > Неправда, главная причина — производительность, Хаскелл намного меленнее C++

              А Си — быстрее Си++. Почему не пишут на нем?

              > Кстати, чуваки в пдф по ссылке считают нормальным 30 фпс — халявщики, в общем.

              Эти чуваки — разработчики одного из самых технологически совершенных движков. И 30 ФПС — вполне достаточно, при условии, что они выдаются стабильно.
              • –2
                > «А Си — быстрее Си++. Почему не пишут на нем?»

                Потому что, опять же, С++ — это золотая середина. С одной стороны он позволяет добиться высокой скорости работы программ, а с другой — предоставляет возможность эффективно работать с большими проектами.
                • НЛО прилетело и опубликовало эту надпись здесь
                  • –2
                    В том примекре предельно упрощенный код, на котором действительно есть какая-то оптимизация. Но в реальности, на много раз унаследованных классах со сложной структурой, затраты на компиляцию могут и не окупиться. В реальности мы имеем ява-машину, которая на HelloWolrd программе ест то ли 10 то ли 19 Мб памяти (точно не помню, давно это было).
                    • НЛО прилетело и опубликовало эту надпись здесь
                  • 0
                    Меня не устраивает «достоточно», я хочу скорость «как у C-программ»
                    • НЛО прилетело и опубликовало эту надпись здесь
                      • 0
                        Ну вот и давайте, Вы напишете небольшую трехмерную desktop-игру на Java+C, а я потестирую её на производительность на своем AMD Duron 1200…
                        • 0
                          Могу предложить большую, даже как минимум две:

                          Ил-2 Штурмовик
                          Vampire: The Masquerade – Redemption
                          • 0
                            Да, кстати обе вышли когда ваш Duron был только в проекте.
                            • 0
                              Так вот, я должен заметить, Vampire: The Masquerade шел на моем Duron не лучшим образом, в то время как игры с графикой явно не хуже (от той же EA) у меня отлично работали.

                              Не думаю, что EA пишет игры на Java (с учетом того, что они даже придумали свою версию STL)
                            • 0
                              А вообще, это глупый, холиварный спор.
                        • НЛО прилетело и опубликовало эту надпись здесь
                          • +1
                            В случае с многоядерным процессором ни С/С++, ни Java не сравнятся с Haskell по удобству распараллеливания.
                            • 0
                              Подозреваю, C на 1 ядре будет быстрее хаскелла на 8.
                              • 0
                                Пока что, наверное, да…
                                • 0
                                  Не так всё плохо, зависит от задач. К примеру, парсер Си на Haskell уступил gcc всего в три раза при разборе исходников ядра Linux (пруфлинк) при несравнимом удобстве разработки и дальнейшего использования.
                                  • 0
                                    В три раза — это не так уж и хорошо :(
                                  • +1
                                    Это частная задача, в общем случае думаю результат будет хуже.
                                    • +1
                                      Блин. Не сочтите за наезд, но даже ссылка на всеми пинаемый shootout выглядит убедительнее, чем размышления о некоем сферическом общем случае в вакууме. Для уровня абстракции Hakell отставание от Си в 2–5 раз — это огромное достижение авторов компилятора и рантайма, а резервы оптимизации далеко не исчерпаны.
                                      • 0
                                        Убедил =)
                        • 0
                          Вот! Пусть не игры, но все современные приложения, ОС и прочий софт должны тестироваться и разрабатываться на таком железе, а не на 8-ядерных компьютерах. Чтобы у разработчиков не возникало нездорового соблазна использовать тормозные технологии.
                • 0
                  Поддержка любого большого проекта на Си++ со временем превращается в ад. В принципе, это качается всех императивных языков. Я уже не говорю невероятно сложной отладке.
              • 0
                > И 30 ФПС — вполне достаточно, при условии, что они выдаются стабильно.

                Нифига. лучше столько же фпс, сколько кадров в секунду отображает монитор. Эот разработчики придумывают себе оправдания, чтобы писать быстро а не качественно.
                • –1
                  Сложность поддержки и отладки Си++ кода растет экспоненциально. Когда у тебя тысячи объектов, каждый из которых изменяет состояние любых сосредоточиться на оптимизации кода невозможно — нужно хотя бы самые заметные ошибки зализать.
          • 0
            Насчет Haskell я спорить не буду — скорее всего за функциональным программированием будущее (хотя я, к сожалению, с ним пока что практически не знаком). Но, если я не ошибаюсь, программы на Haskell все еще уступают по скорости аналогичным программам на C++… Я не буду отстаивать эту точку зрения, потому как просто где-то слышал такое, и буду рад, если сейчас это уже не так.
  • +1
    >… по такому мощному, но всё менее использованному языку…

    Не опечатка ли это?
    • 0
      Прошу прощения :)
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Я согласен. Но я такой же начинающий, как и те, для кого я пишу. И я сам по ходу разбираюсь понемногу.

      А вот Вы, как и многие другие, заигнорили мой совет: «Профессионалам … можно дальше не читать» :) Не в обиду.
  • 0
    Я не понял, где тут, собственно, миф то?
    • 0
      «Миф» выделен полужирным в тексте и состоит в том, что все-таки не всегда удается «безболезненно» определить inline-метод вне объявления класса. Эккель, к примеру, пишет, что в отличие от обычных методов, inline-методы нужно определять прямо в хедере, но мой мозг благополучно «пропустил» этот факт и вспомнил о нем лишь тогда, когда столкнулся на практике.
      Для Вас, видимо, это не миф, а многие до сих пор об этом не подозревают.

      А вообще не нужно относиться к слову «миф», как к ключевому. Я об этом писал: «Оно [название], естественно, появилось не случайно, однако и не совсем соответствует сути».
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Да, Вы правы. Но суть в том, что многие не задумываются об этом до тех пор, пока не возникнут проблемы. У довольно большого числа людей психология в духе «зачем мне знать, как это устроено, если все и так работает?».
  • +2
    Если на то пошло, то некоторые авторитетные авторы пишут, что слово inline вообще нигде и никогда использовать не нужно.
    Смысл в том, что любой разумный оптимизатор и сам сделает подстановку тела, если в этом есть смысл.

    Здесь есть прямая аналогия с практически вымершим словом register, которое объявляло переменную как хранимую в регистре процессора.

    С другой стороны, inline может быть полезно как временное решение следующей проблемы.

    Предположим, есть хэдер H. Есть также модули A и В, которые включают в себя этот хэдер. Теперь в силу каких-то причин нам нужно добавить в хэдер какую-нибудь короткую функцию, при этом создавать соответствующий CPP-модуль не хочется (лень :) ).

    Если просто добавить фукнцию, будет ошибка линковки: duplicate definition in A, B.
    Если же фукнцию объявить как инлайновую, то всё отлично скушается.

    Впрочем, можно просто создать безымянное пространство имён. По идее, это должно сработать (но в хэдерах не пробовал).
    • +1
      А вот мы не так давно на каких-то вычислениях (компилятор — gcc) обнаруживали, что волшебное слово register таки повышает скорость вычислений раза в 4. Так что иногда их вставлять всё-таки нужно, чтобы переубедить неразумный компилятор :)
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Нет, на абсолютно реальных.
  • 0
    Статья хорошая, но подобная тема хорошо изжевана у Герба Саттера в «Новых сложных задачах по С++». Неплохо было бы упоминуть об этом.
    • 0
      У вас же в основном тема полностью не раскрыта. Также не сказано о различных типах инлайна (даже во время компиляции). А просто указана типичная ошибка программирования, когда вы инлайните не метод, а его объявления. Конечно, если вызовы будут в разных единицах трансляции — это приведет в большим проблемам :)
    • 0
      Когда прочитаю книгу Герба Саттера, упомяну ;)
  • 0
    «Почему?! Всё достаточно просто: определение подставляемого метода и её вызов находятся в разных единицах трансляции!»

    В корне не верно, единица трансляции — это cpp файл, который был обработан препроцессором и будет передан линковщику. Если вы инклудите A.h в A.cpp, то A.h и A.cpp образуют одну единицу трансляции. Если вы инклудите A.h в B.cpp, то они образуют вторую, отличную от первой единицу трансляции.
    • +1
      Впрочем ниже, вы все правильно расписали. Поспешил :)
  • 0
    А это ещё что за коды??? Хабр испортился или статья?
    Подставляемая функция объявляется достаточно просто:
    
    <font size="2" face="Courier New" color="black"><font color="#0000ff">inline</font> <font color="#0000ff">void</font> foo<font style="color: #008000;">(</font><font color="#0000ff">int</font> <font color="#000040">&</font> _i<font style="color: #008000;">)</font><br /><font style="color: #008000;">{</font><br />  _i<font color="#000040">++</font><font color="#008080">;</font><br /><font style="color: #008000;">}</font></font>
    
    Но речь сейчас не об этом. Мы рассмотрим использование подставляемых методов 
    
    • +1
      Раньше на хабре не было подсветки синтаксиса для кода. Выживали, как могли. Сейчас уже западло менять, учитывая, что ломал не я :)
      • 0
        Хреново. Поменять то не так уж трудно на самом деле, делов-то, пара минут. А кому-то пригодится :)
        Вот даже тут можно исправить, просто вставляя эти коды в поле ввода комментария без обрамляющих тэгов, и на выходе получается красиво:
        Заголовок спойлера
        1.
        inline void foo(int & _i)
        {
        _i++;
        }


        2.
        // InlineTest.cpp

        #include
        #include

        struct A
        {
        inline void foo() { std::cout << «A::foo()» << std::endl; }
        };

        struct B
        {
        inline void foo();
        };

        void B::foo()
        {
        std::cout << «B::foo()» << std::endl;
        }

        int main()
        {
        A a; B b;
        a.foo();
        b.foo();
        return EXIT_SUCCESS;
        }


        3.
        // A.h

        #ifndef _A_H_
        #define _A_H_

        class A
        {
        public:
        inline void foo();
        };

        #endif // _A_H_


        // A.cpp

        #include «A.h»

        #include

        void A::foo()
        {
        std::cout << «A::foo()» << std::endl;
        }


        // main.cpp

        #include
        #include
        #include «A.h»

        int main()
        {
        A a;
        a.foo();

        return EXIT_SUCCESS;
        }


        4.
        // A.h

        #ifndef _A_H_
        #define _A_H_

        class A
        {
        public:
        inline void foo();
        void bar();
        };

        #endif // _A_H_


        // A.cpp

        #include «A.h»

        #include

        void A::foo()
        {
        std::cout << «A::foo()» << std::endl;
        }

        void A::bar()
        {
        std::cout << «A::bar()» << std::endl;
        foo();
        }


        // main.cpp

        #include
        #include
        #include «A.h»

        int main()
        {
        A a;
        a.foo();

        return EXIT_SUCCESS;
        }



        Где администраторы хабра? Они что, слепые?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.