[英]Accelerated C++ 14-5: Custom string class and reference counter works for one constructor but not another
For those of you familiar with the book Accelerated C++, I was writing a solution to problem 14-5 and came across some interesting behavior that I can't explain. 对于那些熟悉Accelerated C ++一书的人,我正在编写问题14-5的解决方案,并遇到了一些我无法解释的有趣行为。
The problem involves using custom string and pointer/reference counter classes to implement a program that can concatenate vectors of strings and create pictures out of them. 问题涉及使用自定义字符串和指针/引用计数器类来实现一个程序,该程序可以连接字符串的向量并从中创建图片。
Essentially, the part of the program in question is the following: 从本质上讲,该程序的部分内容如下:
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
}
It seems as though my reference counter isn't working properly when I use the commented line: the result I get is as if every str
in test
were the empty string. 当我使用注释行时,似乎我的引用计数器工作不正常:我得到的结果就好像
test
中的每个str
都是空字符串一样。
Here are my implementations of getline
and the relevant parts of the str
and ptr
classes: 以下是我的
getline
实现以及str
和ptr
类的相关部分:
str
class: 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
class: 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;
}
}
The vec
class is essentially a subset of std::vector
. vec
类本质上是std::vector
的子集。 I can supply those details here too, if necessary. 如有必要,我也可以在这里提供这些细节。
And here is getline: 这是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;
}
Even though you are counting references correctly, you are still sharing the same pointer between the instances. 即使您正确计算引用,您仍然在实例之间共享相同的指针。 So
getline
is modifying the same str
object. 所以
getline
正在修改同一个str
对象。 You need to implement Copy-on-write in str
. 您需要在
str
实现Copy-on-write 。
Here is what's wrong: 这是错的:
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;
}
So, you should do: 所以,你应该这样做:
s.data = ptr(new vec<char>());
instead of clearing the shared instance. 而不是清除共享实例。
When you call: 你打电话时:
test.push_back(s); // This line doesn't work here - why?
now s
and a copy of s
in test
share the same data. 现在
s
和test
的s
副本共享相同的数据。 During the next iteration of the while
loop getline
function calls s.data->clear()
, which clears data in both s
and the copy of s
in test
. 在的下一次迭代
while
循环getline
函数调用s.data->clear()
它清除在两个数据s
和副本s
中test
。
When you call: 你打电话时:
test.push_back(str(s.begin(),s.end()));
the str(s.begin(),s.end())
constructor creates a temp str
object with a copy of the data that was in s
and pushes that object into test
. str(s.begin(),s.end())
构造函数创建一个临时str
对象,其中包含s
中的数据副本,并将该对象推送到test
。 So now the temp object and the copy in test
share the same data, which is a non-shared copy of s
. 所以现在临时对象和
test
的副本共享相同的数据,这是s
的非共享副本。 The temp object gets destroyed and the copy in test
stays intact. 临时对象被破坏,
test
的副本保持不变。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.