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

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
DiffEreD
1431 / 768 / 95
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
#1

shared_ptr и реализация семантики переноса - C++

25.12.2012, 16:21. Просмотров 1897. Ответов 21
Метки нет (Все метки)

Написал небольшой класс Array основанный на std::shared_ptr. Но как то не совсем уверен в правильности реализации конструктора и оператора переноса. Плюс, возникли какие то неполадки с перегруженным оператором + - он почему то модифицирует объекты которых не должен модифицировать. Что в моем коде не правильно?
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <iostream>
#include <algorithm>
#include <iterator>
#include <memory>
#include <functional>
 
template<typename T>
class Array
{
    std::shared_ptr<T> m_array;
    size_t m_size;
public:
    typedef T*          iterator;
    typedef const T*    const_iterator;
    iterator        begin()         {return m_array.get();}
    iterator        end()           {return m_array.get()+m_size;}
    const_iterator  begin() const   {return m_array.get();}
    const_iterator  end()   const   {return m_array.get()+m_size;}
 
    const T operator[](size_t index) const {return *(m_array.get()+index);}
    T operator[](size_t index) {return *(m_array.get()+index);}
 
    explicit Array(size_t size = 0, const T& value = T()) : m_size(size),
        m_array(new T[size], std::default_delete<T[]>())
    {
        std::fill(m_array.get(), m_array.get()+m_size, value);
    }
 
    Array(const Array& obj) : m_size(obj.m_size), m_array(obj.m_array) {std::cout<<"Array(const Array& obj)\n";}
 
    Array(Array&& obj) : m_size(std::move(obj.m_size)), m_array(std::move(obj.m_array))
    {
        std::cout<<"Array(Array&& obj)\n";
        obj.m_size = 0;
        obj.m_array.reset();
    }
 
    Array& operator=(const Array& obj)
    {
        std::cout<<"Array& operator=(const Array& obj)\n";
        if (this == &obj)
            return *this;
        m_size = obj.m_size;
        m_array = obj.m_array;
        return *this;
    }
 
    Array& operator=(Array&& obj)
    {
        std::cout<<"Array& operator=(Array&& obj)\n";
        m_size = std::move(obj.m_size);
        m_array = std::move(obj.m_array);
        obj.m_size = 0;
        obj.m_array.reset();
        return *this;
    }
 
    Array& operator+=(const Array& rhs)
    {
        std::transform(begin(), end(), rhs.begin(), begin(), std::plus<T>());
        return *this;
    }
 
    Array operator+(const Array& rhs)
    {
        Array ar;
        ar = *this;
        ar+=rhs;
        return ar;
    }
};
 
int main()
{
    {
        Array<int> a1(10, 6);
        Array<int> a2(a1);       //копирующий конструктор
 
 
        /*std::copy(a1.begin(), a1.end(), std::ostream_iterator<int>(std::cout," "));
        std::cout<<"\n\n";
        std::copy(a2.begin(), a2.end(), std::ostream_iterator<int>(std::cout," "));
        std::cout<<"\n\n";
        std::copy(a1.begin(), a1.end(), std::ostream_iterator<int>(std::cout," "));*/
 
        Array<int> b1(a2);      //копирующий конструктор
        Array<int> b2;
        b2 = a1+a2;            //почему то модифицирует объекты a1, a2 и b1
 
        std::cout<<"\n\n";
        std::copy(b1.begin(), b1.end(), std::ostream_iterator<int>(std::cout," "));
 
        std::cout<<"\n\n";
        std::copy(b2.begin(), b2.end(), std::ostream_iterator<int>(std::cout," "));
 
        std::cout<<"\n\n";
        std::copy(a1.begin(), a1.end(), std::ostream_iterator<int>(std::cout," "));
        std::cout<<"\n\n";
        std::copy(a2.begin(), a2.end(), std::ostream_iterator<int>(std::cout," "));
 
    }
    std::cout<<"\n\n";
    system("pause");
    return 0;
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
25.12.2012, 16:21
Здравствуйте! Я подобрал для вас темы с ответами на вопрос shared_ptr и реализация семантики переноса (C++):

Реализация shared_ptr - C++
Добрый день. Реализовал shared_ptr(), но если больше 2 перемен он не правильно работает. Что я не правильно делаю и как правильно ...

Shared_ptr собственная реализация - C++
Здравствуйте, написал собственную реалицацию &quot;умных&quot; указателей для класса object. Прежде чем попробовать написать шаблонную версию хочу...

Нюансы синтаксиса и семантики: что такое rvalue и lvalue? - C++
Добрый день, всем. Ребят, помогите разобраться в rvalue и lvalue. Читал об этом много чего, но все таки не понимаю, почему этому уделяется...

Где найти список всех операторов С++ с описанием их семантики? - C++
Хочу обучить некоторый проект искусственного разума составлять программы на С++ по установленным целям ( желаниям ). Необходимо найти...

Нюансы синтаксиса и семантики: ссылки, указатели и массивы в качестве аргументов функций - C++
Перенесено из этой темы. Чтобы вернуть указатели почему вот здесь не надо приводить janr к типу указателя char* на выходе? char*...

Shared_ptr - C++
Всем хай. Есть некий класс и в нём такой метод: class Base { public: std::shared_ptr&lt;Base&gt; get_ptr() { ...

21
ForEveR
В астрале
Эксперт С++
7979 / 4738 / 321
Регистрация: 24.06.2010
Сообщений: 10,543
Завершенные тесты: 3
25.12.2012, 16:33 #2
yuron_477,
C++
1
obj.m_array.reset();
зачем? shared_ptr сам умный, сам это сделает. int перемещать вцелом резона нет, ибо он в любом случае скопируется.)
1
gray_fox
What a waste!
1521 / 1226 / 70
Регистрация: 21.04.2012
Сообщений: 2,565
Завершенные тесты: 3
25.12.2012, 16:36 #3
Цитата Сообщение от yuron_477 Посмотреть сообщение
std::move(obj.m_size)
Это ни к чему, пожалуй (std::move).
Цитата Сообщение от yuron_477 Посмотреть сообщение
//почему то модифицирует объекты a1, a2 и b1
Ну так эти объекты ссылаются на один и тот же участок памяти, у тебя же shared_ptr внутри, и ты при конструировании копируешь их, а не то, на что они ссылаются.
1
DiffEreD
1431 / 768 / 95
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
25.12.2012, 16:48  [ТС] #4
Ну а как сделать чтобы все логически правильно работало, а то что то я никак не соображу?
0
gray_fox
What a waste!
1521 / 1226 / 70
Регистрация: 21.04.2012
Сообщений: 2,565
Завершенные тесты: 3
25.12.2012, 16:58 #5
Я бы хранил unique_ptr, в copy c-tor - std::copy, в move c-tor - std::move.

Добавлено через 6 минут
Или можно сделать класс неизменяемым, т.е. никаких операторов с присвоением.
1
ForEveR
В астрале
Эксперт С++
7979 / 4738 / 321
Регистрация: 24.06.2010
Сообщений: 10,543
Завершенные тесты: 3
25.12.2012, 17:37 #6
А я бы как-то так сделал. CoW собственно. Не очень хорошо по отношению к безопасности исключений да и copy-swap бы сделать, но лень.

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <iostream>
#include <algorithm>
#include <iterator>
#include <memory>
#include <functional>
 
template<typename T>
class Array
{
    std::shared_ptr<T> m_array;
    size_t m_size;
public:
    typedef T*          iterator;
    typedef const T*    const_iterator;
    iterator        begin()         {return m_array.get();}
    iterator        end()           {return m_array.get()+m_size;}
    const_iterator  begin() const   {return m_array.get();}
    const_iterator  end()   const   {return m_array.get()+m_size;}
 
    const T operator[](size_t index) const {return *(m_array.get()+index);}
    T operator[](size_t index) {return *(m_array.get()+index);}
 
    explicit Array(size_t size = 0, const T& value = T()) : m_size(size),
        m_array(new T[size], std::default_delete<T[]>())
    {
        std::fill(m_array.get(), m_array.get()+m_size, value);
    }
 
    Array(const Array& obj) : m_size(obj.m_size), m_array(obj.m_array) {std::cout<<"Array(const Array& obj)\n";}
 
    Array(Array&& obj) : m_size(obj.m_size), m_array(std::move(obj.m_array))
    {
        std::cout<<"Array(Array&& obj)\n";
        obj.m_size = 0;
    }
 
    Array& operator=(const Array& obj)
    {
        std::cout<<"Array& operator=(const Array& obj)\n";
        if (this == &obj)
            return *this;
        m_size = obj.m_size;
        m_array = obj.m_array;
        return *this;
    }
 
    Array& operator=(Array&& obj)
    {
        std::cout<<"Array& operator=(Array&& obj)\n";
        m_size = obj.m_size;
        m_array = std::move(obj.m_array);
        obj.m_size = 0;
        return *this;
    }
 
    Array& operator+=(const Array& rhs)
    {
        std::transform(begin(), end(), rhs.begin(), begin(), std::plus<T>());
        return *this;
    }
 
    Array operator+(const Array& rhs)
    {
        Array ar = deep_copy();
        ar+=rhs;
        return ar;
    }
private:
    Array deep_copy()
    {
       Array ar;
       ar.m_array = std::shared_ptr<T>(new T[m_size], std::default_delete<T[]>());
       const T* array = m_array.get();
       std::copy(array, array + m_size, ar.m_array.get());
       ar.m_size = m_size;
       return ar;
    }
};
 
int main()
{
    {
        Array<int> a1(10, 6);
        Array<int> a2(a1);       //копирующий конструктор
 
 
        /*std::copy(a1.begin(), a1.end(), std::ostream_iterator<int>(std::cout," "));
        std::cout<<"\n\n";
        std::copy(a2.begin(), a2.end(), std::ostream_iterator<int>(std::cout," "));
        std::cout<<"\n\n";
        std::copy(a1.begin(), a1.end(), std::ostream_iterator<int>(std::cout," "));*/
 
        Array<int> b1(a2);      //копирующий конструктор
        Array<int> b2;
        b2 = a1+a2;            //почему то модифицирует объекты a1, a2 и b1
 
        std::cout<<"\n\n";
        std::copy(b1.begin(), b1.end(), std::ostream_iterator<int>(std::cout," "));
 
        std::cout<<"\n\n";
        std::copy(b2.begin(), b2.end(), std::ostream_iterator<int>(std::cout," "));
 
        std::cout<<"\n\n";
        std::copy(a1.begin(), a1.end(), std::ostream_iterator<int>(std::cout," "));
        std::cout<<"\n\n";
        std::copy(a2.begin(), a2.end(), std::ostream_iterator<int>(std::cout," "));
 
    }
    std::cout<<"\n\n";
    return 0;
}
Добавлено через 16 минут
copy/move_and_swap можно сделать как-нибудь так к примеру. Ну это так, изврата ради.

C++
1
2
3
4
5
6
7
8
    template<typename Internal>
    void internal_swap(Internal&& rhs)
    {
       std::swap(m_size, rhs.m_size);
       std::shared_ptr<T> array = m_array;
       m_array = std::forward<std::shared_ptr<T>>(rhs.m_array);
       rhs.m_array = array;
    }
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    Array& operator=(const Array& obj)
    {
        std::cout<<"Array& operator=(const Array& obj)\n";
        if (this == &obj)
            return *this;
        Array ar(obj);
        internal_swap(ar);
        return *this;
    }
 
    Array& operator=(Array&& obj)
    {
        std::cout<<"Array& operator=(Array&& obj)\n";
        internal_swap(std::forward<Array>(obj));
        obj.m_size = 0;
        return *this;
    }
2
gray_fox
What a waste!
1521 / 1226 / 70
Регистрация: 21.04.2012
Сообщений: 2,565
Завершенные тесты: 3
25.12.2012, 18:21 #7
Как вариант, не особо красиво конечно)
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <iostream>
#include <memory>
#include <utility>
#include <algorithm>
#include <iterator>
#include <type_traits>
#include <initializer_list>
 
 
template<typename T>
class array {
   
public:
   typedef array           self_type;
   typedef T               value_type;
   typedef T *             pointer;
   typedef T const*        const_pointer;
   typedef T &             reference;
   typedef T const&        const_reference;
   typedef std::size_t     size_type;
   typedef std::ptrdiff_t  difference_type;
   typedef const_pointer   iterator;
   typedef const_pointer   const_iterator;
   
   
   const_iterator begin() const {
      return arrayPtr.get();
   }
   
   const_iterator end() const {
      return arrayPtr.get() + arraySize;
   }
   
   
   const_reference operator [](size_type const idx) const {
      return arrayPtr[idx];
   }
   
   
   size_type size() const {
      return arraySize;
   }
   
   
   void swap(self_type & other) {
      std::swap(arrayPtr,  other.arrayPtr);
      std::swap(arraySize, other.ArraySize);
   }
   
   
   explicit array(size_type const s = 0, const_reference v = T())
         : arrayPtr(new T[s], std::default_delete<T[]>())
         , arraySize(s) {
      std::fill(&*arrayPtr, &*arrayPtr + arraySize, v);      
   }
         
   template<typename Iterator>
   array(Iterator const first, Iterator const last)
         : arraySize(std::distance(first, last)) {
      arrayPtr.reset(new T[arraySize], std::default_delete<T[]>());
      std::copy(first, last, &*arrayPtr);
   }
   
   template<
         typename S
       , typename = typename std::enable_if<std::is_convertible<S, T>::value>::type
   >
   array(std::initializer_list<S> list)
         : array(std::begin(list), std::end(list)) {}
   
   array(self_type const& other)
         : arrayPtr(other.arrayPtr)
         , arraySize(other.arraySize) {}
   
   template<
         typename S
       , typename = typename std::enable_if<std::is_convertible<S, T>::value>::type
   >
   array(array<S> const& other)
         : array(other.begin(), other.end()) {}
      
   array(self_type && other)
         : arrayPtr(std::move(other.arrayPtr))
         , arraySize(other.arraySize) {}
           
   
   array & operator =(array const&) = delete;
   array & operator =(array &&)     = delete;
            
  
private:
   template<typename S>
   friend class array;
   
   template<typename S, typename L>
   array<typename std::common_type<S, L>::type>
   friend operator +(array<S> const& lhs, array<L> const& rhs);
   
 
   std::shared_ptr<T>   arrayPtr;
   size_type            arraySize;
};
 
 
template<typename T, typename S>
array<typename std::common_type<T, S>::type>
operator +(array<T> const& lhs, array<S> const& rhs) {
   using result_type = typename std::common_type<T, S>::type;
   
   array<result_type> result(std::max(lhs.size(), rhs.size()));
   
   if (lhs.size() < rhs.size()) {
      std::copy(rhs.begin(), rhs.end(), &*result.arrayPtr);
      std::transform(lhs.begin(), lhs.end(), rhs.begin(), &*result.arrayPtr, std::plus<result_type>());
   } else {
      std::copy(lhs.begin(), lhs.end(), &*result.arrayPtr);
      std::transform(rhs.begin(), rhs.end(), lhs.begin(), &*result.arrayPtr, std::plus<result_type>());
   }
   
   return result;
}
 
 
int main() {
   array<int>     a1 = {1, 2, 3, 4, 5};
   array<double>  a2 = {1.23, 432., 5.66, 5.56};
   array<int>     b1(a1);
   array<double>  b2(a2);
   array<float>   b3(a1);
   
   auto r1 = a1 + b1;
   std::copy(std::begin(r1), std::end(r1), std::ostream_iterator<int>(std::cout, " "));
   std::cout << std::endl;
   
   auto r2 = a1 + a2;
   std::copy(std::begin(r2), std::end(r2), std::ostream_iterator<double>(std::cout, " "));
   std::cout << std::endl;
   
   auto r3 = b1 + b2 + b3;
   std::copy(std::begin(r3), std::end(r3), std::ostream_iterator<double>(std::cout, " "));
   std::cout << std::endl;
   
   auto r4 = std::move(r3);
   std::copy(std::begin(r4), std::end(r4), std::ostream_iterator<double>(std::cout, " "));
   std::cout << std::endl;
}
4
Avazart
Нарушитель
Эксперт С++
7233 / 5405 / 294
Регистрация: 10.12.2010
Сообщений: 23,956
Записей в блоге: 17
28.12.2012, 15:24 #8
Стоит отметить, что рассмотренные мною умные указатели (кроме unique_ptr) не предназначен для владения массивами. Это связано с тем, что деструктор вызывает именно delete, а не delete[] (что требуется для массивов).
http://www.kalnitsky.org/2011/11/02/smart-pointers-in-cpp11/
0
DiffEreD
1431 / 768 / 95
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
28.12.2012, 15:29  [ТС] #9
Ну так default_delete для чего придумали: http://en.cppreference.com/w/cpp/memory/default_delete
1
Avazart
Нарушитель
Эксперт С++
7233 / 5405 / 294
Регистрация: 10.12.2010
Сообщений: 23,956
Записей в блоге: 17
28.12.2012, 15:55 #10
20.7.1.1.1 In general [unique.ptr.dltr.general]
1 The class template default_delete serves as the default deleter (destruction policy) for the class template
unique_ptr.
2 The template parameter T of default_delete may be an incomplete type.
Разве это не только для std::unique_ptr ?

Добавлено через 10 минут
Все понял... не не только для std::unique_ptr
0
zarko97
278 / 38 / 0
Регистрация: 11.10.2015
Сообщений: 400
26.02.2017, 23:42 #11
gray_fox, вот так ,думаю, будет надежнее
C++
1
std::fill(std::addressof(*arrayPtr), std::addressof(*arrayPtr) + arraySize, v);
1
zarko97
278 / 38 / 0
Регистрация: 11.10.2015
Сообщений: 400
09.04.2017, 11:41 #12
gray_fox, да, и заместо медленного new лучше std::make_shared использовать, т.к он быстрее и надежнее
0
GbaLog-
Любитель чаепитий
3016 / 1384 / 335
Регистрация: 24.08.2014
Сообщений: 4,907
Записей в блоге: 1
Завершенные тесты: 2
09.04.2017, 12:41 #13
Цитата Сообщение от zarko97 Посмотреть сообщение
т.к он быстрее
откуда информация?
0
zarko97
278 / 38 / 0
Регистрация: 11.10.2015
Сообщений: 400
09.04.2017, 13:14 #14
GbaLog-, в этой ситуации new выполняет выделение памяти дважды: для объекта и управляющего им блока, а std::make_shared выполняет одно выделение и значительно надежнее и безопаснее, если аллокация памяти завершится неудачей
1
GbaLog-
Любитель чаепитий
3016 / 1384 / 335
Регистрация: 24.08.2014
Сообщений: 4,907
Записей в блоге: 1
Завершенные тесты: 2
09.04.2017, 13:33 #15
Цитата Сообщение от zarko97 Посмотреть сообщение
значительно надежнее и безопаснее, если аллокация памяти завершится неудачей
если аллокация завершится неудачей, то в любом случае std::bad_alloc вылетит.
0
09.04.2017, 13:33
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
09.04.2017, 13:33
Привет! Вот еще темы с ответами:

Allocator и shared_ptr - C++
Прокомментируйте пожалуйста. Есть ли смысл и вообще возможность оформлять зарезервированную память allocator-ом в shared_ptr? Или это...

Использование shared_ptr - C++
std::shared_ptr&lt;int&gt; sp(new int); sp = 12; std::cout &lt;&lt; sp; Расшареному указателю нельзя присваивать стандартные...

shared_ptr валится - C++
#include &lt;iostream&gt; #include &lt;string&gt; using namespace std; #include &quot;boost/shared_ptr.hpp&quot; class One { public: ...

Наследование от shared_ptr - C++
Добрый день. Подскажите вот какую вещь. Сделал удобный для себя класс для ведения логов. В нем переопределил operator&lt;&lt; след....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru