Для тех из вас, кто знаком с книгой «Ускоренный 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;
}
Даже если вы правильно подсчитываете ссылки, вы все равно используете один и тот же указатель между экземплярами. Так 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>());
вместо очистки общего экземпляра.
Когда вы звоните:
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
остается нетронутым