more docs and more comments in the sample code for wxObject* classes; document CreateRefData() and CloneRefData()

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@59544 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Francesco Montorsi
2009-03-15 01:03:48 +00:00
parent 387e72bad6
commit 1729acd85b

View File

@@ -14,12 +14,13 @@
Derive classes from this to store your own data. When retrieving information Derive classes from this to store your own data. When retrieving information
from a wxObject's reference data, you will need to cast to your own derived class. from a wxObject's reference data, you will need to cast to your own derived class.
@b Example: @section objectrefdata_example Example
@code @code
// include file // include file
// ------------
class MyCar: public wxObject class MyCar : public wxObject
{ {
public: public:
MyCar() { } MyCar() { }
@@ -42,8 +43,11 @@
// implementation // implementation
// --------------
class MyCarRefData: public wxObjectRefData // the reference data class is typically a private class only visible in the
// implementation source file of the refcounted class.
class MyCarRefData : public wxObjectRefData
{ {
public: public:
MyCarRefData() MyCarRefData()
@@ -54,6 +58,9 @@
MyCarRefData( const MyCarRefData& data ) MyCarRefData( const MyCarRefData& data )
: wxObjectRefData() : wxObjectRefData()
{ {
// copy refcounted data; this is usually a time- and memory-consuming operation
// and is only done when two (or more) MyCar instances need to unshare a
// common instance of MyCarRefData
m_price = data.m_price; m_price = data.m_price;
} }
@@ -62,16 +69,21 @@
return m_price == data.m_price; return m_price == data.m_price;
} }
private:
// in real world, reference counting is usually used only when
// the wxObjectRefData-derived class holds data very memory-consuming;
// in this example the various MyCar instances may share a MyCarRefData
// instance which however only takes 4 bytes for this integer!
int m_price; int m_price;
}; };
#define M_CARDATA ((MyCarRefData *)m_refData) #define M_CARDATA ((MyCarRefData *)m_refData)
IMPLEMENT_DYNAMIC_CLASS(MyCar,wxObject) IMPLEMENT_DYNAMIC_CLASS(MyCar,wxObject)
MyCar::MyCar( int price ) MyCar::MyCar( int price )
{ {
// here we init the MyCar internal data:
m_refData = new MyCarRefData(); m_refData = new MyCarRefData();
M_CARDATA->m_price = price; M_CARDATA->m_price = price;
} }
@@ -88,15 +100,24 @@
bool MyCar::operator == ( const MyCar& car ) const bool MyCar::operator == ( const MyCar& car ) const
{ {
if (m_refData == car.m_refData) return true; if (m_refData == car.m_refData)
return true;
if (!m_refData || !car.m_refData) return false; if (!m_refData || !car.m_refData)
return false;
// here we use the MyCarRefData::operator==() function.
// Note however that this comparison may be very slow if the
// reference data contains a lot of data to be compared.
return ( *(MyCarRefData*)m_refData == *(MyCarRefData*)car.m_refData ); return ( *(MyCarRefData*)m_refData == *(MyCarRefData*)car.m_refData );
} }
void MyCar::SetPrice( int price ) void MyCar::SetPrice( int price )
{ {
// since this function modifies one of the MyCar internal property,
// we need to be sure that the other MyCar instances which share the
// same MyCarRefData instance are not affected by this call.
// I.e. it's very important to call UnShare() in all setters of
// refcounted classes!
UnShare(); UnShare();
M_CARDATA->m_price = price; M_CARDATA->m_price = price;
@@ -106,7 +127,7 @@
{ {
wxCHECK_MSG( IsOk(), -1, "invalid car" ); wxCHECK_MSG( IsOk(), -1, "invalid car" );
return (M_CARDATA->m_price); return M_CARDATA->m_price;
} }
@endcode @endcode
@@ -177,24 +198,33 @@ public:
wxObject can be used to implement @ref overview_refcount "reference counted" wxObject can be used to implement @ref overview_refcount "reference counted"
objects, such as wxPen, wxBitmap and others objects, such as wxPen, wxBitmap and others
(see @ref overview_refcount_list "this list"). (see @ref overview_refcount_list "this list").
See wxObjectRefData and @ref overview_refcount for more info about
reference counting.
@library{wxbase} @library{wxbase}
@category{rtti} @category{rtti}
@see wxClassInfo, @ref overview_debugging, wxObjectRefData @see wxClassInfo, @ref overview_debugging, @ref overview_refcount,
wxObjectRefData, wxObjectDataPtr<T>
*/ */
class wxObject class wxObject
{ {
public: public:
/**
Default ctor; initializes to @NULL the internal reference data.
*/
wxObject(); wxObject();
/** /**
Copy ctor. Copy ctor.
Sets the internal wxObject::m_refData pointer to point to the same
instance of the wxObjectRefData-derived class pointed by @c other and
increments the refcount of wxObject::m_refData.
*/ */
wxObject(const wxObject& other); wxObject(const wxObject& other);
/** /**
Destructor. Destructor.
@@ -279,11 +309,7 @@ public:
void UnRef(); void UnRef();
/** /**
Ensure that this object's data is not shared with any other object. This is the same of AllocExclusive() but this method is public.
If we have no data, it is created using CreateRefData() below,
if we have shared data, it is copied using CloneRefData(),
otherwise nothing is done.
*/ */
void UnShare(); void UnShare();
@@ -304,6 +330,48 @@ public:
void* operator new(size_t size, const wxString& filename = NULL, int lineNum = 0); void* operator new(size_t size, const wxString& filename = NULL, int lineNum = 0);
protected: protected:
/**
Ensure that this object's data is not shared with any other object.
If we have no data, it is created using CreateRefData();
if we have shared data (i.e. data with a reference count greater than 1),
it is copied using CloneRefData(); otherwise nothing is done (the data
is already present and is not shared by other object instances).
If you use this function you should make sure that you override the
CreateRefData() and CloneRefData() functions in your class otherwise
an assertion will fail at runtime.
*/
void AllocExclusive();
/**
Creates a new instance of the wxObjectRefData-derived class specific to
this object and returns it.
This is usually implemented as a one-line call:
@code
wxObjectRefData *MyObject::CreateRefData() const
{
return new MyObjectRefData;
}
@endcode
*/
virtual wxObjectRefData *CreateRefData() const;
/**
Creates a new instance of the wxObjectRefData-derived class specific to
this object and initializes it copying @a data.
This is usually implemented as a one-line call:
@code
wxObjectRefData *MyObject::CloneRefData(const wxObjectRefData *data) const
{
// rely on the MyObjectRefData copy ctor:
return new MyObjectRefData(*(MyObjectRefData *)data);
}
@endcode
*/
virtual wxObjectRefData *CloneRefData(const wxObjectRefData *data) const;
/** /**
Pointer to an object which is the object's reference-counted data. Pointer to an object which is the object's reference-counted data.
@@ -391,7 +459,7 @@ public:
/** /**
This is helper template class primarily written to avoid memory leaks because of This is an helper template class primarily written to avoid memory leaks because of
missing calls to wxObjectRefData::DecRef(). missing calls to wxObjectRefData::DecRef().
Despite the name this template can actually be used as a smart pointer for any Despite the name this template can actually be used as a smart pointer for any
@@ -399,11 +467,10 @@ public:
methods @b T::IncRef() and @b T::DecRef(). methods @b T::IncRef() and @b T::DecRef().
The difference to wxSharedPtr<T> is that wxObjectDataPtr<T> relies on the reference The difference to wxSharedPtr<T> is that wxObjectDataPtr<T> relies on the reference
counting to be in the class pointed to where instead wxSharedPtr<T> implements the counting to be in the class pointed to, where instead wxSharedPtr<T> implements the
reference counting itself. reference counting itself.
@section objectdataptr_example Example
@b Example:
@code @code
class MyCarRefData: public wxObjectRefData class MyCarRefData: public wxObjectRefData
@@ -418,7 +485,12 @@ public:
} }
void SetPrice( int price ) { m_price = price; } void SetPrice( int price ) { m_price = price; }
int GetPrice() { return m_price; } int GetPrice() const { return m_price; }
bool operator == ( const MyCarRefData& other ) const
{
return m_price == other.m_price;
}
protected: protected:
int m_price; int m_price;
@@ -427,6 +499,8 @@ public:
class MyCar class MyCar
{ {
public: public:
// initializes this MyCar assigning to the
// internal data pointer a new instance of MyCarRefData
MyCar( int price ) : m_data( new MyCarRefData ) MyCar( int price ) : m_data( new MyCarRefData )
{ {
m_data->SetPrice( price ); m_data->SetPrice( price );
@@ -434,19 +508,29 @@ public:
MyCar& operator =( const MyCar& tocopy ) MyCar& operator =( const MyCar& tocopy )
{ {
// shallow copy: this is just a fast copy of pointers; the real
// memory-consuming data which typically is stored inside
// MyCarRefData is not copied here!
m_data = tocopy.m_data; m_data = tocopy.m_data;
return *this; return *this;
} }
bool operator == ( const MyCar& other ) const bool operator == ( const MyCar& other ) const
{ {
if (m_data.get() == other.m_data.get()) return true; if (m_data.get() == other.m_data.get())
return (m_data->GetPrice() == other.m_data->GetPrice()); return true; // this instance and the 'other' one share the
// same MyCarRefData data...
// rely on the MyCarRefData::operator==()
return (*m_data.get()) == (*other.m_data.get());
} }
void SetPrice( int price ) void SetPrice( int price )
{ {
// make sure changes to this class do not affect other instances
// currently sharing our same refcounted data:
UnShare(); UnShare();
m_data->SetPrice( price ); m_data->SetPrice( price );
} }
@@ -475,6 +559,7 @@ public:
@see wxObject, wxObjectRefData, @ref overview_refcount, wxSharedPtr<T>, @see wxObject, wxObjectRefData, @ref overview_refcount, wxSharedPtr<T>,
wxScopedPtr<T>, wxWeakRef<T> wxScopedPtr<T>, wxWeakRef<T>
*/ */
template <class T>
class wxObjectDataPtr<T> class wxObjectDataPtr<T>
{ {
public: public: