Реализация ссылочной семантики
Контейнерные классы STL поддерживают семантику значений, но не поддерживают ссылочную семантику. Они создают внутренние копии вставляемых элементов и затем возвращают эти копии. Следовательно, если вам потребуется ссылочная семантика в контейнерах STL (например, из-за того, что копирование элементов обходится слишком дорого или же элементы должны совместно использоваться несколькими коллекциями), воспользуйтесь классом умного указателя для предотвращения потенциальных ошибок. Ниже приведено одно из возможных решений проблемы. В нем задействован вспомогательный класс умного указателя с подсчетом ссылок на объекты, на которые ссылается указатель:
#include "stdafx.h"
#include <iostream>
#include <list>
#include <deque>
#include <algorithm>
using namespace std;
/* Класс, обеспечивающий семантику подсчёта ссылок.
* Объект, на который ссылается указатель, автоматически
* уничтожается при удалении последнего экземпляра CountedPtr
* для данного объекта.
*/
template <class T>
class CountedPtr {
private:
T* ptr; // Указатель на значение
long* count; // Количество владельцев (общие данные)
public:
// Инициализация объекта существующим указателем
// - указатель p должен быть получен в результате вызова new
explicit CountedPtr(T* p = 0)
: ptr(p), count(new long(1)) {
}
// Копирующий указатель (увеличивает счётчик владельцев)
CountedPtr(const CountedPtr<T>& p) throw()
: ptr(p.ptr), count(p.count) {
++*count;
}
// Деструктор (уничтожает объект, если владелец был последним)
~CountedPtr() throw() {
dispose();
}
// Присваивание (перевод указателя на новый объект)
CountedPtr<T>& operator= (const CountedPtr<T>& p) throw() {
if (this != &p) {
dispose();
ptr = p.ptr;
count = p.count;
++*count;
}
return *this;
}
// Доступ к объекту, на который ссылается указатель
T& operator*() const throw() {
return *ptr;
}
T* operator->() const throw() {
return ptr;
}
private:
void dispose() {
if (--*count == 0) {
delete count;
delete ptr;
}
}
};
Предполагается, что значения, которыми инициализируются умные указатели, были возвращены оператором new. Класс CountedPtr позволяет копировать умные указатели так, чтобы оригинал и копия оставались действительными. Объект уничтожается только после удаления последнего указателя, ссылающегося на него.
Класс CountedPtr можно усовершенствовать, например реализовать в нем автоматическое преобразование типов или возможность передачи права владения от умного указателя вызывающей стороне.
Ниже приведен пример использования класса CountedPtr:
void printCountedPtr(CountedPtr<int> elem)
{
cout << *elem << ' ';
}
int main()
{
// Массив целых чисел (для совместного использования
// в разных контейнерах)
static int values[] = { 3, 5, 9, 1, 6, 4 };
// Две разные коллекции
typedef CountedPtr<int> IntPtr;
deque<IntPtr> coll1;
list<IntPtr> coll2;
/* Вставка общих объектов в коллекции
* - исходный порядок в coll1
* - обратный порядок в coll2
*/
for (int i = 0; i<sizeof(values) / sizeof(values[0]); ++i) {
IntPtr ptr(new int(values[i]));
coll1.push_back(ptr);
coll2.push_front(ptr);
}
// Вывод содержимого обеих коллекций
for_each(coll1.begin(),coll1.end(),printCountedPtr);
cout << endl;
for_each(coll2.begin(), coll2.end(),printCountedPtr);
cout << endl << endl;
/* Модификация значений в разных коллекциях
* - возведение в квадрат третьего значения в coll1
* - изменение знака первого значения в coll1
* - обнуление первого значения в coll2
*/
*coll1[2] *= *coll1[2];
(**coll1.begin()) *= -1;
(**coll2.begin()) = 0;
// Повторный вывод содержимого обеих коллекций
for_each(coll1.begin(), coll1.end(),printCountedPtr);
cout << endl;
for_each(coll2.begin(), coll2.end(),printCountedPtr);
cout << endl;
}
Результат выполнения программы выглядит так:
3 5 9 1 6 4
4 6 1 9 5 3
-3 5 81 1 6 0
0 6 1 81 5 -3
Если вызвать вспомогательную функцию, сохраняющую элемент коллекции (IntPtr) где-то в другом месте, то значение, на которое ссылается указатель, остается действительным даже после уничтожения коллекций или удаления из них всех элементов.
В архиве Boost библиотек C++ (http://www.boost.org/) хранятся различные классы умных указателей, расширяющие стандартную библиотеку C++.
Дата добавления: 2020-12-11; просмотров: 394;