Форум программистов, компьютерный форум CyberForum.ru

Почему вы избегаете использовать возможности С++11 ? - C++

Войти
Регистрация
Восстановить пароль
Другие темы раздела
C++ реализация IGMP протокола http://www.cyberforum.ru/cpp/thread425890.html
Доброе время суток! подскажите пожалуйста, требуется ли ручками реализовывать IGMP протокол при передаче данных по мультикасту (предполагается использование библиотеки life555). Ещё читал, что IGMP должен кидать клиент свичу\маршрутизатору и т.п. оборудованию с разъяснениями кого он(клиент) хочет слушать. А возможна ли ситуация когда клиент не знает ничего о том, кого он хочет слушать (например...
C++ помогите сделать лабы!!самый простой вариант!!начальный!! массивы и все такое первый курс!!! кому не тяжело вот мой эл.адрес я скину лабы!!очень нужно,народ выручайте!!! Форум - не место обмена аськами, мылами, скайпами и т.д. Выкладывайте задание сюда в приличном и читабельном виде. Если же вы готовы оплатить решение то отпишитесь и я перенесу тему во фриланс. http://www.cyberforum.ru/cpp/thread425593.html
C++ Файловая система на C++
Привет всем))) Ребят, у меня стоит задача - написать свою файловую систему на С++)) Но вот с чего начать, что делать я вообще не знаю)) Если кто знает, что почитать об этом, где почитать и вообще, кто может пояснить что-то по этой теме, прошу отписаться))) Заранее благодарю))
работа с Семафорами C++
Помогите пожалуйста очень срочно На Сях Реализовать работу магазина - отделы, касса, покупатели, с использованием семафоров, учесть вход-выход людей в магазин, возможность открытия других касс
C++ Критическая секция http://www.cyberforum.ru/cpp/thread423404.html
Очень срочно,помогите пожалуйста,на Сях,реализовать работу бензоколонки - бензин 80,92,95,98,Дизель - разлив бензина автомобилям через критическую секцию.Учесть въезд - выезд автомобилей на заправку.
C++ автоматическое нажатие ЛКМ Доброго времени суток! кто сможет помочь? нужно написать программу или скрипт, чтоб имитировать нажатие левой кнопки мыши через каждые 10 секунд в одном и том же месте мыши! заранее благодарю кто может помочь! подробнее

Показать сообщение отдельно
silent_1991
Эксперт С++
4963 / 3039 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
24.07.2016, 20:04
Продемонстрирую то, что я перечислил. Приступим.

1. Лямбда-функции (замыкания).
Предположим, нужно подсчитать количество элементов в векторе, удовлетворяющих заданному критерию (пусть это будут чётные числа).
Раньше:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <algorithm>
#include <iostream>
#include <vector>
 
class EvenNumberPredicate {
public:
    bool operator()(int number) const
    {
        return number % 2 == 0;
    }
};
 
int main()
{
    std::vector<int> vec;
    vec.push_back(7);
    vec.push_back(2);
    vec.push_back(3);
    vec.push_back(8);
    vec.push_back(4);
    vec.push_back(1);
    std::cout << std::count_if(vec.begin(), vec.end(), EvenNumberPredicate()) << std::endl;
    return 0;
}
Теперь (здесь я продемонстрировал ещё одно удобное введение 11 стандарта - списки инициализации, реализуемые библиотечным классом std::initializer_list; теперь так можно инициализировать не только скалярные массивы и структуры, но и любые кастомные классы, или даже передавать их в функции как аргумент):
C++
1
2
3
4
5
6
7
8
9
10
#include <algorithm>
#include <iostream>
#include <vector>
 
int main()
{
    std::vector<int> vec = { 7, 2, 3, 8, 4, 1 };
    std::cout << std::count_if(vec.begin(), vec.end(), [](int n) { return n % 2 == 0; }) << std::endl;
    return 0;
}
2. foreach (покажу для полноты).
Просто обходим словарь и печатаем элементы в виде "key: value".
Раньше:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <map>
 
int main() {
    std::map<std::string, std::string> map;
    map["one"] = "first";
    map["two"] = "second";
    map["three"] = "third";
    map["four"] = "fourth";
    for (std::map<std::string, std::string>::const_iterator it = map.begin(); it != map.end(); ++it) {
        std::cout << it->first << ": " << it->second << std::endl;
    }
    return 0;
}
Теперь (тут и auto заодно применяется, что говорит о согласованности нововведений):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <map>
 
int main() {
    std::map<std::string, std::string> map = {
        { "one",   "first"  },
        { "two",   "second" },
        { "three", "third"  },
        { "four",  "fourth" },
    };
    for (const auto& it : map) {
        std::cout << it.first << ": " << it.second << std::endl;
    }
    return 0;
}
3. Variadic templates.
Честно говоря, мне лень самому писать примеры, тем более, что простые примеры получатся синтетическими и, таким образом, довольно неубедительными. Вот статья, где показаны примеры использования таких шаблонов (правда, там они тоже синтетические, но там есть код). Стоит сказать, что раньше, до появления этого в стандарте, подобное по-человечески провернуть было буквально невозможно. Теперь, при наличии шаблонов с переменным числом параметров, появилась возможность, например, реализовать действительно типобезопасный форматный ввод-вывод (scanf/printf), или же благодаря этой возможности реализуются такие функции как std::make_shared и std::make_unique (см. ниже).

4. Move-семантика и rvalue-ссылки.
Позволяет подсказать компилятору, как именно перемещать содержимое объекта в случае, когда полноценно копировать его нет необходимости. Например:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <algorithm>
#include <cstdint>
#include <initializer_list>
#include <iostream>
 
template<typename T>
class Array {
public:
    Array(std::size_t size):
        m_buf(new T[size]),
        m_size(size)
    {
        std::cout << "Array::Array(std::size_t)" << std::endl;
    }
    
    Array(const std::initializer_list<T>& init):
        m_buf(new T[init.size()]),
        m_size(init.size())
    {
        std::cout << "Array::Array(const std::initializer_list<T>&)" << std::endl;
        std::copy(std::begin(init), std::end(init), m_buf);
    }
    
    Array(const Array& other):
        m_buf(new T[other.m_size]),
        m_size(other.m_size)
    {
        std::cout << "Array::Array(const Array&)" << std::endl;
        std::copy(other.m_buf, other.m_buf + other.m_size, m_buf);
    }
    
    Array(Array&& other):
        m_buf(other.m_buf),
        m_size(other.m_size)
    {
        std::cout << "Array::Array(Array&&)" << std::endl;
        // Важно! Если этого не сделать, вызов деструктора на other сделает
        // невалидным конструируемый объект
        other.m_buf = nullptr;
    }
    
    ~Array()
    {
        delete[] m_buf;
    }
    
    Array& operator=(const Array& other)
    {
        std::cout << "Array::operator=(const Array&)" << std::endl;
        
        if (this != &other)
        {
            delete[] m_buf;
            m_buf = new T[other.m_size];
            m_size = other.m_size;
            std::copy(other.m_buf, other.m_buf + other.m_size, m_buf);
        }
        
        return *this;
    }
    
    Array& operator=(Array&& other)
    {
        std::cout << "Array::operator=(Array&&)" << std::endl;
        m_buf = other.m_buf;
        m_size = other.m_size;
        // Важно! Если этого не сделать, вызов деструктора на other сделает
        // невалидным конструируемый объект
        other.m_buf = nullptr;
        return *this;
    }
    
private:
    T *m_buf;
    std::size_t m_size;
};
 
int main() {
    Array<int> a1 = { 1, 2, 3, 4, 5 };
    Array<int> a2 = { 6, 7, 8 };
    Array<int> a3 = a1;
    Array<int> a4 = std::move(a2); // a2 невалиден после этой строки
    a1 = a3;
    a2 = std::move(a4); // a4 невалиден после этой строки
    return 0;
}
На самом деле, это простой искусственный пример. std::move здесь используется, чтобы показать, в каких случаях какой конструктор/оператор будет вызван. std::move просто превращает переданное ей значение в rvalue-ссылку (Type&&). Но инициировать вызов соответствующего оператора/конструктора можно неявно. Например, если из функции возвращается объект по значению, то в стандартной ситуации будет вызван конструктор копирования (временный объект, возвращённый из функции, будет скопирован в новое место, а затем разрушен), а если это присваивание, а не инициализация, то ещё и оператор присваивания (второе копирование). (Стоит сказать, что здесь может вступить в игру copy elision - оптимизация копирования временных объектов - но до 17 стандарта, который ещё не вышел, не определено, когда и как должна делаться эта оптимизация, так что компиляторы вольны решать это самостоятельно.) При наличии move-семантики он будет перемещён (в случае приведённого кода будет скопирован указатель на буфер, новых выделений памяти не будет).
Кроме того, rvalue-ссылки позволили реализовать такую вещь как perfect forwarding, но это тема отдельного разговора. Если интересно, поищите в гугле по этому словосочетанию.
5. Smart pointers
Не поверю, что вы о них не слышали) Сами умные указатели перетащили из буста (с некоторыми изменениями-улучшениями, насколько я знаю), но благодаря perfect forwarding и variadic templates можно полностью избежать явных вызовов оператора new (что, бесспорно, великолепно; сам умный указатель скрывает в себе вызов delete, а теперь код будет свободен ещё и от явных new; управление ресурсами становится всё проще). Т.е. если у нас есть класс Foo, имеющий такой конструктор:
C++
1
Foo::Foo(int, double, char);
и нам нужно положить его экземпляр в shared_ptr, мы можем написать так:
C++
1
auto ptr = std::make_shared<Foo>(42, 3.14, 'a');
И здесь нет никакой магии. std::make_shared - шаблонная функция, параметром шаблона которой является список типов переменной длины. Принимает она список значений соответствующих типов. Список значений, который приняла функция, можно распаковать при помощи оператора '...'. Соответственно, реализация её может выглядеть примерно так:
C++
1
2
3
4
5
template<typename T, typename... Args>
std::shared_ptr<T> make_shared(Args&&... args)
{
    return std::shared_ptr<T>(new T(args...));
}
new никуда не исчез, он просто сокрыт внутри библиотечной функции. Зато он исчез из пользовательского кода. Теперь можно писать на C++, сохраняя все его преимущества, но при этом не управляя ресурсами вручную.

6. final/override
Тут всё просто, добавили возможность явно выразить намерение переопределить виртуальную функцию базового класса (ключевое слово overrdie) либо наоборот, запретить переопределять виртуальную функцию в производном классе или вообще наследовать от самого класса (ключевое слово final).
Чем полезен overide? Тем, что он гарантирует, что будет функция будет именно переопределена, т.е. будет производиться проверка сигнатуры. Например:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
 
class Foo {
public:
    virtual void method() const
    {
        std::cout << "Foo::method()" << std::endl;
    }
};
 
class Bar : public Foo {
public:
    virtual void method()
    {
        std::cout << "Bar::method()" << std::endl;
    }
};
 
int main() {
    Bar b;
    Foo& f = b;
    b.method();
    f.method();
    return 0;
}
Мы намеревались переопределить method базового класса Foo в производном Bar. Но мы забыли указать const в сигнатуре, и произошло не переопределение, а перекрытие. Упс... Результат совсем не тот, что мы ожидали.
А теперь так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
 
class Foo {
public:
    virtual void method() const
    {
        std::cout << "Foo::method()" << std::endl;
    }
};
 
class Bar : public Foo {
public:
    virtual void method() override
    {
        std::cout << "Bar::method()" << std::endl;
    }
};
 
int main() {
    Bar b;
    Foo& f = b;
    b.method();
    f.method();
    return 0;
}
Другое дело, теперь нас ожидает ошибка компиляции. Мы сказали, что переопределяем, но в базовом классе нет функции с такой сигнатурой, так что компилятор бьёт нас по рукам. Фиксим:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
 
class Foo {
public:
    virtual void method() const
    {
        std::cout << "Foo::method()" << std::endl;
    }
};
 
class Bar : public Foo {
public:
    virtual void method() const override
    {
        std::cout << "Bar::method()" << std::endl;
    }
};
 
int main() {
    Bar b;
    Foo& f = b;
    b.method();
    f.method();
    return 0;
}
Вот теперь код делает то, что мы ожидали.
Чем полезем final? Например, как известно, классы стандартной библиотеки не помечают свои деструкторы виртуальными. Таким образом, наследовать от них небезопасно. Пока мы выделяем объекты на стеке - всё в порядке. Но как только мы начинаем выделять их в куче, а потом, например, пытаться работать с ними (в том числе, удалить их посредством оператора delete/delete[]) через указатель на базовый класс - начинаются проблемы. При попытке удаления память спокойно может утечь в неизвестном направлении. Будь возможность пометить такие классы как ненаследуемые - проблем бы не было. Теперь такая возможность имеется.

Фух, всё это заняло отнюдь не 30 минут.
Вообще, если вы попробуете отбросить скептицизм и даже, в какой-то степени, упрямство, советую почитать последнюю книгу Мейерса "Эффективный и современный C++". Там описаны все фишки 11 и 14 стандартов, и куда лучше, чем я их тут попытался объяснить. Всё подробно, аргументированно, почему так лучше, чем сяк, и с примерами.
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru