умные указатели — Ускоренный C ++ 14-5: Пользовательский класс строки и счетчик ссылок работают для одного конструктора, но не для другого

Для тех из вас, кто знаком с книгой «Ускоренный C ++», я писал решение проблемы 14-5 и столкнулся с некоторым интересным поведением, которое я не могу объяснить.

Проблема заключается в использовании пользовательских классов строк и указателей / счетчиков ссылок для реализации программы, которая может объединять векторы строк и создавать из них изображения.

По сути, часть рассматриваемой программы заключается в следующем:

int main()
{
vec<str> test;
str s;

while(getline(std::cin,s))
{
test.push_back(str(s.begin(),s.end()));
//test.push_back(s); // This line doesn't work here - why?

// Using the above line results in every str in test being
// the empty string
}

// Use the vec<str> to make pictures

}

Кажется, что мой счетчик ссылок не работает должным образом, когда я использую закомментированную строку: результат, который я получаю, как будто каждый str в test были пустые строки.

Вот мои реализации getline и соответствующие части str а также ptr классы:

str учебный класс:

class str
{
friend std::istream& getline(std::istream &is, str &s);

public:
typedef char* iterator;
typedef const char* const_iterator;
typedef size_t size_type;

str() : data(new vec<char>) { }
str(size_type n, char c) : data(new vec<char>(n,c)) { }

str(const char *cp) : data(new vec<char>)
{
std::copy(cp,cp+std::strlen(cp),std::back_inserter(*data));
}

template <class InputIterator>
str(InputIterator b, InputIterator e) : data(new vec<char>)
{
std::copy(b,e,std::back_inserter(*data));
}

// Other str member functions and operators
private:
ptr< vec<char> > data;
};

ptr учебный класс:

template <class T>
class ptr
{
public:
void make_unique()
{
if(*refptr != 1)
{
--*refptr;
refptr = new std::size_t(1);
p = p ? clone(p) : 0;
}
}

ptr() : p(0), refptr(new std::size_t(1)) { }
ptr(T* t) : p(t), refptr(new std::size_t(1)) { }

ptr(const ptr &h) : p(h.p), refptr(h.refptr) { ++*refptr; }
ptr& operator=(const ptr &);
~ptr();

T& operator*() const
{
if(p)
{
return *p;
}

throw std::runtime_error("unbound ptr");
}

T* operator->() const
{
if(p)
{
return p;
}

throw std::runtime_error("unbound ptr");
}

private:
T* p;
std::size_t* refptr;
};

template <class T>
ptr<T>& ptr<T>::operator=(const ptr &rhs)
{
++*rhs.refptr;
// free the left hand side, destroying pointers if appropriate
if(--*refptr == 0)
{
delete refptr;
delete p;
}

// copy in values from the right-hand side
refptr = rhs.refptr;
p = rhs.p;
return *this;
}

template <class T>
ptr<T>::~ptr()
{
if(--*refptr == 0)
{
delete refptr;
delete p;
}
}

vec класс по сути является подмножеством std::vector, Я могу предоставить эти детали и здесь, если это необходимо.

А вот и getline:

std::istream& getline(std::istream &is, str &s)
{
s.data->clear();
char c;
while(is.get(c))
{
if(c != '\n')
{
s.data->push_back(c);
}
else
{
break;
}
}

return is;
}

1

Решение

Даже если вы правильно подсчитываете ссылки, вы все равно используете один и тот же указатель между экземплярами. Так getline модифицирует то же самое str объект. Вам необходимо реализовать Копирование при записи в str,

Вот что не так:

std::istream& getline(std::istream &is, str &s)
{
s.data->clear();          //should make a copy of data first
char c;
while(is.get(c))
{
if(c != '\n')
{
s.data->push_back(c);
}
else
{
break;
}
}

return is;
}

Итак, вы должны сделать:

s.data = ptr(new vec<char>());

вместо очистки общего экземпляра.

2

Другие решения

Когда вы звоните:

test.push_back(s); // This line doesn't work here - why?

сейчас s и копия s в test поделиться одними и теми же данными. Во время следующей итерации while петля getline вызовы функций s.data->clear(), который очищает данные в и то и другое s и копия s в test,

Когда вы звоните:

test.push_back(str(s.begin(),s.end()));

str(s.begin(),s.end()) конструктор создает временную str объект с копией данных, которые были в s и толкает этот объект в test, Так что теперь временный объект и копия в test использовать одни и те же данные, которые не являются общей копией s, Временный объект уничтожается и копия в test остается нетронутым

1

По вопросам рекламы [email protected]