Wei Sun wrote:
I have a few questions about writing VCL Components for Win32
applications - specifically - how do we deal with ownership of
pointers in component classes, shown as below?
If the component creates an inner object, it should own it and free it
in the destructor. Any property you define to access the object
pointer should be read-only, or have a setter that calls the object's
Assign() method if it is derived from TPersistent.
If the component accepts a pointer to an external object, it should not
take ownership. If the object is derived from TComponent, call its
FreeNotification() method so you can be notified (by having your
component override its virtual Notification() method) if the object is
freed while your component is still pointing at it. NULL out your
pointer if notified. If changing the object pointer to point at a new
object, call RemoveFreeNotification() on the previous object. And make
sure you check for NULL any time you want to access the object.
1. Is making TMyGraphicControl own the member font_ correct practice?
Yes, since the component is creating the TFont object, so it is
responsible for freeing it. You should also be assigning an OnChange
event handler to the TFont so you can call Invalidate() when any of the
font's property values change. Don't call it in your Font property
setter.
2. If the component must own the pointers, is it safe to return raw
pointer like TMyGraphicControl::GetFont() and TMyGraphicControl::Font?
Yes, as long as noone else tries to take ownership of the pointer. Any
class/component that follows the rules above will be safe.
3. In TMyGraphicControl::Paint(), is it safe to pass raw pointer to
TCanvas::Font?
Yes, because the TCanvas::Font property has a setter that calls
TFont::Assign() on the Canvas's internal TFont object. It does not
take ownership of the pointer you pass to it. It copies the values of
the font's sub-properties instead.
void __fastcall SetFont(Vcl::Graphics::TFont* Value) {
font_.reset(Value); //is it safe to take ownership of the passed
pointer???
Invalidate();
}
NO! Call Assign() to copy the sub-property values instead:
Canvas->Font = GetFont(); //is it safe to pass raw pointer here???
Yes.
With that said, your component should look more like this:
class TMyGraphicControl : public TGraphicControl
{
private:
std::unique_ptr<Vcl::Graphics::TFont> font_;
void __fastcall FontChanged(TObject *Sender)
{
Invalidate();
}
Vcl::Graphics::TFont* __fastcall GetFont()
{
return font_.get();
}
void __fastcall SetFont(Vcl::Graphics::TFont* Value)
{
font_->Assign(Value);
}
protected:
virtual void __fastcall Paint(void)
{
Canvas->Font = Font;
// effectively Canvas->Font->Assign(Font) ...
//the rest of implementation
}
public:
__fastcall TMyGraphicControl(TComponent* Owner)
: TGraphicControl(Owner), font_(new Vcl::Graphics::TFont())
{
font_->OnChange = &FontChanged;
}
__published:
__property Vcl::Graphics::TFont* Font = {read = GetFont, write =
SetFont};
}
However, TGraphicControl already has a Font property inherited from
TControl, so you shouldn't be re-defining the Font property in your
component at all (but, you should follow the above logic if you need to
define additional object-based properties). Promote the existing Font
property instead, eg:
class TMyGraphicControl : public TGraphicControl
{
protected:
virtual void __fastcall Paint(void)
{
Canvas->Font = Font;
// effectively Canvas->Font->Assign(Font) ...
//the rest of implementation
}
public:
__fastcall TMyGraphicControl(TComponent* Owner)
: TGraphicControl(Owner)
{
}
__published:
__property Font; // <-- that is all you need!
};
--
Remy Lebeau (TeamB)
Connect with Us