forked from boostorg/smart_ptr
Documentation updated to reflect changes to shared_ptr
[SVN r16739]
This commit is contained in:
@@ -106,7 +106,7 @@ public:
|
|||||||
typedef T * pointer;
|
typedef T * pointer;
|
||||||
typedef typename detail::shared_ptr_traits<T>::reference reference;
|
typedef typename detail::shared_ptr_traits<T>::reference reference;
|
||||||
|
|
||||||
shared_ptr(): px(0), pn()
|
shared_ptr(): px(0), pn() // never throws in 1.30+
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ public:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void reset()
|
void reset() // never throws in 1.30+
|
||||||
{
|
{
|
||||||
this_type().swap(*this);
|
this_type().swap(*this);
|
||||||
}
|
}
|
||||||
@@ -253,6 +253,8 @@ public:
|
|||||||
return px == 0? 0: &this_type::get;
|
return px == 0? 0: &this_type::get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// operator! is redundant, but some compilers need it
|
||||||
|
|
||||||
bool operator! () const // never throws
|
bool operator! () const // never throws
|
||||||
{
|
{
|
||||||
return px == 0;
|
return px == 0;
|
||||||
|
261
shared_ptr.htm
261
shared_ptr.htm
@@ -8,13 +8,13 @@
|
|||||||
<h1><IMG height="86" alt="c++boost.gif (8819 bytes)" src="../../c++boost.gif" width="277" align="middle">shared_ptr
|
<h1><IMG height="86" alt="c++boost.gif (8819 bytes)" src="../../c++boost.gif" width="277" align="middle">shared_ptr
|
||||||
class template</h1>
|
class template</h1>
|
||||||
<p><A href="#Introduction">Introduction</A><br>
|
<p><A href="#Introduction">Introduction</A><br>
|
||||||
<a href="#BestPractices">Best Practices</a><br>
|
<A href="#BestPractices">Best Practices</A><br>
|
||||||
<A href="#Synopsis">Synopsis</A><br>
|
<A href="#Synopsis">Synopsis</A><br>
|
||||||
<A href="#Members">Members</A><br>
|
<A href="#Members">Members</A><br>
|
||||||
<A href="#functions">Free Functions</A><br>
|
<A href="#functions">Free Functions</A><br>
|
||||||
<A href="#example">Example</A><br>
|
<A href="#example">Example</A><br>
|
||||||
<A href="#Handle/Body">Handle/Body Idiom</A><br>
|
<A href="#Handle/Body">Handle/Body Idiom</A><br>
|
||||||
<a href="#ThreadSafety">Thread Safety</a><br>
|
<A href="#ThreadSafety">Thread Safety</A><br>
|
||||||
<A href="#FAQ">Frequently Asked Questions</A><br>
|
<A href="#FAQ">Frequently Asked Questions</A><br>
|
||||||
<A href="smarttests.htm">Smart Pointer Timings</A></p>
|
<A href="smarttests.htm">Smart Pointer Timings</A></p>
|
||||||
<h2><a name="Introduction">Introduction</a></h2>
|
<h2><a name="Introduction">Introduction</a></h2>
|
||||||
@@ -60,8 +60,7 @@
|
|||||||
be rare.</P>
|
be rare.</P>
|
||||||
<P>Avoid using unnamed <STRONG>shared_ptr</STRONG> temporaries to save typing; to
|
<P>Avoid using unnamed <STRONG>shared_ptr</STRONG> temporaries to save typing; to
|
||||||
see why this is dangerous, consider this example:</P>
|
see why this is dangerous, consider this example:</P>
|
||||||
<PRE>
|
<PRE>void f(shared_ptr<int>, int);
|
||||||
void f(shared_ptr<int>, int);
|
|
||||||
int g();
|
int g();
|
||||||
|
|
||||||
void ok()
|
void ok()
|
||||||
@@ -86,7 +85,7 @@ void bad()
|
|||||||
<h2><a name="Synopsis">Synopsis</a></h2>
|
<h2><a name="Synopsis">Synopsis</a></h2>
|
||||||
<pre>namespace boost {
|
<pre>namespace boost {
|
||||||
|
|
||||||
class use_count_is_zero: public std::exception;
|
class bad_weak_ptr: public std::exception;
|
||||||
|
|
||||||
template<class T> class <A href="weak_ptr.htm" >weak_ptr</A>;
|
template<class T> class <A href="weak_ptr.htm" >weak_ptr</A>;
|
||||||
|
|
||||||
@@ -96,21 +95,21 @@ void bad()
|
|||||||
|
|
||||||
typedef T <A href="#element_type" >element_type</A>;
|
typedef T <A href="#element_type" >element_type</A>;
|
||||||
|
|
||||||
<A href="#constructors">shared_ptr</A>();
|
<A href="#constructors" >shared_ptr</A>(); // never throws
|
||||||
template<class Y> explicit <A href="#constructors" >shared_ptr</A>(Y * p);
|
template<class Y> explicit <A href="#constructors" >shared_ptr</A>(Y * p);
|
||||||
template<class Y, class D> <A href="#constructors" >shared_ptr</A>(Y * p, D d);
|
template<class Y, class D> <A href="#constructors" >shared_ptr</A>(Y * p, D d);
|
||||||
<A href="#destructor">~shared_ptr</A>(); // never throws
|
<A href="#destructor" >~shared_ptr</A>(); // never throws
|
||||||
|
|
||||||
<A href="#constructors">shared_ptr</A>(shared_ptr const & r); // never throws
|
<A href="#constructors" >shared_ptr</A>(shared_ptr const & r); // never throws
|
||||||
template<class Y> <A href="#constructors">shared_ptr</A>(shared_ptr<Y> const & r); // never throws
|
template<class Y> <A href="#constructors" >shared_ptr</A>(shared_ptr<Y> const & r); // never throws
|
||||||
template<class Y> explicit <A href="#constructors">shared_ptr</A>(<A href="weak_ptr.htm" >weak_ptr</A><Y> const & r);
|
template<class Y> explicit <A href="#constructors" >shared_ptr</A>(<A href="weak_ptr.htm" >weak_ptr</A><Y> const & r);
|
||||||
template<class Y> explicit <A href="#constructors" >shared_ptr</A>(std::auto_ptr<Y> & r);
|
template<class Y> explicit <A href="#constructors" >shared_ptr</A>(std::auto_ptr<Y> & r);
|
||||||
|
|
||||||
shared_ptr & <A href="#assignment" >operator=</A>(shared_ptr const & r); // never throws
|
shared_ptr & <A href="#assignment" >operator=</A>(shared_ptr const & r); // never throws
|
||||||
template<class Y> shared_ptr & <A href="#assignment" >operator=</A>(shared_ptr<Y> const & r); // never throws
|
template<class Y> shared_ptr & <A href="#assignment" >operator=</A>(shared_ptr<Y> const & r); // never throws
|
||||||
template<class Y> shared_ptr & <A href="#assignment" >operator=</A>(std::auto_ptr<Y> & r);
|
template<class Y> shared_ptr & <A href="#assignment" >operator=</A>(std::auto_ptr<Y> & r);
|
||||||
|
|
||||||
void <A href="#reset" >reset</A>();
|
void <A href="#reset" >reset</A>(); // never throws
|
||||||
template<class Y> void <A href="#reset" >reset</A>(Y * p);
|
template<class Y> void <A href="#reset" >reset</A>(Y * p);
|
||||||
template<class Y, class D> void <A href="#reset" >reset</A>(Y * p, D d);
|
template<class Y, class D> void <A href="#reset" >reset</A>(Y * p, D d);
|
||||||
|
|
||||||
@@ -121,37 +120,39 @@ void bad()
|
|||||||
bool <A href="#unique" >unique</A>() const; // never throws
|
bool <A href="#unique" >unique</A>() const; // never throws
|
||||||
long <A href="#use_count" >use_count</A>() const; // never throws
|
long <A href="#use_count" >use_count</A>() const; // never throws
|
||||||
|
|
||||||
operator <a href="#conversions"><i>unspecified-bool-type</i></a>() const; // never throws
|
operator <A href="#conversions" ><i>unspecified-bool-type</i></A>() const; // never throws
|
||||||
|
|
||||||
void <A href="#swap" >swap</A>(shared_ptr & b); // never throws
|
void <A href="#swap" >swap</A>(shared_ptr & b); // never throws
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T, class U>
|
template<class T, class U>
|
||||||
bool <A href="#comparison" >operator==</A>(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
|
bool <A href="#comparison" >operator==</A>(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
|
||||||
|
|
||||||
template<class T, class U>
|
template<class T, class U>
|
||||||
bool <A href="#comparison" >operator!=</A>(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
|
bool <A href="#comparison" >operator!=</A>(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
|
||||||
template<class T>
|
|
||||||
bool <A href="#comparison" >operator<</A>(shared_ptr<T> const & a, shared_ptr<T> const & b); // never throws
|
template<class T, class U>
|
||||||
|
bool <A href="#comparison" >operator<</A>(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
|
||||||
|
|
||||||
template<class T> void <A href="#free-swap" >swap</A>(shared_ptr<T> & a, shared_ptr<T> & b); // never throws
|
template<class T> void <A href="#free-swap" >swap</A>(shared_ptr<T> & a, shared_ptr<T> & b); // never throws
|
||||||
|
|
||||||
template<class T> T * <A href="#get_pointer" >get_pointer</A>(shared_ptr<T> const & p); // never throws
|
template<class T> T * <A href="#get_pointer" >get_pointer</A>(shared_ptr<T> const & p); // never throws
|
||||||
|
|
||||||
template<class T, class U>
|
template<class T, class U>
|
||||||
shared_ptr<T> <A href="#shared_static_cast" >shared_static_cast</A>(shared_ptr<U> const & r); // never throws
|
shared_ptr<T> <A href="#static_pointer_cast" >static_pointer_cast</A>(shared_ptr<U> const & r); // never throws
|
||||||
|
|
||||||
template<class T, class U>
|
template<class T, class U>
|
||||||
shared_ptr<T> <A href="#shared_dynamic_cast" >shared_dynamic_cast</A>(shared_ptr<U> const & r);
|
shared_ptr<T> <A href="#dynamic_pointer_cast" >dynamic_pointer_cast</A>(shared_ptr<U> const & r); // never throws
|
||||||
template<class T, class U>
|
|
||||||
shared_ptr<T> <A href="#shared_polymorphic_cast" >shared_polymorphic_cast</A>(shared_ptr<U> const & r);
|
template<class E, class T, class Y>
|
||||||
template<class T, class U>
|
std::basic_ostream<E, T> & <A href="#insertion-operator" >operator<<</A> (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);
|
||||||
shared_ptr<T> <A href="#shared_polymorphic_downcast" >shared_polymorphic_downcast</A>(shared_ptr<U> const & r); // never throws
|
|
||||||
|
|
||||||
}</pre>
|
}</pre>
|
||||||
<P><EM>[It might be convenient to relax the requirements on <STRONG>shared_ptr</STRONG>'s
|
<P><EM>[It might be convenient to relax the requirements on <STRONG>shared_ptr</STRONG>'s
|
||||||
signature, allowing an additional, defaulted, template parameter; the parameter
|
signature, allowing an additional, defaulted, template parameter; the parameter
|
||||||
can encode the threading model, for example. This would help in detecting
|
can encode the threading model, for example. This would help in detecting
|
||||||
possible ODR violations.</EM></P>
|
possible ODR violations.</EM></P>
|
||||||
<P><EM> On the other hand, using <STRONG>shared_ptr</STRONG> as an argument to a
|
<P><EM>On the other hand, using <STRONG>shared_ptr</STRONG> as an argument to a
|
||||||
template template parameter requires an exact signature match. </EM><EM>Metaprogramming
|
template template parameter requires an exact signature match. </EM><EM>Metaprogramming
|
||||||
experts tend to deemphasize template template parameters as they are too
|
experts tend to deemphasize template template parameters as they are too
|
||||||
inflexible, but the alternative is typically an std::allocator::rebind-type
|
inflexible, but the alternative is typically an std::allocator::rebind-type
|
||||||
@@ -163,27 +164,22 @@ void bad()
|
|||||||
<p>Provides the type of the template parameter T.</p>
|
<p>Provides the type of the template parameter T.</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<h3><a name="constructors">constructors</a></h3>
|
<h3><a name="constructors">constructors</a></h3>
|
||||||
<pre>shared_ptr();</pre>
|
<pre>shared_ptr(); // never throws</pre>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p><b>Effects:</b> Constructs a <b>shared_ptr</b>.</p>
|
<p><b>Effects:</b> Constructs an <EM>empty</EM> <b>shared_ptr</b>. <EM>Empty</EM> <STRONG>
|
||||||
<p><b>Postconditions:</b> <A href="#use_count">use count</A> is 1; the stored
|
shared_ptr</STRONG> objects have an unspecified <A href="#use_count">use_count</A>.</p>
|
||||||
pointer is 0.</p>
|
<p><b>Postconditions:</b> <code>get() == 0</code>.</p>
|
||||||
<p><b>Throws:</b> <b>std::bad_alloc</b>.</p>
|
<p><b>Throws:</b> nothing.</p>
|
||||||
<p><b>Exception safety:</b> If an exception is thrown, the constructor has no
|
|
||||||
effect.</p>
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<P><EM>[The postcondition of use_count() == 1 is too strong. Having the nothrow
|
<P><EM>[The nothrow guarantee is important, since <STRONG>reset()</STRONG> is specified
|
||||||
guarantee is important, since <STRONG>reset()</STRONG> is specified in terms of
|
in terms of the default constructor; this implies that the constructor must not
|
||||||
the default constructor, but the current specification requires that a count be
|
allocate memory.</EM></P>
|
||||||
allocated. Therefore, this postcondition will be dropped in a future release.
|
<P><EM>There are two possible implementations, one stores 0 as a pointer to the
|
||||||
The use count of a default-constructed <STRONG>shared_ptr</STRONG>, including
|
|
||||||
all copies created from it, will probably be left unspecified.</EM></P>
|
|
||||||
<P><EM>There are two possible nothrow implementations, one stores 0 as a pointer to the
|
|
||||||
reference count, the other uses a single statically allocated count for all
|
reference count, the other uses a single statically allocated count for all
|
||||||
default-constructed <STRONG>shared_ptr</STRONG>s. The second option is
|
default-constructed <STRONG>shared_ptr</STRONG>s. The second option is
|
||||||
difficult to achieve in the current header-only reference implementation due to
|
difficult to achieve in the current header-only reference implementation due to
|
||||||
thread safety issues and initialization order, but it should not be precluded
|
thread safety issues and initialization order, but it should not be precluded
|
||||||
by the specification.</EM></P>
|
by the specification. That's why the use_count() has been left unspecified.</EM></P>
|
||||||
<P><EM>A future release may enable <STRONG>shared_ptr</STRONG> construction from a
|
<P><EM>A future release may enable <STRONG>shared_ptr</STRONG> construction from a
|
||||||
literal zero, for consistency with built-in pointers. It is not clear yet
|
literal zero, for consistency with built-in pointers. It is not clear yet
|
||||||
whether this constructor should be left implicit, enabling <STRONG>0</STRONG> to
|
whether this constructor should be left implicit, enabling <STRONG>0</STRONG> to
|
||||||
@@ -195,8 +191,9 @@ void bad()
|
|||||||
well-formed, must not invoke undefined behavior, and must not throw exceptions.
|
well-formed, must not invoke undefined behavior, and must not throw exceptions.
|
||||||
</p>
|
</p>
|
||||||
<p><b>Effects:</b> Constructs a <b>shared_ptr</b>, storing a copy of <b>p</b>.</p>
|
<p><b>Effects:</b> Constructs a <b>shared_ptr</b>, storing a copy of <b>p</b>.</p>
|
||||||
<p><b>Postconditions:</b> <A href="#use_count">use count</A> is 1.</p>
|
<p><b>Postconditions:</b> <code>use_count() == 1 && get() == p</code>.</p>
|
||||||
<p><b>Throws:</b> <b>std::bad_alloc</b>.</p>
|
<p><b>Throws:</b> <b>std::bad_alloc</b> or an implementation-defined exception when
|
||||||
|
a resource other than memory could not be obtained.</p>
|
||||||
<p><b>Exception safety:</b> If an exception is thrown, <code>delete p</code> is
|
<p><b>Exception safety:</b> If an exception is thrown, <code>delete p</code> is
|
||||||
called.</p>
|
called.</p>
|
||||||
<P><STRONG>Notes:</STRONG> <B>p</B> must be a pointer to an object that was
|
<P><STRONG>Notes:</STRONG> <B>p</B> must be a pointer to an object that was
|
||||||
@@ -208,28 +205,11 @@ void bad()
|
|||||||
pointer type passed. The destructor will call <STRONG>delete</STRONG> with the
|
pointer type passed. The destructor will call <STRONG>delete</STRONG> with the
|
||||||
same pointer, complete with its original type, even when <STRONG>T</STRONG> does
|
same pointer, complete with its original type, even when <STRONG>T</STRONG> does
|
||||||
not have a virtual destructor, or is <STRONG>void</STRONG>.</EM></P>
|
not have a virtual destructor, or is <STRONG>void</STRONG>.</EM></P>
|
||||||
<P><EM>In the current implementation, if <STRONG>p</STRONG> is convertible to <STRONG>counted_base
|
<P><EM>The optional intrusive counting support has been dropped as it exposes too much
|
||||||
*</STRONG>, <STRONG>shared_ptr</STRONG> will use the embedded reference
|
implementation details and doesn't interact well with <STRONG>weak_ptr</STRONG>.
|
||||||
count supplied by <STRONG>counted_base</STRONG>. This is an (experimental)
|
The current implementation uses a different mechanism, <A href="enable_shared_from_this.html">
|
||||||
attempt to provide a way for <STRONG>shared_ptr</STRONG> to be constructed from
|
enable_shared_from_this</A>, to solve the "<STRONG>shared_ptr</STRONG> from <STRONG>
|
||||||
a raw pointer such as <STRONG>this</STRONG>. A free function <STRONG>shared_from_this(q)</STRONG>
|
this</STRONG>" problem.</EM><EM>]</EM></P>
|
||||||
performs the conversion when <STRONG>q</STRONG> is convertible to <STRONG>counted_base
|
|
||||||
const *</STRONG>.</EM></P>
|
|
||||||
<P><EM>The optional intrusive counting employed by the current implementation allows <STRONG>
|
|
||||||
shared_ptr</STRONG> to interoperate with <STRONG>intrusive_ptr</STRONG>, an
|
|
||||||
experimental generic intrusive-counted smart pointer.</EM></P>
|
|
||||||
<P><EM> Another possible implementation is to use a global pointer-to-count map instead
|
|
||||||
of intrusive counting. <STRONG>shared_from_this</STRONG> would no longer be
|
|
||||||
O(1), which is a concern for some users, although I do not expect any
|
|
||||||
performance problems, since the operation is rare. Maintaining a global map is
|
|
||||||
difficult; it needs to be initialized before any <STRONG>shared_ptr</STRONG> instances
|
|
||||||
are constructed, and the initialization needs to be thread safe. In addition,
|
|
||||||
under the Windows dynamic library model, it is possible for several maps to
|
|
||||||
exist.</EM></P>
|
|
||||||
<P><EM> It is not yet clear which implementation should be used, or whether the
|
|
||||||
specification should allow both; nevertheless, the ability to make a <STRONG>shared_ptr</STRONG>
|
|
||||||
from <STRONG>this</STRONG> is considered essential by experienced smart pointer
|
|
||||||
users.</EM><EM>]</EM></P>
|
|
||||||
<pre>template<class Y, class D> shared_ptr(Y * p, D d);</pre>
|
<pre>template<class Y, class D> shared_ptr(Y * p, D d);</pre>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p><b>Requirements:</b> <B>p</B> must be convertible to <B>T *</B>. <STRONG>D</STRONG>
|
<p><b>Requirements:</b> <B>p</B> must be convertible to <B>T *</B>. <STRONG>D</STRONG>
|
||||||
@@ -238,8 +218,9 @@ void bad()
|
|||||||
well-formed, must not invoke undefined behavior, and must not throw exceptions.
|
well-formed, must not invoke undefined behavior, and must not throw exceptions.
|
||||||
</p>
|
</p>
|
||||||
<p><b>Effects:</b> Constructs a <b>shared_ptr</b>, storing a copy of <b>p</b> and <b>d</b>.</p>
|
<p><b>Effects:</b> Constructs a <b>shared_ptr</b>, storing a copy of <b>p</b> and <b>d</b>.</p>
|
||||||
<p><b>Postconditions:</b> <A href="#use_count">use count</A> is 1.</p>
|
<p><b>Postconditions:</b> <code>use_count() == 1 && get() == p</code>.</p>
|
||||||
<p><b>Throws:</b> <b>std::bad_alloc</b>.</p>
|
<p><b>Throws:</b> <b>std::bad_alloc</b> or an implementation-defined exception when
|
||||||
|
a resource other than memory could not be obtained.</p>
|
||||||
<p><b>Exception safety:</b> If an exception is thrown, <code>d(p)</code> is called.</p>
|
<p><b>Exception safety:</b> If an exception is thrown, <code>d(p)</code> is called.</p>
|
||||||
<p><b>Notes:</b> When the the time comes to delete the object pointed to by <b>p</b>,
|
<p><b>Notes:</b> When the the time comes to delete the object pointed to by <b>p</b>,
|
||||||
the stored copy of <STRONG>d</STRONG> is invoked with the stored copy of <STRONG>p</STRONG>
|
the stored copy of <STRONG>d</STRONG> is invoked with the stored copy of <STRONG>p</STRONG>
|
||||||
@@ -250,38 +231,39 @@ void bad()
|
|||||||
is not part of the type, changing the allocation strategy does not break source
|
is not part of the type, changing the allocation strategy does not break source
|
||||||
or binary compatibility, and does not require a client recompilation. For
|
or binary compatibility, and does not require a client recompilation. For
|
||||||
example, a "no-op" deallocator is useful when returning a <STRONG>shared_ptr</STRONG>
|
example, a "no-op" deallocator is useful when returning a <STRONG>shared_ptr</STRONG>
|
||||||
to a statically allocated object.</EM></P>
|
to a statically allocated object, and other variations allow a <STRONG>shared_ptr</STRONG>
|
||||||
|
to be used as a wrapper for another smart pointer, easing interoperability.</EM></P>
|
||||||
<P><EM>The support for custom deallocators does not impose significant overhead. Other <STRONG>
|
<P><EM>The support for custom deallocators does not impose significant overhead. Other <STRONG>
|
||||||
shared_ptr</STRONG> features still require a deallocator to be kept.</EM></P>
|
shared_ptr</STRONG> features still require a deallocator to be kept.</EM></P>
|
||||||
<P><EM>The requirement that the copy constructor of <b>D</b> does not throw comes from
|
<P><EM>The requirement that the copy constructor of <b>D</b> does not throw comes from
|
||||||
the pass by value. If the copy constructor throws, the pointer is leaked.
|
the pass by value. If the copy constructor throws, the pointer is leaked.
|
||||||
Removing the requirement requires a pass by (const) reference. The problems are
|
Removing the requirement requires a pass by (const) reference.</EM></P>
|
||||||
that (1) pass by value conveniently changes functions (function references) to
|
<P><EM>Pass by reference is problematic since (1) pass by value conveniently changes
|
||||||
function pointers (this has to be performed manually otherwise and some
|
functions (function references) to function pointers (this has to be performed
|
||||||
compilers may not be able to do it) and (2) const references don't currently
|
manually otherwise and some compilers may not be able to do it) and (2) const
|
||||||
(per the standard) bind to functions. This can be solved (I think) but it
|
references don't currently (per the standard) bind to functions. This can be
|
||||||
requires an overload set that breaks on many compilers due to 14.5.5.2 problems
|
solved (I think) but it requires an overload set that breaks on many compilers
|
||||||
(and of course it will break on compilers that don't do partial ordering at
|
due to 14.5.5.2 problems (and of course it will break on compilers that don't
|
||||||
all.)</EM></P>
|
do partial ordering at all.)</EM></P>
|
||||||
<P><EM>The requrement will be removed when the aforementioned issues are resolved.]</EM></P>
|
<P><EM>The main problem with pass by reference, though, lies in its interaction with
|
||||||
|
rvalues. A const reference may still cause a copy, and will require a const
|
||||||
|
operator(). A non-const reference won't bind to an rvalue at all. A good
|
||||||
|
solution to this problem is the rvalue reference proposed in <A href="http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1377.htm">
|
||||||
|
N1377</A>/<A href="http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm">N1385</A>.]</EM></P>
|
||||||
<pre>shared_ptr(shared_ptr const & r); // never throws
|
<pre>shared_ptr(shared_ptr const & r); // never throws
|
||||||
template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws</pre>
|
template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws</pre>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p><b>Effects:</b> Constructs a <b>shared_ptr</b>, as if by storing a copy of the
|
<p><b>Effects:</b> If <b>r</b> is <EM>empty</EM>, constructs an <EM>empty</EM> <b>shared_ptr</b>;
|
||||||
pointer stored in <STRONG>r</STRONG>.</p>
|
otherwise, constructs a <b>shared_ptr</b> that <EM>shares ownership</EM> with <b>r</b>.</p>
|
||||||
<p><b>Postconditions:</b> <A href="#use_count">use count</A> for all copies is
|
<p><b>Postconditions:</b> <code>get() == r.get()</code>.</p>
|
||||||
increased by one.</p>
|
|
||||||
<p><b>Throws:</b> nothing.</p>
|
<p><b>Throws:</b> nothing.</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<P><EM>[The postcondition will be relaxed when a default-constructed <STRONG>shared_ptr</STRONG>
|
<pre>template<class Y> explicit shared_ptr(<A href="weak_ptr.htm" >weak_ptr</A><Y> const & r);</pre>
|
||||||
is being copied.]</EM></P>
|
|
||||||
<pre>template<class Y> explicit shared_ptr(<A href="weak_ptr.htm">weak_ptr</A><Y> const & r);</pre>
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p><b>Effects:</b> Constructs a <b>shared_ptr</b>, as if by storing a copy of the
|
<p><b>Effects:</b> If <b>r</b> is <EM>empty</EM>, constructs an <EM>empty</EM> <b>shared_ptr</b>;
|
||||||
pointer stored in <STRONG>r</STRONG>.</p>
|
otherwise, constructs a <b>shared_ptr</b> that <EM>shares ownership</EM> with <b>r</b>
|
||||||
<p><b>Postconditions:</b> <A href="#use_count">use count</A> for all copies is
|
and stores a copy of the pointer stored in <STRONG>r</STRONG>.</p>
|
||||||
increased by one.</p>
|
<p><b>Throws:</b> <b>bad_weak_ptr</b> when <code>r.use_count() == 0</code>.</p>
|
||||||
<p><b>Throws:</b> <b>use_count_is_zero</b> when <code>r.use_count() == 0</code>.</p>
|
|
||||||
<p><b>Exception safety:</b> If an exception is thrown, the constructor has no
|
<p><b>Exception safety:</b> If an exception is thrown, the constructor has no
|
||||||
effect.</p>
|
effect.</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
@@ -299,21 +281,22 @@ template<class Y> shared_ptr(shared_ptr<Y> const & r); // never
|
|||||||
<pre>template<class Y> shared_ptr(std::auto_ptr<Y> & r);</pre>
|
<pre>template<class Y> shared_ptr(std::auto_ptr<Y> & r);</pre>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><B>Effects:</B> Constructs a <B>shared_ptr</B>, as if by storing a copy of <STRONG>r.release()</STRONG>.</P>
|
<P><B>Effects:</B> Constructs a <B>shared_ptr</B>, as if by storing a copy of <STRONG>r.release()</STRONG>.</P>
|
||||||
<P><B>Postconditions:</B> <A href="#use_count">use count</A> is 1.</P>
|
<p><b>Postconditions:</b> <code>use_count() == 1</code>.</p>
|
||||||
<P><B>Throws:</B> <B>std::bad_alloc</B>.</P>
|
<p><b>Throws:</b> <b>std::bad_alloc</b> or an implementation-defined exception when
|
||||||
|
a resource other than memory could not be obtained.</p>
|
||||||
<P><B>Exception safety:</B> If an exception is thrown, the constructor has no
|
<P><B>Exception safety:</B> If an exception is thrown, the constructor has no
|
||||||
effect.</P>
|
effect.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
<P><EM>[This constructor takes a the source <STRONG>auto_ptr</STRONG> by reference and
|
<P><EM>[This constructor takes a the source <STRONG>auto_ptr</STRONG> by reference and
|
||||||
not by value, and cannot accept <STRONG>auto_ptr</STRONG> temporaries. This is
|
not by value, and cannot accept <STRONG>auto_ptr</STRONG> temporaries. This is
|
||||||
by design, as the constructor offers the strong guarantee.]</EM></P>
|
by design, as the constructor offers the strong guarantee; an rvalue reference
|
||||||
|
would solve this problem, too.]</EM></P>
|
||||||
<h3><a name="destructor">destructor</a></h3>
|
<h3><a name="destructor">destructor</a></h3>
|
||||||
<pre>~shared_ptr(); // never throws</pre>
|
<pre>~shared_ptr(); // never throws</pre>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><B>Effects:</B> If <STRONG>*this</STRONG> is the sole owner (<code>use_count() == 1</code>),
|
<P><B>Effects:</B> If <STRONG>*this</STRONG> is the sole owner (<code>use_count() == 1</code>),
|
||||||
destroys the object pointed to by the stored pointer.</P>
|
destroys the object pointed to by the stored pointer as specified at
|
||||||
<P><B>Postconditions:</B> <A href="#use_count">use count</A> for all remaining
|
construction time.</P>
|
||||||
copies is decreased by one.</P>
|
|
||||||
<P><B>Throws:</B> nothing.</P>
|
<P><B>Throws:</B> nothing.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
<H3><a name="assignment">assignment</a></H3>
|
<H3><a name="assignment">assignment</a></H3>
|
||||||
@@ -327,8 +310,7 @@ template<class Y> shared_ptr & operator=(std::auto_ptr<Y> &
|
|||||||
and destruction are not considered observable side effects, and the
|
and destruction are not considered observable side effects, and the
|
||||||
implementation is free to meet the effects (and the implied guarantees) via
|
implementation is free to meet the effects (and the implied guarantees) via
|
||||||
different means, without creating a temporary. In particular, in the example:</P>
|
different means, without creating a temporary. In particular, in the example:</P>
|
||||||
<pre>
|
<pre>shared_ptr<int> p(new int);
|
||||||
shared_ptr<int> p(new int);
|
|
||||||
shared_ptr<void> q(p);
|
shared_ptr<void> q(p);
|
||||||
p = p;
|
p = p;
|
||||||
q = p;
|
q = p;
|
||||||
@@ -342,12 +324,10 @@ q = p;
|
|||||||
actually applies here, so it's better to be explicit about the possible
|
actually applies here, so it's better to be explicit about the possible
|
||||||
optimizations.]</EM></P>
|
optimizations.]</EM></P>
|
||||||
<h3><a name="reset">reset</a></h3>
|
<h3><a name="reset">reset</a></h3>
|
||||||
<pre>void reset();</pre>
|
<pre>void reset(); // never throws</pre>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><B>Effects:</B> Equivalent to <code>shared_ptr().swap(*this)</code>.</P>
|
<P><B>Effects:</B> Equivalent to <code>shared_ptr().swap(*this)</code>.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
<P><EM>[<STRONG>reset()</STRONG> will offer the nothrow guarantee in a future
|
|
||||||
implementation.]</EM></P>
|
|
||||||
<pre>template<class Y> void reset(Y * p);</pre>
|
<pre>template<class Y> void reset(Y * p);</pre>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><B>Effects:</B> Equivalent to <code>shared_ptr(p).swap(*this)</code>.</P>
|
<P><B>Effects:</B> Equivalent to <code>shared_ptr(p).swap(*this)</code>.</P>
|
||||||
@@ -384,13 +364,12 @@ q = p;
|
|||||||
If you are using <code>unique()</code> to implement copy on write, do not rely
|
If you are using <code>unique()</code> to implement copy on write, do not rely
|
||||||
on a specific value when the stored pointer is zero.</P>
|
on a specific value when the stored pointer is zero.</P>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<P><EM>[In a future release, <STRONG>unique()</STRONG> will return an unspecified value
|
|
||||||
for a default-constructed <STRONG>shared_ptr.</STRONG>]</EM></P>
|
|
||||||
<h3><a name="use_count">use_count</a></h3>
|
<h3><a name="use_count">use_count</a></h3>
|
||||||
<pre>long use_count() const; // never throws</pre>
|
<pre>long use_count() const; // never throws</pre>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p><b>Returns:</b> the number of <b>shared_ptr</b> objects sharing ownership of the
|
<p><b>Returns:</b> the number of <b>shared_ptr</b> objects that <i>share ownership</i>
|
||||||
stored pointer.</p>
|
with <b>*this</b>, or an unspecified nonnegative value when <STRONG>*this</STRONG>
|
||||||
|
is <EM>empty</EM>.</p>
|
||||||
<p><b>Throws:</b> nothing.</p>
|
<p><b>Throws:</b> nothing.</p>
|
||||||
<P><B>Notes:</B> <code>use_count()</code> is not necessarily efficient. Use only
|
<P><B>Notes:</B> <code>use_count()</code> is not necessarily efficient. Use only
|
||||||
for debugging and testing purposes, not for production code.</P>
|
for debugging and testing purposes, not for production code.</P>
|
||||||
@@ -407,8 +386,8 @@ q = p;
|
|||||||
many of the implicit conversion pitfalls.</P>
|
many of the implicit conversion pitfalls.</P>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<P><EM>[The conversion to bool is not merely syntactic sugar. It allows <STRONG>shared_ptr</STRONG>s
|
<P><EM>[The conversion to bool is not merely syntactic sugar. It allows <STRONG>shared_ptr</STRONG>s
|
||||||
to be declared in conditions when using <STRONG>shared_dynamic_cast </STRONG>or <STRONG>
|
to be declared in conditions when using <STRONG>dynamic_pointer_cast </STRONG>or
|
||||||
make_shared</STRONG>.]</EM></P>
|
<STRONG>make_shared</STRONG>.]</EM></P>
|
||||||
<h3><a name="swap">swap</a></h3>
|
<h3><a name="swap">swap</a></h3>
|
||||||
<pre>void swap(shared_ptr & b); // never throws</pre>
|
<pre>void swap(shared_ptr & b); // never throws</pre>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
@@ -429,12 +408,18 @@ q = p;
|
|||||||
<p><b>Returns:</b> <code>a.get() != b.get()</code>.</p>
|
<p><b>Returns:</b> <code>a.get() != b.get()</code>.</p>
|
||||||
<p><b>Throws:</b> nothing.</p>
|
<p><b>Throws:</b> nothing.</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<pre>template<class T>
|
<pre>template<class T, class U>
|
||||||
bool operator<(shared_ptr<T> const & a, shared_ptr<T> const & b); // never throws</pre>
|
bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws</pre>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p><b>Returns:</b> an unspecified value such that <b>operator<</b> is a strict
|
<p><b>Returns:</b> an unspecified value such that</p>
|
||||||
weak ordering as described in section 25.3 <code>[lib.alg.sorting]</code> of
|
<UL>
|
||||||
the C++ standard.</p>
|
<LI>
|
||||||
|
<b>operator<</b> is a strict weak ordering as described in section 25.3 <code>[lib.alg.sorting]</code>
|
||||||
|
of the C++ standard;
|
||||||
|
<LI>
|
||||||
|
under the equivalence relation defined by <STRONG>operator<</STRONG>, <code>!(a
|
||||||
|
< b) && !(b < a)</code>, two <STRONG>shared_ptr</STRONG> instances
|
||||||
|
are equivalent if and only if they <EM>share ownership</EM>.</LI></UL>
|
||||||
<p><b>Throws:</b> nothing.</p>
|
<p><b>Throws:</b> nothing.</p>
|
||||||
<P><B>Notes:</B> Allows <STRONG>shared_ptr</STRONG> objects to be used as keys in
|
<P><B>Notes:</B> Allows <STRONG>shared_ptr</STRONG> objects to be used as keys in
|
||||||
associative containers.</P>
|
associative containers.</P>
|
||||||
@@ -468,23 +453,24 @@ q = p;
|
|||||||
<P><B>Notes:</B> Provided as an aid to generic programming. Used by <A href="../bind/mem_fn.html">
|
<P><B>Notes:</B> Provided as an aid to generic programming. Used by <A href="../bind/mem_fn.html">
|
||||||
mem_fn</A>.</P>
|
mem_fn</A>.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
<h3><a name="shared_static_cast">shared_static_cast</a></h3>
|
<h3><a name="static_pointer_cast">static_pointer_cast</a></h3>
|
||||||
<pre>template<class T, class U>
|
<pre>template<class T, class U>
|
||||||
shared_ptr<T> shared_static_cast(shared_ptr<U> const & r); // never throws</pre>
|
shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r); // never throws</pre>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><STRONG>Requires:</STRONG> The expression <code>static_cast<T*>(r.get())</code>
|
<P><STRONG>Requires:</STRONG> The expression <code>static_cast<T*>(r.get())</code>
|
||||||
must be well-formed.</P>
|
must be well-formed.</P>
|
||||||
<P><B>Returns:</B> A <STRONG>shared_ptr<T></STRONG> object that stores a copy
|
<P><B>Returns:</B> If <b>r</b> is <i>empty</i>, an <i>empty</i> <b>shared_ptr<T></b>;
|
||||||
of <code>static_cast<T*>(r.get())</code> and shares ownership with <b>r</b>.</P>
|
otherwise, a <STRONG>shared_ptr<T></STRONG> object that stores a copy of <code>
|
||||||
|
static_cast<T*>(r.get())</code> and <i>shares ownership</i> with <b>r</b>.</P>
|
||||||
<P><B>Throws:</B> nothing.</P>
|
<P><B>Throws:</B> nothing.</P>
|
||||||
<P><B>Notes:</B> the seemingly equivalent expression</P>
|
<P><B>Notes:</B> the seemingly equivalent expression</P>
|
||||||
<p><code>shared_ptr<T>(static_cast<T*>(r.get()))</code></p>
|
<p><code>shared_ptr<T>(static_cast<T*>(r.get()))</code></p>
|
||||||
<p>will eventually result in undefined behavior, attempting to delete the same
|
<p>will eventually result in undefined behavior, attempting to delete the same
|
||||||
object twice.</p>
|
object twice.</p>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
<h3><a name="shared_dynamic_cast">shared_dynamic_cast</a></h3>
|
<h3><a name="dynamic_pointer_cast">dynamic_pointer_cast</a></h3>
|
||||||
<pre>template<class T, class U>
|
<pre>template<class T, class U>
|
||||||
shared_ptr<T> shared_dynamic_cast(shared_ptr<U> const & r);</pre>
|
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r);</pre>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><STRONG>Requires:</STRONG> The expression <CODE>dynamic_cast<T*>(r.get())</CODE>
|
<P><STRONG>Requires:</STRONG> The expression <CODE>dynamic_cast<T*>(r.get())</CODE>
|
||||||
must be well-formed and its behavior defined.</P>
|
must be well-formed and its behavior defined.</P>
|
||||||
@@ -492,42 +478,22 @@ q = p;
|
|||||||
<UL>
|
<UL>
|
||||||
<LI>
|
<LI>
|
||||||
When <CODE>dynamic_cast<T*>(r.get())</CODE> returns a nonzero value, a <STRONG>
|
When <CODE>dynamic_cast<T*>(r.get())</CODE> returns a nonzero value, a <STRONG>
|
||||||
shared_ptr<T></STRONG> object that stores a copy of it and shares
|
shared_ptr<T></STRONG> object that stores a copy of it and <i>shares
|
||||||
ownership with <STRONG>r</STRONG>;
|
ownership</i> with <STRONG>r</STRONG>;
|
||||||
<LI>
|
<LI>
|
||||||
Otherwise, a default-constructed <STRONG>shared_ptr<T></STRONG> object.</LI></UL>
|
Otherwise, an <i>empty</i> <STRONG>shared_ptr<T></STRONG> object.</LI></UL>
|
||||||
<P><B>Throws:</B> <STRONG>std::bad_alloc</STRONG>.</P>
|
<P><B>Throws:</B> nothing.</P>
|
||||||
<P><B>Exception safety:</B> If an exception is thrown, the function has no effect.</P>
|
|
||||||
<P><B>Notes:</B> the seemingly equivalent expression</P>
|
<P><B>Notes:</B> the seemingly equivalent expression</P>
|
||||||
<P><CODE>shared_ptr<T>(dynamic_cast<T*>(r.get()))</CODE></P>
|
<P><CODE>shared_ptr<T>(dynamic_cast<T*>(r.get()))</CODE></P>
|
||||||
<P>will eventually result in undefined behavior, attempting to delete the same
|
<P>will eventually result in undefined behavior, attempting to delete the same
|
||||||
object twice.</P>
|
object twice.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
<h3><a name="shared_polymorphic_cast">shared_polymorphic_cast</a></h3>
|
<h3><a name="insertion-operator">operator<<</a></h3>
|
||||||
<pre>template<class T, class U>
|
<pre>template<class E, class T, class Y>
|
||||||
shared_ptr<T> shared_polymorphic_cast(shared_ptr<U> const & r);</pre>
|
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);</pre>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<p><STRONG>Requires:</STRONG> The expression <CODE><A href="../conversion/cast.htm#Polymorphic_cast">
|
<p><STRONG>Effects:</STRONG> <code>os << p.get();</code>.</p>
|
||||||
polymorphic_cast</A><T*>(r.get())</CODE> must be well-formed and
|
<P><B>Returns:</B> <b>os</b>.</P>
|
||||||
its behavior defined.</p>
|
|
||||||
<P><B>Returns:</B> A <STRONG>shared_ptr<T></STRONG> object that stores a copy
|
|
||||||
of <CODE><A href="../conversion/cast.htm#Polymorphic_cast">polymorphic_cast</A><T*>(r.get())</CODE>
|
|
||||||
and shares ownership with <B>r</B>.</P>
|
|
||||||
<P><B>Throws:</B> <STRONG>std::bad_cast</STRONG> when the pointer cannot be
|
|
||||||
converted.</P>
|
|
||||||
<P><B>Exception safety:</B> If an exception is thrown, the function has no effect.</P>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
<h3><a name="shared_polymorphic_downcast">shared_polymorphic_downcast</a></h3>
|
|
||||||
<pre>template<class T, class U>
|
|
||||||
shared_ptr<T> shared_polymorphic_downcast(shared_ptr<U> const & r); // never throws</pre>
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<p><STRONG>Requires:</STRONG> The expression <CODE><A href="../conversion/cast.htm#Polymorphic_cast">
|
|
||||||
polymorphic_downcast</A><T*>(r.get())</CODE> must be well-formed
|
|
||||||
and its behavior defined.</p>
|
|
||||||
<P><B>Returns:</B> A <STRONG>shared_ptr<T></STRONG> object that stores a copy
|
|
||||||
of <CODE><A href="../conversion/cast.htm#Polymorphic_cast">polymorphic_downcast</A><T*>(r.get())</CODE>
|
|
||||||
and shares ownership with <B>r</B>.</P>
|
|
||||||
<P><B>Throws:</B> nothing.</P>
|
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
<h2><a name="example">Example</a></h2>
|
<h2><a name="example">Example</a></h2>
|
||||||
<p>See <A href="shared_ptr_example.cpp">shared_ptr_example.cpp</A> for a complete
|
<p>See <A href="shared_ptr_example.cpp">shared_ptr_example.cpp</A> for a complete
|
||||||
@@ -563,8 +529,7 @@ q = p;
|
|||||||
underneath.)</p>
|
underneath.)</p>
|
||||||
<P>Any other simultaneous accesses result in undefined behavior.</P>
|
<P>Any other simultaneous accesses result in undefined behavior.</P>
|
||||||
<P>Examples:</P>
|
<P>Examples:</P>
|
||||||
<pre>
|
<pre>shared_ptr<int> p(new int(42));
|
||||||
shared_ptr<int> p(new int(42));
|
|
||||||
|
|
||||||
//--- Example 1 ---
|
//--- Example 1 ---
|
||||||
|
|
||||||
@@ -577,7 +542,6 @@ shared_ptr<int> p3(p); // OK, multiple reads are safe
|
|||||||
//--- Example 2 ---
|
//--- Example 2 ---
|
||||||
|
|
||||||
// thread A
|
// thread A
|
||||||
|
|
||||||
p.reset(new int(1912)); // writes p
|
p.reset(new int(1912)); // writes p
|
||||||
|
|
||||||
// thread B
|
// thread B
|
||||||
@@ -668,11 +632,12 @@ int * p = a.release();
|
|||||||
implementation or a linked list implementation, or some other specific
|
implementation or a linked list implementation, or some other specific
|
||||||
implementation. This is not the intent.</p>
|
implementation. This is not the intent.</p>
|
||||||
<hr>
|
<hr>
|
||||||
<p>Revised $Date$</p>
|
<p>
|
||||||
|
$Date$</p>
|
||||||
<p>Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler.
|
<p>Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler.
|
||||||
Copyright 2002 Peter Dimov. Permission to copy, use, modify, sell and
|
Copyright 2002, 2003 Peter Dimov. Permission to copy, use, modify, sell
|
||||||
distribute this document is granted provided this copyright notice appears in
|
and distribute this document is granted provided this copyright notice appears
|
||||||
all copies. This document is provided "as is" without express or implied
|
in all copies. This document is provided "as is" without express or implied
|
||||||
warranty, and with no claim as to its suitability for any purpose.</p>
|
warranty, and with no claim as to its suitability for any purpose.</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Reference in New Issue
Block a user