Реализация ссылочной семантики


 

Контейнерные классы 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;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

Поделитесь с друзьями:

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.012 сек.