Страуструп: наставления начинающему программисту 5

Posted by Андрей on Июнь 26, 2008

Прочитал интервью Бьерна Страуструпа для австралийского ComputerWorld. В этом интервью ему задают вопрос:

Do you have any advice for up-and-coming programmers?

Мне кажется, ответ на данный вопрос формулирует то самое, к чему должен стремиться любой программист. Итак, далее мой вольный перевод на русский:

Можете что-то посоветовать начинающим программистам?

Изучайте основы программирования: алгоритмы, архитектуру машин, структуры данных и т.д. Не копируйте слепо подходы из одного приложения в другое. Вы всегда должны знать, что вы делаете, быть уверенными, что ваша программа работает, и твёрдо знать, почему она работает. Не думайте, что вы можете предсказать, какой будет индустрия программирования через 5 лет и чем именно придётся заниматься вам, поэтому учитесь более общим и полезным приёмам и подходам. Старайтесь писать код, который лучше, код, который больше соответствует вашим принципам программирования. Работайте так, чтобы программирование в большей степени было профессиональной деятельностью, а не низкоуровневым «хакерством» (программирование — это и ремесло, но не только ремесло). Учитесь на классике в области разработки и с помощью лучших книг, не надо полагаться на «how to» и документацию в онлайне — она недостаточно глубоко затрагивает вопросы программирования.

Как преподавать C++? 8

Posted by Андрей on Март 30, 2008

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

А почему мы заставляем студентов знать подробности языка, которые нельзя продемонстрировать на разумном примере? Например, множественное наследование (я уже молчу о виртуальном наследование в сочетании с множественном). Для этого нужны примеры, большие примеры, огромное количество понятий (например, интерфейсы), чтобы студент почувствовал, что и зачем. А мы с упорством сумасшедших рассказываем о том, какие члены классов будут видны или не видны в той или иной ситуации.

Вот объясните мне, кто-нибудь использовал перегрузку наряду с виртуальностью?

class A
{
public:
    virtual int f();
};
class B : public A
{
public:
    virtual int f(double);
};

Что это нормально, это работает? Зачем нужны эти знания? А кто перегружал функции, расположенные в разных областях видимости?

class A
{
    void f(char);
public:
    void f(int);
};

A a;
a.f('a');

Ну кто такое делает в реальных задачах? Зачем студентов заставлять разбирать эти примеры?

Как я понял из интервью Бьерна Страуструпа, это проблема преподавания Computer Science не только на факультете ВМиК МГУ. Он говорит, кого мы готовим? lanugage lawyers? По-моему, это очень точно английское словосочетание… Мы готовим студентов стать членами комитета ISO по стандартизации C++?

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

Кому это нужно? Самоудовлетворение от задалбливания в умы студентов бесполезных знаний?

Почему мы не экзаменуем их на умение программировать? Ну или хотя бы на знание базовых конструкций языка? Зачем такие извращенные примеры? Если бы я опубликовал весь вариант работы, которую писали студенты, думаю, многие были бы в шоке. Я уверяю, что подавляющее большинство потрясающих, талантливых C++-программистов не смогут решить этот вариант на «пять». Но от этого они не становятся хуже, а претензии надо предъявлять тем, кто составил такую программу курса, предъявляет такие требования к студентам. Как они могут полюбить такой предмет?

По-моему, пора остановиться и сменить ориентиры.

Чудеса С++: разные формы вызова конструктора копирования 1

Posted by Андрей on Февраль 04, 2008

Что происходит, когда мы пишем A a(3), A a = 3 и A a = A(3)? Меня этот вопрос давно интересовал. Конечно, у класса должны быть корректные конструкторы, в том числе и копирования, операторы присваивания и т.п.

Проведем следственный эксперимент…

#include <stdio.h>

class A
{
    public:
    A() 
    {
        printf("A::A()\n");
    }

    A(int) 
    {
        printf("A::A(int)\n");
    }

    A(int, int) 
    {
        printf("A::A(int, int)\n");
    }

    A(const A& a) 
    {
        printf("A::A(const A& a)\n");
    }

    void operator=(const A& a) 
    {
        printf("A::operator=(const A& a)\n");
    }
};

int main()
{
    printf("=> A a1;\n");
    A a1;
    printf("=> A a2(3);\n");
    A a2(3);
    printf("=> A a3;\n");
    A a3 = 3;
    printf("=> A a4 = A(3);\n");
    A a4 = A(3);
    printf("=> A a5; a5 = A(3);\n");
    A a5; a5 = A(3);
    printf("=> A a6(a1);\n");
    A a6(a1);
    printf("=> A a7(4,4);\n");
    A a7(4, 4);
    printf("=> A a8 = A(4,4);\n");
    A a8 = A(4, 4);
    return 0;
}

Выведет на экран:

=> A a1;
A::A()
=> A a2(3);
A::A(int)
=> A a3;
A::A(int)
=> A a4 = A(3);
A::A(int)
=> A a5; a5 = A(3);
A::A()
A::A(int)
A::operator=(const A& a)
=> A a6(a1);
A::A(const A& a)
=> A a7(4,4);
A::A(int, int)
=> A a8 = A(4,4);
A::A(int, int)

Т.е. каждый раз вызывается обычный конструктор и все три формы, приведённые в начале поста, эквивалентны.

Однако стоит нам конструктор копирования объявить private, и сразу же получим вот это:

[smir@smira Teaching]$ g++ test.cxx 
test.cxx: In function ‘int main()’:
test.cxx:22: ошибка: ‘A::A(const A&)’ is private
test.cxx:39: ошибка: в данном контексте
test.cxx:22: ошибка: ‘A::A(const A&)’ is private
test.cxx:39: ошибка: в данном контексте
test.cxx:39: ошибка:   initializing temporary from result of ‘A::A(int)’
test.cxx:22: ошибка: ‘A::A(const A&)’ is private
test.cxx:41: ошибка: в данном контексте
test.cxx:22: ошибка: ‘A::A(const A&)’ is private
test.cxx:41: ошибка: в данном контексте

Строка 22 — это объявление конструктора копирования, а 39 и 41 это обращения A a3 = 3 и A a4 = A(3);. Интересно получается, что хотя конструктор копирования и не вызывается, но эти формы требуют его открытости. Это стандарт C++, оптимизация gcc или что-то ещё?